From 5f15e99a61d509b210767967353bee688eede73a Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 29 Mar 2026 17:20:58 +0200 Subject: [PATCH 01/30] Add spotless plugin and fix grammar issues --- mvnw | 2 +- mvnw.cmd | 4 +- .../restlet/ext/crypto/AwsAuthenticator.java | 83 +- .../ext/crypto/CookieAuthenticator.java | 418 +- .../ext/crypto/DigestAuthenticator.java | 139 +- .../org/restlet/ext/crypto/DigestUtils.java | 199 +- .../restlet/ext/crypto/DigestVerifier.java | 125 +- .../restlet/ext/crypto/internal/AwsUtils.java | 235 +- .../ext/crypto/internal/AwsVerifier.java | 166 +- .../ext/crypto/internal/CryptoUtils.java | 134 +- .../crypto/internal/HttpAwsQueryHelper.java | 29 +- .../ext/crypto/internal/HttpAwsS3Helper.java | 26 +- .../internal/HttpAzureSharedKeyHelper.java | 98 +- .../HttpAzureSharedKeyLiteHelper.java | 57 +- .../ext/crypto/internal/HttpDigestHelper.java | 204 +- .../crypto/internal/HttpDigestVerifier.java | 78 +- .../crypto/CookieAuthenticatorTestCase.java | 46 +- .../ext/crypto/DigestVerifierTestCase.java | 46 +- .../ext/crypto/HttpDigestTestCase.java | 64 +- .../internal/HttpAwsS3HelperTestCase.java | 41 +- .../internal/HttpAwsS3HostNameTestCase.java | 43 +- .../internal/HttpAwsS3SigningTestCase.java | 105 +- .../internal/HttpAwsS3VerifierTestCase.java | 43 +- .../internal/HttpDigestHelperTestCase.java | 83 +- .../ext/freemarker/ContextTemplateLoader.java | 84 +- .../ext/freemarker/FreemarkerConverter.java | 39 +- .../ext/freemarker/TemplateFilter.java | 123 +- .../freemarker/TemplateRepresentation.java | 241 +- .../internal/ResolverHashModel.java | 30 +- .../ext/freemarker/internal/ScalarModel.java | 16 +- .../ext/freemarker/FreeMarkerTestCase.java | 27 +- .../freemarker/TemplateFilterTestCase.java | 29 +- .../org/restlet/ext/gson/GsonConverter.java | 52 +- .../restlet/ext/gson/GsonRepresentation.java | 107 +- .../org/restlet/ext/gson/GsonTestCase.java | 45 +- .../ext/jaas/ChallengeCallbackHandler.java | 72 +- .../java/org/restlet/ext/jaas/JaasUtils.java | 71 +- .../org/restlet/ext/jaas/JaasVerifier.java | 98 +- .../restlet/ext/jackson/JacksonConverter.java | 102 +- .../ext/jackson/JacksonRepresentation.java | 234 +- .../jackson/internal/XmlFactoryProvider.java | 50 +- .../org/restlet/ext/jackson/Customer.java | 10 +- .../java/org/restlet/ext/jackson/Invoice.java | 10 +- .../restlet/ext/jackson/JacksonTestCase.java | 91 +- .../org/restlet/ext/jackson/MyException.java | 26 +- .../org/restlet/ext/json/JsonConverter.java | 59 +- .../restlet/ext/json/JsonRepresentation.java | 91 +- .../org/restlet/ext/json/JsonpFilter.java | 47 +- .../restlet/ext/json/JsonpRepresentation.java | 41 +- .../restlet/ext/json/JsonpFilterTestCase.java | 32 +- .../ext/json/JsonpRepresentationTestCase.java | 57 +- .../org/restlet/ext/slf4j/Slf4jLogger.java | 93 +- .../restlet/ext/slf4j/Slf4jLoggerFacade.java | 33 +- .../restlet/ext/spring/SpringBeanFinder.java | 101 +- .../restlet/ext/spring/SpringBeanRouter.java | 244 +- .../restlet/ext/spring/SpringComponent.java | 66 +- .../org/restlet/ext/spring/SpringContext.java | 63 +- .../org/restlet/ext/spring/SpringFinder.java | 97 +- .../org/restlet/ext/spring/SpringHost.java | 71 +- .../restlet/ext/spring/SpringResource.java | 64 +- .../org/restlet/ext/spring/SpringRouter.java | 88 +- .../org/restlet/ext/spring/SpringServer.java | 55 +- .../ext/spring/SpringBeanFinderTestCase.java | 74 +- .../ext/spring/SpringBeanRouterTestCase.java | 96 +- .../restlet/ext/spring/SpringTestCase.java | 26 +- .../ext/spring/resources/OrderResource.java | 10 +- .../ext/spring/resources/OrdersResource.java | 10 +- .../ext/spring/resources/UserResource.java | 9 +- .../restlet/ext/thymeleaf/TemplateFilter.java | 75 +- .../ext/thymeleaf/TemplateRepresentation.java | 222 +- .../ext/thymeleaf/ThymeleafConverter.java | 41 +- .../ext/thymeleaf/ThymeleafTestCase.java | 32 +- .../RepresentationResourceLoader.java | 32 +- .../restlet/ext/velocity/TemplateFilter.java | 82 +- .../ext/velocity/TemplateRepresentation.java | 207 +- .../ext/velocity/VelocityConverter.java | 26 +- .../ext/velocity/TemplateFilterTestCase.java | 28 +- .../ext/velocity/VelocityTestCase.java | 41 +- .../restlet/ext/xml/DomRepresentation.java | 109 +- .../java/org/restlet/ext/xml/NodeList.java | 32 +- .../restlet/ext/xml/SaxRepresentation.java | 159 +- .../ext/xml/TransformRepresentation.java | 333 +- .../java/org/restlet/ext/xml/Transformer.java | 123 +- .../org/restlet/ext/xml/XmlConverter.java | 56 +- .../restlet/ext/xml/XmlRepresentation.java | 546 +- .../java/org/restlet/ext/xml/XmlWriter.java | 1233 ++-- .../ext/xml/internal/AbstractXmlReader.java | 79 +- .../ext/xml/internal/ContextResolver.java | 35 +- .../ext/xml/ResolvingTransformerTestCase.java | 140 +- .../xml/TransformRepresentationTestCase.java | 70 +- .../restlet/ext/xml/TransformerTestCase.java | 85 +- .../main/java/org/restlet/Application.java | 1129 ++-- .../src/main/java/org/restlet/Client.java | 79 +- .../src/main/java/org/restlet/Component.java | 1101 ++-- .../src/main/java/org/restlet/Connector.java | 157 +- .../src/main/java/org/restlet/Context.java | 676 +- .../src/main/java/org/restlet/Message.java | 873 ++- .../src/main/java/org/restlet/Request.java | 1783 +++--- .../src/main/java/org/restlet/Response.java | 1977 +++--- .../src/main/java/org/restlet/Restlet.java | 812 ++- .../src/main/java/org/restlet/Server.java | 947 ++- .../src/main/java/org/restlet/Uniform.java | 45 +- .../org/restlet/data/AuthenticationInfo.java | 431 +- .../java/org/restlet/data/CacheDirective.java | 859 ++- .../org/restlet/data/ChallengeMessage.java | 517 +- .../org/restlet/data/ChallengeRequest.java | 326 +- .../org/restlet/data/ChallengeResponse.java | 823 +-- .../org/restlet/data/ChallengeScheme.java | 430 +- .../java/org/restlet/data/CharacterSet.java | 565 +- .../java/org/restlet/data/ClientInfo.java | 1994 +++--- .../java/org/restlet/data/Conditions.java | 831 ++- .../main/java/org/restlet/data/Cookie.java | 392 +- .../java/org/restlet/data/CookieSetting.java | 484 +- .../main/java/org/restlet/data/Digest.java | 183 +- .../main/java/org/restlet/data/Dimension.java | 27 +- .../java/org/restlet/data/Disposition.java | 379 +- .../main/java/org/restlet/data/Encoding.java | 263 +- .../java/org/restlet/data/Expectation.java | 303 +- .../src/main/java/org/restlet/data/Form.java | 473 +- .../main/java/org/restlet/data/Header.java | 180 +- .../main/java/org/restlet/data/Language.java | 382 +- .../java/org/restlet/data/LocalReference.java | 721 ++- .../main/java/org/restlet/data/MediaType.java | 911 ++- .../main/java/org/restlet/data/Metadata.java | 221 +- .../main/java/org/restlet/data/Method.java | 723 +-- .../main/java/org/restlet/data/Parameter.java | 284 +- .../java/org/restlet/data/Preference.java | 242 +- .../main/java/org/restlet/data/Product.java | 156 +- .../main/java/org/restlet/data/Protocol.java | 705 ++- .../src/main/java/org/restlet/data/Range.java | 445 +- .../java/org/restlet/data/RecipientInfo.java | 158 +- .../main/java/org/restlet/data/Reference.java | 5581 +++++++++-------- .../java/org/restlet/data/ReferenceList.java | 289 +- .../java/org/restlet/data/ServerInfo.java | 184 +- .../main/java/org/restlet/data/Status.java | 1936 +++--- .../src/main/java/org/restlet/data/Tag.java | 319 +- .../main/java/org/restlet/data/Warning.java | 205 +- .../org/restlet/engine/CompositeHelper.java | 480 +- .../main/java/org/restlet/engine/Edition.java | 147 +- .../main/java/org/restlet/engine/Engine.java | 647 +- .../main/java/org/restlet/engine/Helper.java | 15 +- .../org/restlet/engine/RestletHelper.java | 295 +- .../org/restlet/engine/adapter/Adapter.java | 75 +- .../java/org/restlet/engine/adapter/Call.java | 124 +- .../restlet/engine/adapter/ClientAdapter.java | 107 +- .../restlet/engine/adapter/ClientCall.java | 144 +- .../engine/adapter/HttpClientHelper.java | 140 +- .../restlet/engine/adapter/HttpRequest.java | 1129 ++-- .../restlet/engine/adapter/HttpResponse.java | 118 +- .../engine/adapter/HttpServerHelper.java | 162 +- .../engine/adapter/JettyClientCall.java | 147 +- .../restlet/engine/adapter/JettyHandler.java | 37 +- .../engine/adapter/JettyServerCall.java | 47 +- .../restlet/engine/adapter/ServerAdapter.java | 403 +- .../restlet/engine/adapter/ServerCall.java | 971 +-- .../engine/application/ApplicationHelper.java | 282 +- .../restlet/engine/application/Conneg.java | 122 +- .../engine/application/CorsFilter.java | 653 +- .../application/CorsResponseHelper.java | 633 +- .../application/DecodeRepresentation.java | 371 +- .../restlet/engine/application/Decoder.java | 288 +- .../application/EncodeRepresentation.java | 496 +- .../restlet/engine/application/Encoder.java | 290 +- .../engine/application/FlexibleConneg.java | 369 +- .../engine/application/MetadataExtension.java | 142 +- .../engine/application/RangeFilter.java | 57 +- .../application/RangeRepresentation.java | 184 +- .../engine/application/StatusFilter.java | 296 +- .../engine/application/StatusInfo.java | 377 +- .../engine/application/StrictConneg.java | 633 +- .../engine/application/TunnelFilter.java | 1135 ++-- .../restlet/engine/component/ClientRoute.java | 121 +- .../engine/component/ClientRouter.java | 130 +- .../component/ComponentClientDispatcher.java | 233 +- .../engine/component/ComponentContext.java | 87 +- .../engine/component/ComponentHelper.java | 447 +- .../component/ComponentServerDispatcher.java | 82 +- .../restlet/engine/component/HostRoute.java | 347 +- .../engine/component/InternalRouter.java | 147 +- .../engine/component/ServerRouter.java | 163 +- .../engine/connector/ClientHelper.java | 35 +- .../engine/connector/ConnectorHelper.java | 110 +- .../engine/connector/HttpClientHelper.java | 236 +- .../engine/connector/HttpProtocolHelper.java | 38 +- .../engine/connector/HttpServerHelper.java | 49 +- .../engine/connector/HttpsServerHelper.java | 159 +- .../engine/connector/JettyServerHelper.java | 368 +- .../org/restlet/engine/connector/Method.java | 32 +- .../engine/connector/ProtocolHelper.java | 34 +- .../engine/connector/ServerHelper.java | 108 +- .../engine/converter/ConverterHelper.java | 381 +- .../engine/converter/ConverterUtils.java | 204 +- .../engine/converter/DefaultConverter.java | 530 +- .../converter/StatusInfoHtmlConverter.java | 292 +- .../engine/header/CacheDirectiveReader.java | 63 +- .../engine/header/CacheDirectiveWriter.java | 43 +- .../engine/header/ChallengeRequestReader.java | 127 +- .../engine/header/ChallengeWriter.java | 264 +- .../restlet/engine/header/ContentType.java | 275 +- .../engine/header/ContentTypeReader.java | 378 +- .../restlet/engine/header/CookieReader.java | 316 +- .../engine/header/CookieSettingReader.java | 429 +- .../engine/header/CookieSettingWriter.java | 273 +- .../restlet/engine/header/CookieWriter.java | 260 +- .../org/restlet/engine/header/DateWriter.java | 63 +- .../engine/header/DimensionReader.java | 101 +- .../engine/header/DimensionWriter.java | 125 +- .../engine/header/DispositionReader.java | 85 +- .../engine/header/DispositionWriter.java | 76 +- .../restlet/engine/header/EncodingReader.java | 51 +- .../restlet/engine/header/EncodingWriter.java | 40 +- .../engine/header/ExpectationReader.java | 77 +- .../engine/header/ExpectationWriter.java | 65 +- .../engine/header/HeaderConstants.java | 207 +- .../restlet/engine/header/HeaderReader.java | 1330 ++-- .../restlet/engine/header/HeaderUtils.java | 2274 ++++--- .../restlet/engine/header/HeaderWriter.java | 542 +- .../restlet/engine/header/LanguageReader.java | 39 +- .../restlet/engine/header/LanguageWriter.java | 33 +- .../restlet/engine/header/MetadataWriter.java | 20 +- .../restlet/engine/header/MethodReader.java | 63 +- .../restlet/engine/header/MethodWriter.java | 41 +- .../engine/header/PreferenceReader.java | 822 +-- .../engine/header/PreferenceWriter.java | 156 +- .../restlet/engine/header/ProductReader.java | 203 +- .../restlet/engine/header/ProductWriter.java | 67 +- .../restlet/engine/header/RangeReader.java | 104 +- .../restlet/engine/header/RangeWriter.java | 234 +- .../engine/header/RecipientInfoReader.java | 98 +- .../engine/header/RecipientInfoWriter.java | 84 +- .../restlet/engine/header/StringReader.java | 61 +- .../restlet/engine/header/StringWriter.java | 38 +- .../org/restlet/engine/header/TagReader.java | 63 +- .../org/restlet/engine/header/TagWriter.java | 63 +- .../restlet/engine/header/TokenReader.java | 36 +- .../restlet/engine/header/WarningReader.java | 101 +- .../restlet/engine/header/WarningWriter.java | 93 +- .../restlet/engine/internal/Activator.java | 252 +- .../java/org/restlet/engine/io/IoUtils.java | 1152 ++-- .../org/restlet/engine/io/PipeStream.java | 147 +- .../restlet/engine/io/RangeInputStream.java | 304 +- .../restlet/engine/io/ReaderInputStream.java | 246 +- .../engine/io/UnclosableInputStream.java | 36 +- .../engine/io/UnclosableOutputStream.java | 35 +- .../restlet/engine/io/WriterOutputStream.java | 83 +- .../engine/local/ClapClientHelper.java | 308 +- .../engine/local/DirectoryServerResource.java | 1540 ++--- .../java/org/restlet/engine/local/Entity.java | 594 +- .../engine/local/EntityClientHelper.java | 485 +- .../engine/local/FileClientHelper.java | 1124 ++-- .../org/restlet/engine/local/FileEntity.java | 157 +- .../engine/local/LocalClientHelper.java | 138 +- .../engine/local/RiapClientHelper.java | 94 +- .../engine/local/RiapServerHelper.java | 52 +- .../restlet/engine/local/ZipClientHelper.java | 580 +- .../restlet/engine/local/ZipEntryEntity.java | 213 +- .../engine/local/ZipEntryRepresentation.java | 111 +- .../engine/log/AccessLogFileHandler.java | 151 +- .../engine/log/AccessLogFormatter.java | 23 +- .../engine/log/DefaultAccessLogFormatter.java | 52 +- .../org/restlet/engine/log/IdentClient.java | 201 +- .../org/restlet/engine/log/LogFilter.java | 160 +- .../java/org/restlet/engine/log/LogUtils.java | 88 +- .../org/restlet/engine/log/LoggerFacade.java | 146 +- .../engine/log/LoggingThreadFactory.java | 96 +- .../restlet/engine/log/SimplerFormatter.java | 80 +- .../restlet/engine/log/SimplestFormatter.java | 72 +- .../engine/resource/AnnotationInfo.java | 454 +- .../engine/resource/AnnotationUtils.java | 651 +- .../resource/ClientInvocationHandler.java | 543 +- .../engine/resource/MethodAnnotationInfo.java | 781 +-- .../resource/ThrowableAnnotationInfo.java | 152 +- .../restlet/engine/resource/VariantInfo.java | 195 +- .../engine/security/AuthenticatorHelper.java | 320 +- .../engine/security/AuthenticatorUtils.java | 793 +-- .../engine/security/HttpBasicHelper.java | 309 +- .../RestletSslContextFactoryClient.java | 28 +- .../RestletSslContextFactoryServer.java | 28 +- .../restlet/engine/security/RoleMapping.java | 96 +- .../restlet/engine/ssl/DefaultSslContext.java | 59 +- .../engine/ssl/DefaultSslContextFactory.java | 1464 ++--- .../restlet/engine/ssl/SslContextFactory.java | 48 +- .../java/org/restlet/engine/ssl/SslUtils.java | 216 +- .../engine/ssl/WrapperSslContextSpi.java | 227 +- .../ssl/WrapperSslServerSocketFactory.java | 224 +- .../engine/ssl/WrapperSslSocketFactory.java | 249 +- .../engine/util/AlphaNumericComparator.java | 200 +- .../engine/util/AlphabeticalComparator.java | 82 +- .../restlet/engine/util/BeanInfoUtils.java | 83 +- .../org/restlet/engine/util/CallResolver.java | 470 +- .../engine/util/CaseInsensitiveHashSet.java | 65 +- .../engine/util/ChildClientDispatcher.java | 195 +- .../org/restlet/engine/util/ChildContext.java | 107 +- .../engine/util/ContextualRunnable.java | 61 +- .../org/restlet/engine/util/DateUtils.java | 427 +- .../engine/util/DefaultSaxHandler.java | 261 +- .../engine/util/EngineClassLoader.java | 296 +- .../org/restlet/engine/util/FormReader.java | 704 +-- .../org/restlet/engine/util/FormUtils.java | 459 +- .../restlet/engine/util/ImmutableDate.java | 157 +- .../engine/util/InternetDateFormat.java | 870 ++- .../org/restlet/engine/util/ListUtils.java | 61 +- .../org/restlet/engine/util/MapResolver.java | 42 +- .../java/org/restlet/engine/util/Pool.java | 208 +- .../restlet/engine/util/ReferenceUtils.java | 166 +- .../org/restlet/engine/util/SetUtils.java | 36 +- .../org/restlet/engine/util/StringUtils.java | 1135 ++-- .../org/restlet/engine/util/SystemUtils.java | 178 +- .../engine/util/TemplateDispatcher.java | 93 +- .../util/WrapperScheduledExecutorService.java | 243 +- .../AppendableRepresentation.java | 249 +- .../BufferingRepresentation.java | 312 +- .../ByteArrayRepresentation.java | 124 +- .../CharacterRepresentation.java | 57 +- .../DigesterRepresentation.java | 430 +- .../representation/EmptyRepresentation.java | 81 +- .../representation/FileRepresentation.java | 391 +- .../representation/InputRepresentation.java | 183 +- .../MultiPartRepresentation.java | 221 +- .../representation/ObjectRepresentation.java | 461 +- .../representation/OutputRepresentation.java | 82 +- .../representation/ReaderRepresentation.java | 164 +- .../representation/Representation.java | 947 ++- .../representation/RepresentationInfo.java | 303 +- .../representation/StreamRepresentation.java | 57 +- .../representation/StringRepresentation.java | 349 +- .../org/restlet/representation/Variant.java | 930 +-- .../representation/WriterRepresentation.java | 76 +- .../org/restlet/resource/ClientProxy.java | 32 +- .../org/restlet/resource/ClientResource.java | 3659 ++++++----- .../java/org/restlet/resource/Delete.java | 57 +- .../java/org/restlet/resource/Directory.java | 602 +- .../java/org/restlet/resource/Finder.java | 418 +- .../main/java/org/restlet/resource/Get.java | 59 +- .../java/org/restlet/resource/Options.java | 57 +- .../main/java/org/restlet/resource/Patch.java | 72 +- .../main/java/org/restlet/resource/Post.java | 72 +- .../main/java/org/restlet/resource/Put.java | 72 +- .../java/org/restlet/resource/Resource.java | 1692 +++-- .../restlet/resource/ResourceException.java | 447 +- .../java/org/restlet/resource/Result.java | 36 +- .../org/restlet/resource/ServerResource.java | 3333 +++++----- .../java/org/restlet/resource/Status.java | 60 +- .../java/org/restlet/routing/Extractor.java | 426 +- .../main/java/org/restlet/routing/Filter.java | 439 +- .../java/org/restlet/routing/Redirector.java | 925 ++- .../main/java/org/restlet/routing/Route.java | 113 +- .../main/java/org/restlet/routing/Router.java | 1424 ++--- .../java/org/restlet/routing/Template.java | 1640 ++--- .../org/restlet/routing/TemplateRoute.java | 464 +- .../java/org/restlet/routing/Validator.java | 312 +- .../java/org/restlet/routing/Variable.java | 502 +- .../java/org/restlet/routing/VirtualHost.java | 912 ++- .../org/restlet/security/Authenticator.java | 445 +- .../java/org/restlet/security/Authorizer.java | 245 +- .../security/CertificateAuthenticator.java | 180 +- .../security/ChallengeAuthenticator.java | 577 +- .../security/ConfidentialAuthorizer.java | 38 +- .../java/org/restlet/security/Enroler.java | 40 +- .../main/java/org/restlet/security/Group.java | 330 +- .../org/restlet/security/LocalVerifier.java | 39 +- .../org/restlet/security/MapVerifier.java | 102 +- .../org/restlet/security/MemoryRealm.java | 986 ++- .../restlet/security/MethodAuthorizer.java | 228 +- .../main/java/org/restlet/security/Realm.java | 328 +- .../main/java/org/restlet/security/Role.java | 376 +- .../org/restlet/security/RoleAuthorizer.java | 237 +- .../org/restlet/security/SecretVerifier.java | 215 +- .../main/java/org/restlet/security/User.java | 380 +- .../java/org/restlet/security/Verifier.java | 53 +- .../org/restlet/service/ConnectorService.java | 195 +- .../org/restlet/service/ConnegService.java | 137 +- .../org/restlet/service/ConverterService.java | 620 +- .../java/org/restlet/service/CorsService.java | 471 +- .../org/restlet/service/DecoderService.java | 66 +- .../org/restlet/service/EncoderService.java | 349 +- .../java/org/restlet/service/LogService.java | 751 +-- .../org/restlet/service/MetadataService.java | 1550 ++--- .../org/restlet/service/RangeService.java | 53 +- .../java/org/restlet/service/Service.java | 247 +- .../org/restlet/service/StatusService.java | 673 +- .../java/org/restlet/service/TaskService.java | 1276 ++-- .../org/restlet/service/TunnelService.java | 829 ++- .../java/org/restlet/util/ClientList.java | 108 +- .../java/org/restlet/util/NamedValue.java | 48 +- .../main/java/org/restlet/util/Resolver.java | 89 +- .../main/java/org/restlet/util/RouteList.java | 386 +- .../main/java/org/restlet/util/Series.java | 1012 ++- .../java/org/restlet/util/ServerList.java | 235 +- .../java/org/restlet/util/ServiceList.java | 333 +- .../java/org/restlet/util/WrapperList.java | 617 +- .../java/org/restlet/util/WrapperMap.java | 347 +- .../restlet/util/WrapperRepresentation.java | 439 +- .../java/org/restlet/util/WrapperRequest.java | 955 ++- .../org/restlet/util/WrapperResponse.java | 945 ++- .../java/org/restlet/util/WrapperRestlet.java | 215 +- .../restlet/ApplicationContextTestCase.java | 21 +- .../java/org/restlet/Bug1145TestCase.java | 10 +- .../test/java/org/restlet/CallTestCase.java | 100 +- .../java/org/restlet/RestartTestCase.java | 13 +- .../data/AuthenticationInfoTestCase.java | 88 +- .../org/restlet/data/ClientInfoTestCase.java | 61 +- .../java/org/restlet/data/CookieTestCase.java | 24 +- .../org/restlet/data/FileClientTestCase.java | 22 +- .../restlet/data/FileReferenceTestCase.java | 16 +- .../java/org/restlet/data/FormTestCase.java | 21 +- .../org/restlet/data/LanguageTestCase.java | 21 +- .../org/restlet/data/MediaTypeTestCase.java | 206 +- .../java/org/restlet/data/MethodTestCase.java | 32 +- .../restlet/data/ProductTokenTestCase.java | 111 +- .../java/org/restlet/data/RangeTestCase.java | 62 +- .../restlet/data/RecipientInfoTestCase.java | 44 +- .../org/restlet/data/ReferenceTestCase.java | 385 +- .../restlet/data/RiapConnectorsTestCase.java | 80 +- .../java/org/restlet/data/RiapTestCase.java | 103 +- .../java/org/restlet/data/StatusTestCase.java | 30 +- .../java/org/restlet/data/TagTestCase.java | 20 +- .../org/restlet/data/ZipClientTestCase.java | 32 +- .../java/org/restlet/engine/EngineTest.java | 24 +- .../CorsResponseFilterTestCase.java | 45 +- .../RangeRepresentationTestCase.java | 18 +- .../application/TunnelFilterTestCase.java | 120 +- .../engine/connector/AsyncTestCase.java | 127 +- .../connector/BaseConnectorsTestCase.java | 30 +- .../connector/ChunkedEncodingPutTestCase.java | 58 +- .../connector/ChunkedEncodingTestCase.java | 39 +- .../engine/connector/ConnectorsPair.java | 12 +- .../engine/connector/GetChunkedTestCase.java | 19 +- .../connector/GetQueryParamTestCase.java | 36 +- .../restlet/engine/connector/GetTestCase.java | 26 +- .../HttpTransportProtocolsTestCase.java | 117 +- .../org/restlet/engine/connector/Lock.java | 13 +- .../MultiPartRepresentationTestCase.java | 71 +- .../engine/connector/PostPutTestCase.java | 12 +- .../RemoteClientAddressTestCase.java | 42 +- .../ServerMaxConnectionsTestCase.java | 42 +- .../connector/ShutdownHookTestCase.java | 215 +- .../connector/SslBaseConnectorsTestCase.java | 30 +- .../SslClientContextGetTestCase.java | 17 +- .../engine/connector/SslGetTestCase.java | 15 +- .../engine/header/ContentTypeTestCase.java | 24 +- .../engine/header/CookieReaderTestCase.java | 97 +- .../header/DispositionReaderTestCase.java | 63 +- .../header/DispositionWriterTestCase.java | 35 +- .../restlet/engine/header/HeaderTestCase.java | 105 +- .../engine/header/HeaderUtilsTestCase.java | 152 +- .../header/PreferenceReaderTestCase.java | 36 +- .../engine/header/RangeReaderTestCase.java | 23 +- .../restlet/engine/io/IoUtilsTestCase.java | 41 +- .../engine/io/ReaderInputStreamTestCase.java | 22 +- .../io/UnclosableInputStreamTestCase.java | 16 +- .../io/UnclosableOutputStreamTestCase.java | 26 +- .../AbstractAnnotatedServerResource03.java | 12 +- .../jetty/resource/AnnotatedInterface03.java | 19 +- .../resource/AnnotatedInterface03_01.java | 13 +- .../resource/AnnotatedInterface03_02.java | 13 +- .../resource/AnnotatedResource09TestCase.java | 51 +- .../resource/AnnotatedResource10TestCase.java | 15 +- .../resource/AnnotatedResource11TestCase.java | 19 +- .../resource/JettyConnectorTestCase.java | 18 +- .../engine/jetty/resource/MyResource09.java | 9 +- .../engine/jetty/resource/MyResource10.java | 16 +- .../engine/jetty/resource/MyResource11.java | 18 +- .../engine/jetty/resource/SIMethod.java | 11 +- .../engine/jetty/resource/SNIMethod.java | 11 +- .../engine/jetty/resource/USIMethod.java | 11 +- .../engine/jetty/resource/USNIMethod.java | 11 +- .../resource/AnnotationUtilsTestCase.java | 23 +- .../util/AlphaNumericComparatorTestCase.java | 38 +- .../engine/util/DateUtilsTestCase.java | 71 +- .../engine/util/HtmlEncodingTestCase.java | 39 +- .../AppendableRepresentationTestCase.java | 16 +- .../DigesterRepresentationTestCase.java | 80 +- .../FileRepresentationTestCase.java | 35 +- .../AbstractAnnotatedResourceTestCase.java | 10 +- ...ctAnnotatedResourceWithFinderTestCase.java | 13 +- ...bstractGenericAnnotatedServerResource.java | 9 +- .../resource/AnnotatedResource01TestCase.java | 25 +- .../resource/AnnotatedResource02TestCase.java | 21 +- .../resource/AnnotatedResource03TestCase.java | 14 +- .../resource/AnnotatedResource04TestCase.java | 29 +- .../resource/AnnotatedResource05TestCase.java | 21 +- .../resource/AnnotatedResource06TestCase.java | 24 +- .../resource/AnnotatedResource07TestCase.java | 24 +- .../resource/AnnotatedResource08TestCase.java | 38 +- .../resource/AnnotatedResource12TestCase.java | 16 +- .../resource/AnnotatedResource13TestCase.java | 58 +- .../resource/AnnotatedResource14TestCase.java | 43 +- .../AnnotatedResource15OkTestCase.java | 15 +- .../resource/AnnotatedResource15TestCase.java | 26 +- .../resource/AnnotatedResource16TestCase.java | 20 +- .../resource/AnnotatedResource17TestCase.java | 27 +- .../resource/AnnotatedResource18TestCase.java | 27 +- .../resource/AnnotatedResource20TestCase.java | 21 +- .../restlet/resource/DirectoryTestCase.java | 494 +- .../GenericAnnotatedServerResource.java | 9 +- .../resource/GenericServerResource16.java | 13 +- .../resource/GenericServerResource17.java | 13 +- .../java/org/restlet/resource/MyBean.java | 31 +- .../org/restlet/resource/MyException01.java | 13 +- .../org/restlet/resource/MyException02.java | 9 +- .../org/restlet/resource/MyResource01.java | 12 +- .../org/restlet/resource/MyResource02.java | 10 +- .../org/restlet/resource/MyResource03.java | 12 +- .../org/restlet/resource/MyResource04.java | 10 +- .../org/restlet/resource/MyResource05.java | 10 +- .../org/restlet/resource/MyResource06.java | 10 +- .../org/restlet/resource/MyResource07.java | 10 +- .../org/restlet/resource/MyResource08.java | 10 +- .../org/restlet/resource/MyResource12.java | 12 +- .../org/restlet/resource/MyResource17.java | 10 +- .../org/restlet/resource/MyResource20.java | 12 +- .../restlet/resource/MyServerResource01.java | 12 +- .../restlet/resource/MyServerResource12.java | 12 +- .../restlet/resource/MyServerResource15.java | 11 +- .../resource/MyServerResource15Ok.java | 11 +- .../restlet/resource/MyServerResource16.java | 15 +- .../restlet/resource/MyServerResource17.java | 15 +- .../restlet/resource/MyServerResource18.java | 15 +- .../restlet/resource/MyServerResource20.java | 13 +- .../routing/AbstractFilterTestCase.java | 42 +- .../org/restlet/routing/FilterTestCase.java | 15 +- .../java/org/restlet/routing/MockFilter.java | 53 +- .../java/org/restlet/routing/MockRestlet.java | 18 +- .../org/restlet/routing/RedirectTestCase.java | 81 +- .../restlet/routing/RouteListTestCase.java | 22 +- .../org/restlet/routing/TemplateTestCase.java | 32 +- .../org/restlet/routing/TraceRestlet.java | 34 +- .../restlet/routing/ValidatorTestCase.java | 13 +- .../restlet/security/HelloWorldRestlet.java | 12 +- .../restlet/security/HttpBasicTestCase.java | 299 +- .../org/restlet/security/MemoryRealmTest.java | 103 +- .../org/restlet/security/RoleTestCase.java | 15 +- .../org/restlet/security/SaasApplication.java | 26 +- .../org/restlet/security/SaasComponent.java | 17 +- .../restlet/security/SecurityTestCase.java | 63 +- .../service/ConnegServiceTestCase.java | 36 +- .../service/MetadataServiceTestCase.java | 13 +- .../service/StatusServiceTestCase.java | 75 +- .../service/UserAgentTestResource.java | 13 +- .../UserAgentTunnelFilterTestCase.java | 34 +- pom.xml | 50 +- 542 files changed, 67800 insertions(+), 69084 deletions(-) diff --git a/mvnw b/mvnw index aa09908bea..b8d231c9f6 100755 --- a/mvnw +++ b/mvnw @@ -28,7 +28,7 @@ # Optional ENV vars # ----------------- # MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use +# e.g., to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- diff --git a/mvnw.cmd b/mvnw.cmd index 1088545e13..fe66b73afa 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -27,7 +27,7 @@ @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use +@REM e.g., to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @@ -81,7 +81,7 @@ goto error :init -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Find the project base dir, i.e., the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java index 94215304db..55f8b58a7e 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; import org.restlet.Context; @@ -18,19 +17,16 @@ /** * Authenticator supporting the {@link ChallengeScheme#HTTP_AWS_S3} scheme. - * + * * @author Jean-Philippe Steinmetz */ public class AwsAuthenticator extends ChallengeAuthenticator { /** * Creates a new HttpAwsS3Authenticator instance. - * - * @param context - * The context - * @param optional - * Indicates if the authentication success is optional - * @param realm - * The authentication realm + * + * @param context The context + * @param optional Indicates if the authentication success is optional + * @param realm The authentication realm */ public AwsAuthenticator(Context context, boolean optional, String realm) { this(context, optional, realm, new AwsVerifier(null)); @@ -38,38 +34,30 @@ public AwsAuthenticator(Context context, boolean optional, String realm) { /** * Creates a new HttpAwsS3Authenticator instance. - * - * @param context - * The context - * @param optional - * Indicates if the authentication success is optional - * @param realm - * The authentication realm + * + * @param context The context + * @param optional Indicates if the authentication success is optional + * @param realm The authentication realm * @param verifier */ - public AwsAuthenticator(Context context, boolean optional, String realm, - Verifier verifier) { + public AwsAuthenticator(Context context, boolean optional, String realm, Verifier verifier) { super(context, optional, ChallengeScheme.HTTP_AWS_S3, realm, verifier); } /** * Creates a new HttpAwsS3Authenticator instance. - * - * @param context - * The context - * @param realm - * The authentication realm + * + * @param context The context + * @param realm The authentication realm */ public AwsAuthenticator(Context context, String realm) { this(context, false, realm); } /** - * Returns the maximum age of a request, in milliseconds, before it is - * considered stale. - *

- * A negative or zero value indicates no age restriction. The default value - * is 15 minutes. + * Returns the maximum age of a request, in milliseconds, before it is considered stale. + * + *

A negative or zero value indicates no age restriction. The default value is 15 minutes. */ public long getMaxRequestAge() { return getVerifier().getMaxRequestAge(); @@ -81,9 +69,9 @@ public AwsVerifier getVerifier() { } /** - * Returns the secret verifier that will be wrapped by the real verifier - * supporting all the HTTP AWS verifications. - * + * Returns the secret verifier that will be wrapped by the real verifier supporting all the HTTP + * AWS verifications. + * * @return the local wrapped verifier */ public LocalVerifier getWrappedVerifier() { @@ -91,35 +79,30 @@ public LocalVerifier getWrappedVerifier() { } /** - * Sets the maximum age of a request, in milliseconds, before it is - * considered stale. - *

- * A negative or zero value indicates no age restriction. The default value - * is 15 minutes. + * Sets the maximum age of a request, in milliseconds, before it is considered stale. + * + *

A negative or zero value indicates no age restriction. The default value is 15 minutes. */ public void setMaxRequestAge(long value) { getVerifier().setMaxRequestAge(value); } /** - * Sets the internal verifier. In general you shouldn't replace it but - * instead set the {@code wrappedVerifier} via the - * {@link #setWrappedVerifier(LocalVerifier)} method. + * Sets the internal verifier. In general you shouldn't replace it but instead set the {@code + * wrappedVerifier} via the {@link #setWrappedVerifier(LocalVerifier)} method. */ @Override public void setVerifier(Verifier verifier) { - if (!(verifier instanceof AwsVerifier)) - throw new IllegalArgumentException(); + if (!(verifier instanceof AwsVerifier)) throw new IllegalArgumentException(); super.setVerifier(verifier); } /** - * Sets the secret verifier that will be wrapped by the real verifier - * supporting all the HTTP AWS verifications. - * - * @param verifier - * The local verifier to wrap + * Sets the secret verifier that will be wrapped by the real verifier supporting all the HTTP + * AWS verifications. + * + * @param verifier The local verifier to wrap */ public void setWrappedVerifier(LocalVerifier verifier) { getVerifier().setWrappedVerifier(verifier); diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java index 3a2de458cc..c8a9495e9d 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java @@ -1,18 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; import java.security.GeneralSecurityException; import java.util.Base64; import java.util.logging.Level; - import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -28,33 +26,29 @@ import org.restlet.security.ChallengeAuthenticator; /** - * Challenge authenticator based on browser cookies. This is useful when the web - * application requires a finer grained control on the login and logout process - * and can't rely solely on standard schemes such as - * {@link ChallengeScheme#HTTP_BASIC}.
+ * Challenge authenticator based on browser cookies. This is useful when the web application + * requires a finer grained control on the login and logout process and can't rely solely on + * standard schemes such as {@link ChallengeScheme#HTTP_BASIC}.
*
- * Login can be automatically handled by intercepting HTTP POST calls to the - * {@link #getLoginPath()} URI. The request entity should contain an HTML form - * with two fields, the first one named {@link #getIdentifierFormName()} and the - * second one named {@link #getSecretFormName()}.
+ * Login can be automatically handled by intercepting HTTP POST calls to the {@link #getLoginPath()} + * URI. The request entity should contain an HTML form with two fields, the first one named {@link + * #getIdentifierFormName()} and the second one named {@link #getSecretFormName()}.
*
- * Logout can be automatically handled as well by intercepting HTTP GET or POST - * calls to the {@link #getLogoutPath()} URI.
+ * Logout can be automatically handled as well by intercepting HTTP GET or POST calls to the {@link + * #getLogoutPath()} URI.
*
- * After login or logout, the user's browser can be redirected to the URI - * provided in a query parameter named by {@link #getRedirectQueryName()}.
+ * After login or logout, the user's browser can be redirected to the URI provided in a query + * parameter named by {@link #getRedirectQueryName()}.
*
- * When the credentials are missing or stale, the - * {@link #challenge(Response, boolean)} method is invoked by the parent class, - * and its default behavior is to redirect the user's browser to the - * {@link #getLoginFormPath()} URI, adding the URI of the target resource as a - * query parameter of name {@link #getRedirectQueryName()}.
+ * When the credentials are missing or stale, the {@link #challenge(Response, boolean)} method is + * invoked by the parent class, and its default behavior is to redirect the user's browser to the + * {@link #getLoginFormPath()} URI, adding the URI of the target resource as a query parameter of + * name {@link #getRedirectQueryName()}.
*
- * Note that credentials, both identifier and secret, are stored in a cookie in - * an encrypted manner. The default encryption algorithm is AES but can be - * changed with {@link #setEncryptAlgorithm(String)}. It is also strongly - * recommended to - * + * Note that credentials, both identifier and secret, are stored in a cookie in an encrypted manner. + * The default encryption algorithm is AES but can be changed with {@link + * #setEncryptAlgorithm(String)}. It is also strongly recommended to + * * @author Remi Dewitte * @author Jerome Louvel */ @@ -66,10 +60,7 @@ public class CookieAuthenticator extends ChallengeAuthenticator { /** The name of the algorithm used to encrypt the log info cookie value. */ private volatile String encryptAlgorithm; - /** - * The secret key for the algorithm used to encrypt the log info cookie - * value. - */ + /** The secret key for the algorithm used to encrypt the log info cookie value. */ private volatile byte[] encryptSecretKey; /** The name of the HTML login form field containing the identifier. */ @@ -94,8 +85,8 @@ public class CookieAuthenticator extends ChallengeAuthenticator { private volatile int maxCookieAge; /** - * The name of the query parameter containing the URI to redirect the - * browser to after login or logout. + * The name of the query parameter containing the URI to redirect the browser to after login or + * logout. */ private volatile String redirectQueryName; @@ -104,19 +95,15 @@ public class CookieAuthenticator extends ChallengeAuthenticator { /** * Constructor. Use the {@link ChallengeScheme#HTTP_COOKIE} pseudo-scheme. - * - * @param context - * The parent context. - * @param optional - * Indicates if this authenticator is optional so alternative - * authenticators down the chain can be attempted. - * @param realm - * The name of the security realm. - * @param encryptSecretKey - * The secret key used to encrypt the cookie value. - */ - public CookieAuthenticator(Context context, boolean optional, String realm, - byte[] encryptSecretKey) { + * + * @param context The parent context. + * @param optional Indicates if this authenticator is optional so alternative authenticators + * down the chain can be attempted. + * @param realm The name of the security realm. + * @param encryptSecretKey The secret key used to encrypt the cookie value. + */ + public CookieAuthenticator( + Context context, boolean optional, String realm, byte[] encryptSecretKey) { super(context, optional, ChallengeScheme.HTTP_COOKIE, realm); this.cookieName = "Credentials"; this.interceptingLogin = true; @@ -133,31 +120,25 @@ public CookieAuthenticator(Context context, boolean optional, String realm, /** * Constructor for mandatory cookie authenticators. - * - * @param context - * The parent context. - * @param realm - * The name of the security realm. - * @param encryptSecretKey - * The secret key used to encrypt the cookie value. - */ - public CookieAuthenticator(Context context, String realm, - byte[] encryptSecretKey) { + * + * @param context The parent context. + * @param realm The name of the security realm. + * @param encryptSecretKey The secret key used to encrypt the cookie value. + */ + public CookieAuthenticator(Context context, String realm, byte[] encryptSecretKey) { this(context, false, realm, encryptSecretKey); } /** - * Attempts to redirect the user's browser to the URI provided in a query - * parameter named by {@link #getRedirectQueryName()}. - * - * @param request - * The current request. - * @param response - * The current response. + * Attempts to redirect the user's browser to the URI provided in a query parameter named by + * {@link #getRedirectQueryName()}. + * + * @param request The current request. + * @param response The current response. */ protected void attemptRedirect(Request request, Response response) { - String targetUri = request.getResourceRef().getQueryAsForm() - .getFirstValue(getRedirectQueryName()); + String targetUri = + request.getResourceRef().getQueryAsForm().getFirstValue(getRedirectQueryName()); if (targetUri != null) { response.redirectSeeOther(Reference.decode(targetUri)); @@ -165,45 +146,38 @@ protected void attemptRedirect(Request request, Response response) { } /** - * Restores credentials from the cookie named {@link #getCookieName()} if - * available. The usual processing is the followed. + * Restores credentials from the cookie named {@link #getCookieName()} if available. The usual + * processing is the followed. */ @Override protected boolean authenticate(Request request, Response response) { // Restore credentials from the cookie - Cookie credentialsCookie = request.getCookies().getFirst( - getCookieName()); + Cookie credentialsCookie = request.getCookies().getFirst(getCookieName()); if (credentialsCookie != null) { - request.setChallengeResponse(parseCredentials(credentialsCookie - .getValue())); + request.setChallengeResponse(parseCredentials(credentialsCookie.getValue())); } return super.authenticate(request, response); } - /** - * Sets or updates the credentials cookie. - */ + /** Sets or updates the credentials cookie. */ @Override protected int authenticated(Request request, Response response) { try { - CookieSetting credentialsCookie = getCredentialsCookie(request, - response); - credentialsCookie.setValue(formatCredentials(request - .getChallengeResponse())); + CookieSetting credentialsCookie = getCredentialsCookie(request, response); + credentialsCookie.setValue(formatCredentials(request.getChallengeResponse())); credentialsCookie.setMaxAge(getMaxCookieAge()); } catch (GeneralSecurityException e) { - getLogger().log(Level.SEVERE, - "Could not format credentials cookie", e); + getLogger().log(Level.SEVERE, "Could not format credentials cookie", e); } return super.authenticated(request, response); } /** - * Optionally handles the login and logout actions by intercepting the HTTP - * calls to the {@link #getLoginPath()} and {@link #getLogoutPath()} URIs. + * Optionally handles the login and logout actions by intercepting the HTTP calls to the {@link + * #getLoginPath()} and {@link #getLogoutPath()} URIs. */ @Override protected int beforeHandle(Request request, Response response) { @@ -218,9 +192,9 @@ protected int beforeHandle(Request request, Response response) { /** * This method should be overridden to return a login form representation.
- * By default, it redirects the user's browser to the - * {@link #getLoginFormPath()} URI, adding the URI of the target resource as - * a query parameter of name {@link #getRedirectQueryName()}.
+ * By default, it redirects the user's browser to the {@link #getLoginFormPath()} URI, adding + * the URI of the target resource as a query parameter of name {@link #getRedirectQueryName()}. + *
* In case the getLoginFormPath() is not set, it calls the parent's method. */ @Override @@ -230,13 +204,13 @@ public void challenge(Response response, boolean stale) { } else { Reference ref = response.getRequest().getResourceRef(); String redirectQueryName = getRedirectQueryName(); - String redirectQueryValue = ref.getQueryAsForm().getFirstValue( - redirectQueryName, ""); + String redirectQueryValue = ref.getQueryAsForm().getFirstValue(redirectQueryName, ""); if ("".equals(redirectQueryValue)) { - redirectQueryValue = new Reference(getLoginFormPath()) - .addQueryParameter(redirectQueryName, ref.toString()) - .toString(); + redirectQueryValue = + new Reference(getLoginFormPath()) + .addQueryParameter(redirectQueryName, ref.toString()) + .toString(); } response.redirectSeeOther(redirectQueryValue); @@ -245,14 +219,12 @@ public void challenge(Response response, boolean stale) { /** * Formats the raws credentials to store in the cookie. - * - * @param challenge - * The challenge response to format. + * + * @param challenge The challenge response to format. * @return The raw credentials. * @throws GeneralSecurityException */ - public String formatCredentials(ChallengeResponse challenge) - throws GeneralSecurityException { + public String formatCredentials(ChallengeResponse challenge) throws GeneralSecurityException { // Data buffer StringBuffer sb = new StringBuffer(); @@ -278,14 +250,16 @@ public String formatCredentials(ChallengeResponse challenge) sb.append('/'); sb.append(isb); - return Base64.getEncoder().encodeToString(CryptoUtils.encrypt(getEncryptAlgorithm(), - getEncryptSecretKey(), sb.toString())); + return Base64.getEncoder() + .encodeToString( + CryptoUtils.encrypt( + getEncryptAlgorithm(), getEncryptSecretKey(), sb.toString())); } /** - * Returns the cookie name to use for the authentication credentials. By - * default, it is is "Credentials". - * + * Returns the cookie name to use for the authentication credentials. By default, it is is + * "Credentials". + * * @return The cookie name to use for the authentication credentials. */ public String getCookieName() { @@ -293,19 +267,15 @@ public String getCookieName() { } /** - * Returns the credentials cookie setting. It first try to find an existing - * cookie. If necessary, it creates a new one. - * - * @param request - * The current request. - * @param response - * The current response. + * Returns the credentials cookie setting. It first try to find an existing cookie. If + * necessary, it creates a new one. + * + * @param request The current request. + * @param response The current response. * @return The credentials cookie setting. */ - protected CookieSetting getCredentialsCookie(Request request, - Response response) { - CookieSetting credentialsCookie = response.getCookieSettings() - .getFirst(getCookieName()); + protected CookieSetting getCredentialsCookie(Request request, Response response) { + CookieSetting credentialsCookie = response.getCookieSettings().getFirst(getCookieName()); if (credentialsCookie == null) { credentialsCookie = new CookieSetting(getCookieName(), null); @@ -326,31 +296,28 @@ protected CookieSetting getCredentialsCookie(Request request, } /** - * Returns the name of the algorithm used to encrypt the log info cookie - * value. By default, it returns "AES". - * - * @return The name of the algorithm used to encrypt the log info cookie - * value. + * Returns the name of the algorithm used to encrypt the log info cookie value. By default, it + * returns "AES". + * + * @return The name of the algorithm used to encrypt the log info cookie value. */ public String getEncryptAlgorithm() { return encryptAlgorithm; } /** - * Returns the secret key for the algorithm used to encrypt the log info - * cookie value. - * - * @return The secret key for the algorithm used to encrypt the log info - * cookie value. + * Returns the secret key for the algorithm used to encrypt the log info cookie value. + * + * @return The secret key for the algorithm used to encrypt the log info cookie value. */ public byte[] getEncryptSecretKey() { return encryptSecretKey; } /** - * Returns the name of the HTML login form field containing the identifier. - * Returns "login" by default. - * + * Returns the name of the HTML login form field containing the identifier. Returns "login" by + * default. + * * @return The name of the HTML login form field containing the identifier. */ public String getIdentifierFormName() { @@ -359,7 +326,7 @@ public String getIdentifierFormName() { /** * Returns the URI path of the HTML login form to use to challenge the user. - * + * * @return The URI path of the HTML login form to use to challenge the user. */ public String getLoginFormPath() { @@ -368,7 +335,7 @@ public String getLoginFormPath() { /** * Returns the login URI path to intercept. - * + * * @return The login URI path to intercept. */ public String getLoginPath() { @@ -377,7 +344,7 @@ public String getLoginPath() { /** * Returns the logout URI path to intercept. - * + * * @return The logout URI path to intercept. */ public String getLogoutPath() { @@ -385,9 +352,9 @@ public String getLogoutPath() { } /** - * Returns the maximum age of the log info cookie. By default, it uses -1 to - * make the cookie only last until the end of the current browser session. - * + * Returns the maximum age of the log info cookie. By default, it uses -1 to make the cookie + * only last until the end of the current browser session. + * * @return The maximum age of the log info cookie. * @see CookieSetting#getMaxAge() */ @@ -396,20 +363,20 @@ public int getMaxCookieAge() { } /** - * Returns the name of the query parameter containing the URI to redirect - * the browser to after login or logout. By default, it uses "targetUri". - * - * @return The name of the query parameter containing the URI to redirect - * the browser to after login or logout. + * Returns the name of the query parameter containing the URI to redirect the browser to after + * login or logout. By default, it uses "targetUri". + * + * @return The name of the query parameter containing the URI to redirect the browser to after + * login or logout. */ public String getRedirectQueryName() { return redirectQueryName; } /** - * Returns the name of the HTML login form field containing the secret. - * Returns "password" by default. - * + * Returns the name of the HTML login form field containing the secret. Returns "password" by + * default. + * * @return The name of the HTML login form field containing the secret. */ public String getSecretFormName() { @@ -418,7 +385,7 @@ public String getSecretFormName() { /** * Indicates if the login requests should be intercepted. - * + * * @return True if the login requests should be intercepted. */ public boolean isInterceptingLogin() { @@ -427,7 +394,7 @@ public boolean isInterceptingLogin() { /** * Indicates if the logout requests should be intercepted. - * + * * @return True if the logout requests should be intercepted. */ public boolean isInterceptingLogout() { @@ -435,51 +402,37 @@ public boolean isInterceptingLogout() { } /** - * Indicates if the request is an attempt to log in and should be - * intercepted. - * - * @param request - * The current request. - * @param response - * The current response. - * @return True if the request is an attempt to log in and should be - * intercepted. + * Indicates if the request is an attempt to log in and should be intercepted. + * + * @param request The current request. + * @param response The current response. + * @return True if the request is an attempt to log in and should be intercepted. */ protected boolean isLoggingIn(Request request, Response response) { return isInterceptingLogin() - && getLoginPath() - .equals(request.getResourceRef().getRemainingPart( - false, false)) + && getLoginPath().equals(request.getResourceRef().getRemainingPart(false, false)) && Method.POST.equals(request.getMethod()); } /** - * Indicates if the request is an attempt to log out and should be - * intercepted. - * - * @param request - * The current request. - * @param response - * The current response. - * @return True if the request is an attempt to log out and should be - * intercepted. + * Indicates if the request is an attempt to log out and should be intercepted. + * + * @param request The current request. + * @param response The current response. + * @return True if the request is an attempt to log out and should be intercepted. */ protected boolean isLoggingOut(Request request, Response response) { return isInterceptingLogout() - && getLogoutPath() - .equals(request.getResourceRef().getRemainingPart( - false, false)) - && (Method.GET.equals(request.getMethod()) || Method.POST - .equals(request.getMethod())); + && getLogoutPath().equals(request.getResourceRef().getRemainingPart(false, false)) + && (Method.GET.equals(request.getMethod()) + || Method.POST.equals(request.getMethod())); } /** * Processes the login request. - * - * @param request - * The current request. - * @param response - * The current response. + * + * @param request The current request. + * @param response The current response. */ protected void login(Request request, Response response) { // Login detected @@ -488,9 +441,11 @@ protected void login(Request request, Response response) { Parameter secret = form.getFirst(getSecretFormName()); // Set credentials - ChallengeResponse cr = new ChallengeResponse(getScheme(), - identifier != null ? identifier.getValue() : null, - secret != null ? secret.getValue() : null); + ChallengeResponse cr = + new ChallengeResponse( + getScheme(), + identifier != null ? identifier.getValue() : null, + secret != null ? secret.getValue() : null); request.setChallengeResponse(cr); // Attempt to redirect @@ -499,17 +454,14 @@ protected void login(Request request, Response response) { /** * Processes the logout request. - * - * @param request - * The current request. - * @param response - * The current response. + * + * @param request The current request. + * @param response The current response. */ protected int logout(Request request, Response response) { // Clears the credentials request.setChallengeResponse(null); - CookieSetting credentialsCookie = getCredentialsCookie(request, - response); + CookieSetting credentialsCookie = getCredentialsCookie(request, response); credentialsCookie.setMaxAge(0); // Attempt to redirect @@ -519,26 +471,23 @@ protected int logout(Request request, Response response) { } /** - * Decodes the credentials stored in a cookie into a proper - * {@link ChallengeResponse} object. - * - * @param cookieValue - * The credentials to decode from cookie value. + * Decodes the credentials stored in a cookie into a proper {@link ChallengeResponse} object. + * + * @param cookieValue The credentials to decode from cookie value. * @return The credentials as a proper challenge response. */ protected ChallengeResponse parseCredentials(String cookieValue) { try { // 1) Decode Base64 string byte[] encrypted = Base64.getDecoder().decode(cookieValue); - + if (encrypted == null) { - getLogger().warning( - "Cannot decode cookie credentials : " + cookieValue); + getLogger().warning("Cannot decode cookie credentials : " + cookieValue); } - + // 2) Decrypt the credentials - String decrypted = CryptoUtils.decrypt(getEncryptAlgorithm(), - getEncryptSecretKey(), encrypted); + String decrypted = + CryptoUtils.decrypt(getEncryptAlgorithm(), getEncryptSecretKey(), encrypted); // 3) Parse the decrypted cookie value int lastSlash = decrypted.lastIndexOf('/'); @@ -549,24 +498,20 @@ protected ChallengeResponse parseCredentials(String cookieValue) { // 4) Create the challenge response ChallengeResponse cr = new ChallengeResponse(getScheme()); cr.setRawValue(cookieValue); - cr.setTimeIssued(Long.parseLong(decrypted.substring(0, - identifierIndex))); - cr.setIdentifier(decrypted.substring(identifierIndex + 1, - secretIndex)); + cr.setTimeIssued(Long.parseLong(decrypted.substring(0, identifierIndex))); + cr.setIdentifier(decrypted.substring(identifierIndex + 1, secretIndex)); cr.setSecret(decrypted.substring(secretIndex + 1, lastSlash)); return cr; } catch (Exception e) { - getLogger().log(Level.INFO, "Unable to decrypt cookie credentials", - e); + getLogger().log(Level.INFO, "Unable to decrypt cookie credentials", e); return null; } } /** * Sets the cookie name to use for the authentication credentials. - * - * @param cookieName - * The cookie name to use for the authentication credentials. + * + * @param cookieName The cookie name to use for the authentication credentials. */ public void setCookieName(String cookieName) { this.cookieName = cookieName; @@ -574,22 +519,17 @@ public void setCookieName(String cookieName) { /** * Sets the name of the algorithm used to encrypt the log info cookie value. - * - * @param secretAlgorithm - * The name of the algorithm used to encrypt the log info cookie - * value. + * + * @param secretAlgorithm The name of the algorithm used to encrypt the log info cookie value. */ public void setEncryptAlgorithm(String secretAlgorithm) { this.encryptAlgorithm = secretAlgorithm; } /** - * Sets the secret key for the algorithm used to encrypt the log info cookie - * value. - * - * @param secretKey - * The secret key for the algorithm used to encrypt the log info - * cookie value. + * Sets the secret key for the algorithm used to encrypt the log info cookie value. + * + * @param secretKey The secret key for the algorithm used to encrypt the log info cookie value. */ public void setEncryptSecretKey(byte[] secretKey) { this.encryptSecretKey = secretKey; @@ -597,10 +537,8 @@ public void setEncryptSecretKey(byte[] secretKey) { /** * Sets the name of the HTML login form field containing the identifier. - * - * @param loginInputName - * The name of the HTML login form field containing the - * identifier. + * + * @param loginInputName The name of the HTML login form field containing the identifier. */ public void setIdentifierFormName(String loginInputName) { this.identifierFormName = loginInputName; @@ -608,9 +546,8 @@ public void setIdentifierFormName(String loginInputName) { /** * Indicates if the login requests should be intercepted. - * - * @param intercepting - * True if the login requests should be intercepted. + * + * @param intercepting True if the login requests should be intercepted. */ public void setInterceptingLogin(boolean intercepting) { this.interceptingLogin = intercepting; @@ -618,9 +555,8 @@ public void setInterceptingLogin(boolean intercepting) { /** * Indicates if the logout requests should be intercepted. - * - * @param intercepting - * True if the logout requests should be intercepted. + * + * @param intercepting True if the logout requests should be intercepted. */ public void setInterceptingLogout(boolean intercepting) { this.interceptingLogout = intercepting; @@ -628,10 +564,8 @@ public void setInterceptingLogout(boolean intercepting) { /** * Sets the URI path of the HTML login form to use to challenge the user. - * - * @param loginFormPath - * The URI path of the HTML login form to use to challenge the - * user. + * + * @param loginFormPath The URI path of the HTML login form to use to challenge the user. */ public void setLoginFormPath(String loginFormPath) { this.loginFormPath = loginFormPath; @@ -639,9 +573,8 @@ public void setLoginFormPath(String loginFormPath) { /** * Sets the login URI path to intercept. - * - * @param loginPath - * The login URI path to intercept. + * + * @param loginPath The login URI path to intercept. */ public void setLoginPath(String loginPath) { this.loginPath = loginPath; @@ -649,9 +582,8 @@ public void setLoginPath(String loginPath) { /** * Sets the logout URI path to intercept. - * - * @param logoutPath - * The logout URI path to intercept. + * + * @param logoutPath The logout URI path to intercept. */ public void setLogoutPath(String logoutPath) { this.logoutPath = logoutPath; @@ -659,9 +591,8 @@ public void setLogoutPath(String logoutPath) { /** * Sets the maximum age of the log info cookie. - * - * @param timeout - * The maximum age of the log info cookie. + * + * @param timeout The maximum age of the log info cookie. * @see CookieSetting#setMaxAge(int) */ public void setMaxCookieAge(int timeout) { @@ -669,12 +600,11 @@ public void setMaxCookieAge(int timeout) { } /** - * Sets the name of the query parameter containing the URI to redirect the - * browser to after login or logout. - * - * @param redirectQueryName - * The name of the query parameter containing the URI to redirect - * the browser to after login or logout. + * Sets the name of the query parameter containing the URI to redirect the browser to after + * login or logout. + * + * @param redirectQueryName The name of the query parameter containing the URI to redirect the + * browser to after login or logout. */ public void setRedirectQueryName(String redirectQueryName) { this.redirectQueryName = redirectQueryName; @@ -682,12 +612,10 @@ public void setRedirectQueryName(String redirectQueryName) { /** * Sets the name of the HTML login form field containing the secret. - * - * @param passwordInputName - * The name of the HTML login form field containing the secret. + * + * @param passwordInputName The name of the HTML login form field containing the secret. */ public void setSecretFormName(String passwordInputName) { this.secretFormName = passwordInputName; } - } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java index f8c1f410dc..96a4cec08f 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; - import org.restlet.Context; import org.restlet.data.ChallengeRequest; import org.restlet.data.ChallengeScheme; @@ -23,9 +21,9 @@ import org.restlet.security.Verifier; /** - * Authenticator supporting the digest challenge authentication schemes. By - * default, it only knows about the {@link ChallengeScheme#HTTP_DIGEST} scheme. - * + * Authenticator supporting the digest challenge authentication schemes. By default, it only knows + * about the {@link ChallengeScheme#HTTP_DIGEST} scheme. + * * @see DigestVerifier * @see DigestAuthenticator * @author Jerome Louvel @@ -45,41 +43,35 @@ public class DigestAuthenticator extends ChallengeAuthenticator { private volatile String serverKey; /** - * Constructor. Sets the challenge scheme to - * {@link ChallengeScheme#HTTP_DIGEST} and the nonce lifespan to 5 minutes - * by default. - * - * @param context - * The context. - * @param optional - * Indicates if the authentication success is optional. - * @param realm - * The authentication realm. - * @param domainRefs - * The URI references that define the protection domains. - * @param serverKey - * The secret key known only to server. + * Constructor. Sets the challenge scheme to {@link ChallengeScheme#HTTP_DIGEST} and the nonce + * lifespan to 5 minutes by default. + * + * @param context The context. + * @param optional Indicates if the authentication success is optional. + * @param realm The authentication realm. + * @param domainRefs The URI references that define the protection domains. + * @param serverKey The secret key known only to server. */ - public DigestAuthenticator(Context context, boolean optional, String realm, - List domainRefs, String serverKey) { + public DigestAuthenticator( + Context context, + boolean optional, + String realm, + List domainRefs, + String serverKey) { super(context, optional, ChallengeScheme.HTTP_DIGEST, realm); this.domainRefs = domainRefs; this.maxServerNonceAge = DEFAULT_MAX_SERVER_NONCE_AGE; this.serverKey = serverKey; - setVerifier(new org.restlet.ext.crypto.internal.HttpDigestVerifier( - this, null, null)); + setVerifier(new org.restlet.ext.crypto.internal.HttpDigestVerifier(this, null, null)); } /** - * Constructor. By default, it set the "optional" property to 'false' and - * the "domainUris" property to a single '/' URI. - * - * @param context - * The context. - * @param realm - * The authentication realm. - * @param serverKey - * secret key known only to server + * Constructor. By default, it set the "optional" property to 'false' and the "domainUris" + * property to a single '/' URI. + * + * @param context The context. + * @param realm The authentication realm. + * @param serverKey secret key known only to server */ public DigestAuthenticator(Context context, String realm, String serverKey) { this(context, false, realm, null, serverKey); @@ -96,7 +88,7 @@ protected ChallengeRequest createChallengeRequest(boolean stale) { /** * Generates a server nonce. - * + * * @return A new server nonce. */ public String generateServerNonce() { @@ -104,10 +96,9 @@ public String generateServerNonce() { } /** - * Returns the base URI references that collectively define the protected - * domains for the digest authentication. By default, it returns a list with a - * single "/" URI reference. - * + * Returns the base URI references that collectively define the protected domains for the digest + * authentication. By default, it returns a list with a single "/" URI reference. + * * @return The base URI references. */ public List getDomainRefs() { @@ -126,14 +117,11 @@ public List getDomainRefs() { } /** - * Return the hashed secret. By default, it knows how to hash HTTP DIGEST - * secrets, specified as A1 in section 3.2.2.2 of RFC2617, or null if the - * identifier has no corresponding secret. - * - * @param identifier - * The user identifier to hash. - * @param secret - * The user secret. + * Return the hashed secret. By default, it knows how to hash HTTP DIGEST secrets, specified as + * A1 in section 3.2.2.2 of RFC2617, or null if the identifier has no corresponding secret. + * + * @param identifier The user identifier to hash. + * @param secret The user secret. * @return A hash of the user name, realm, and password. */ public String getHashedSecret(String identifier, char[] secret) { @@ -146,7 +134,7 @@ public String getHashedSecret(String identifier, char[] secret) { /** * Returns the number of milliseconds between each mandatory nonce refresh. - * + * * @return The server nonce lifespan. */ public long getMaxServerNonceAge() { @@ -155,7 +143,7 @@ public long getMaxServerNonceAge() { /** * Returns the secret key known only by server. - * + * * @return The server secret key. */ public String getServerKey() { @@ -169,11 +157,9 @@ public DigestVerifier getVerifier() { } /** - * Sets the URI references that define the protection domains for the digest - * authentication. - * - * @param domainRefs - * The base URI references. + * Sets the URI references that define the protection domains for the digest authentication. + * + * @param domainRefs The base URI references. */ public void setDomainRefs(List domainRefs) { this.domainRefs = domainRefs; @@ -181,9 +167,8 @@ public void setDomainRefs(List domainRefs) { /** * Sets the number of milliseconds between each mandatory nonce refresh. - * - * @param maxServerNonceAge - * The nonce lifespan in milliseconds. + * + * @param maxServerNonceAge The nonce lifespan in milliseconds. */ public void setMaxServerNonceAge(long maxServerNonceAge) { this.maxServerNonceAge = maxServerNonceAge; @@ -191,20 +176,18 @@ public void setMaxServerNonceAge(long maxServerNonceAge) { /** * Sets the secret key known only by server. - * - * @param serverKey - * The server secret key. + * + * @param serverKey The server secret key. */ public void setServerKey(String serverKey) { this.serverKey = serverKey; } /** - * Set the internal verifier. In general you shouldn't replace it and - * instead use the {@link #setWrappedVerifier(LocalVerifier)} method. - * - * @param verifier - * The internal verifier. + * Set the internal verifier. In general you shouldn't replace it and instead use the {@link + * #setWrappedVerifier(LocalVerifier)} method. + * + * @param verifier The internal verifier. */ @Override public void setVerifier(Verifier verifier) { @@ -226,13 +209,11 @@ public void setVerifier(Verifier verifier) { } /** - * Sets the digest algorithm of secrets returned by the wrapped verifier. - * The secrets from the wrapped verifier are the ones used by the verifier - * to compare those sent by clients when attempting to authenticate. - * - * @param wrappedAlgorithm - * The digest algorithm of secrets returned by the wrapped - * verifier. + * Sets the digest algorithm of secrets returned by the wrapped verifier. The secrets from the + * wrapped verifier are the ones used by the verifier to compare those sent by clients when + * attempting to authenticate. + * + * @param wrappedAlgorithm The digest algorithm of secrets returned by the wrapped verifier. * @see Digest */ public void setWrappedAlgorithm(String wrappedAlgorithm) { @@ -240,14 +221,12 @@ public void setWrappedAlgorithm(String wrappedAlgorithm) { } /** - * Sets the secret verifier that will be wrapped by real verifier supporting - * all the HTTP DIGEST verifications (nonce, domain URIs, etc.). - * - * @param localVerifier - * The local verifier to wrap. + * Sets the secret verifier that will be wrapped by real verifier supporting all the HTTP DIGEST + * verifications (nonce, domain URIs, etc.). + * + * @param localVerifier The local verifier to wrap. */ public void setWrappedVerifier(LocalVerifier localVerifier) { getVerifier().setWrappedVerifier(localVerifier); } - } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java index 14faff4087..882cb5879a 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; import java.io.UnsupportedEncodingException; @@ -14,36 +13,31 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; - import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; - import org.restlet.data.Digest; /** * Security data manipulation utilities. - * + * * @author Jerome Louvel */ public class DigestUtils { /** - * General regex pattern to extract comma separated name-value components. - * This pattern captures one name and value per match(), and is repeatedly - * applied to the input string to extract all components. Must handle both - * quoted and unquoted values as RFC2617 isn't consistent in this respect. - * Pattern is immutable and thread-safe so reuse one static instance. + * General regex pattern to extract comma separated name-value components. This pattern captures + * one name and value per match(), and is repeatedly applied to the input string to extract all + * components. Must handle both quoted and unquoted values as RFC2617 isn't consistent in this + * respect. Pattern is immutable and thread-safe so reuse one static instance. */ private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray(); /** - * Returns the digest of the target string. Target is decoded to bytes using - * the US-ASCII charset. Supports MD5 and SHA-1 algorithms. - * - * @param target - * The string to encode. - * @param algorithm - * The digest algorithm to use. + * Returns the digest of the target string. Target is decoded to bytes using the US-ASCII + * charset. Supports MD5 and SHA-1 algorithms. + * + * @param target The string to encode. + * @param algorithm The digest algorithm to use. * @return The digest of the target string. */ public static char[] digest(char[] target, String algorithm) { @@ -51,13 +45,11 @@ public static char[] digest(char[] target, String algorithm) { } /** - * Returns the digest of the target string. Target is decoded to bytes using - * the US-ASCII charset. Supports MD5 and SHA-1 algorithms. - * - * @param target - * The string to encode. - * @param algorithm - * The digest algorithm to use. + * Returns the digest of the target string. Target is decoded to bytes using the US-ASCII + * charset. Supports MD5 and SHA-1 algorithms. + * + * @param target The string to encode. + * @param algorithm The digest algorithm to use. * @return The digest of the target string. */ public static String digest(String target, String algorithm) { @@ -68,15 +60,14 @@ public static String digest(String target, String algorithm) { } throw new IllegalArgumentException("Unsupported algorithm."); - }; + } + ; /** * Converts a source string to its HMAC/SHA-1 value. - * - * @param source - * The source string to convert. - * @param secretKey - * The secret key to use for conversion. + * + * @param source The source string to convert. + * @param secretKey The secret key to use for conversion. * @return The HMac value of the source string. */ public static byte[] toHMacSha1(String source, byte[] secretKey) { @@ -94,12 +85,10 @@ public static byte[] toHMacSha1(String source, byte[] secretKey) { result = mac.doFinal(source.getBytes()); } catch (NoSuchAlgorithmException nsae) { throw new RuntimeException( - "Could not find the SHA-1 algorithm. HMac conversion failed.", - nsae); + "Could not find the SHA-1 algorithm. HMac conversion failed.", nsae); } catch (InvalidKeyException ike) { throw new RuntimeException( - "Invalid key exception detected. HMac conversion failed.", - ike); + "Invalid key exception detected. HMac conversion failed.", ike); } return result; @@ -107,11 +96,9 @@ public static byte[] toHMacSha1(String source, byte[] secretKey) { /** * Converts a source string to its HMAC/SHA-1 value. - * - * @param source - * The source string to convert. - * @param secretKey - * The secret key to use for conversion. + * + * @param source The source string to convert. + * @param secretKey The secret key to use for conversion. * @return The HMac value of the source string. */ public static byte[] toHMacSha1(String source, String secretKey) { @@ -120,11 +107,9 @@ public static byte[] toHMacSha1(String source, String secretKey) { /** * Converts a source string to its HMAC/SHA256 value. - * - * @param source - * The source string to convert. - * @param secretKey - * The secret key to use for conversion. + * + * @param source The source string to convert. + * @param secretKey The secret key to use for conversion. * @return The HMac value of the source string. */ public static byte[] toHMacSha256(String source, byte[] secretKey) { @@ -132,8 +117,7 @@ public static byte[] toHMacSha256(String source, byte[] secretKey) { try { // Create the HMAC/SHA256 key - SecretKeySpec signingKey = new SecretKeySpec(secretKey, - "HmacSHA256"); + SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256"); // Create the message authentication code (MAC) Mac mac = Mac.getInstance("HmacSHA256"); @@ -143,19 +127,15 @@ public static byte[] toHMacSha256(String source, byte[] secretKey) { result = mac.doFinal(source.getBytes("UTF-8")); } catch (NoSuchAlgorithmException nsae) { throw new RuntimeException( - "Could not find the SHA256 algorithm. HMac conversion failed.", - nsae); + "Could not find the SHA256 algorithm. HMac conversion failed.", nsae); } catch (InvalidKeyException ike) { throw new RuntimeException( - "Invalid key exception detected. HMac conversion failed.", - ike); + "Invalid key exception detected. HMac conversion failed.", ike); } catch (IllegalStateException ise) { throw new RuntimeException( - "IIllegal state exception detected. HMac conversion failed.", - ise); + "IIllegal state exception detected. HMac conversion failed.", ise); } catch (UnsupportedEncodingException uee) { - throw new RuntimeException( - "Unsuported encoding UTF-8. HMac conversion failed.", uee); + throw new RuntimeException("Unsuported encoding UTF-8. HMac conversion failed.", uee); } return result; @@ -163,11 +143,9 @@ public static byte[] toHMacSha256(String source, byte[] secretKey) { /** * Converts a source string to its HMAC/SHA256 value. - * - * @param source - * The source string to convert. - * @param secretKey - * The secret key to use for conversion. + * + * @param source The source string to convert. + * @param secretKey The secret key to use for conversion. * @return The HMac value of the source string. */ public static byte[] toHMacSha256(String source, String secretKey) { @@ -175,21 +153,16 @@ public static byte[] toHMacSha256(String source, String secretKey) { } /** - * Return the HTTP DIGEST hashed secret. It concatenates the identifier, - * realm and secret, separated by a comma and digest them using MD5. - * - * @param identifier - * The user identifier to hash. - * @param secret - * The user secret. - * @param realm - * The authentication realm. - * @return A hash of the user name, realm, and password, specified as A1 in - * section 3.2.2.2 of RFC2617, or null if the identifier has no - * corresponding secret. + * Return the HTTP DIGEST hashed secret. It concatenates the identifier, realm and secret, + * separated by a comma and digest them using MD5. + * + * @param identifier The user identifier to hash. + * @param secret The user secret. + * @param realm The authentication realm. + * @return A hash of the user name, realm, and password, specified as A1 in section 3.2.2.2 of + * RFC2617, or null if the identifier has no corresponding secret. */ - public static String toHttpDigest(String identifier, char[] secret, - String realm) { + public static String toHttpDigest(String identifier, char[] secret, String realm) { if (secret != null) { return toMd5(identifier + ":" + realm + ":" + new String(secret)); } @@ -198,13 +171,12 @@ public static String toHttpDigest(String identifier, char[] secret, } /** - * Returns the MD5 digest of the target string. Target is decoded to bytes - * using the US-ASCII charset. The returned hexadecimal String always - * contains 32 lowercase alphanumeric characters. For example, if target is - * "HelloWorld", this method returns "68e109f0f40ca72a15e05cc22786f8e6". - * - * @param target - * The string to encode. + * Returns the MD5 digest of the target string. Target is decoded to bytes using the US-ASCII + * charset. The returned hexadecimal String always contains 32 lowercase alphanumeric + * characters. For example, if target is "HelloWorld", this method returns + * "68e109f0f40ca72a15e05cc22786f8e6". + * + * @param target The string to encode. * @return The MD5 digest of the target string. */ public static String toMd5(String target) { @@ -218,24 +190,20 @@ public static String toMd5(String target) { } /** - * Returns the MD5 digest of target string. Target is decoded to bytes using - * the named charset. The returned hexadecimal String always contains 32 - * lowercase alphanumeric characters. For example, if target is - * "HelloWorld", this method returns "68e109f0f40ca72a15e05cc22786f8e6". - * - * @param target - * The string to encode. - * @param charsetName - * The character set. + * Returns the MD5 digest of target string. Target is decoded to bytes using the named charset. + * The returned hexadecimal String always contains 32 lowercase alphanumeric characters. For + * example, if target is "HelloWorld", this method returns "68e109f0f40ca72a15e05cc22786f8e6". + * + * @param target The string to encode. + * @param charsetName The character set. * @return The MD5 digest of the target string. - * * @throws UnsupportedEncodingException */ public static String toMd5(String target, String charsetName) throws UnsupportedEncodingException { try { - final byte[] md5 = MessageDigest.getInstance("MD5").digest( - target.getBytes(charsetName)); + final byte[] md5 = + MessageDigest.getInstance("MD5").digest(target.getBytes(charsetName)); final char[] md5Chars = new char[32]; int i = 0; for (final byte b : md5) { @@ -244,17 +212,15 @@ public static String toMd5(String target, String charsetName) } return new String(md5Chars); } catch (NoSuchAlgorithmException nsae) { - throw new RuntimeException( - "No MD5 algorithm, unable to compute MD5"); + throw new RuntimeException("No MD5 algorithm, unable to compute MD5"); } } /** - * Returns the SHA1 digest of the target string. Target is decoded to bytes - * using the US-ASCII charset. - * - * @param target - * The string to encode. + * Returns the SHA1 digest of the target string. Target is decoded to bytes using the US-ASCII + * charset. + * + * @param target The string to encode. * @return The MD5 digest of the target string. */ public static String toSha1(String target) { @@ -268,34 +234,27 @@ public static String toSha1(String target) { } /** - * Returns the SHA1 digest of target string. Target is decoded to bytes - * using the named charset. - * - * @param target - * The string to encode. - * @param charsetName - * The character set. + * Returns the SHA1 digest of target string. Target is decoded to bytes using the named charset. + * + * @param target The string to encode. + * @param charsetName The character set. * @return The SHA1 digest of the target string. - * * @throws UnsupportedEncodingException */ public static String toSha1(String target, String charsetName) throws UnsupportedEncodingException { try { - return Base64.getEncoder().encodeToString( - MessageDigest.getInstance("SHA1").digest( - target.getBytes(charsetName))); + return Base64.getEncoder() + .encodeToString( + MessageDigest.getInstance("SHA1").digest(target.getBytes(charsetName))); } catch (NoSuchAlgorithmException nsae) { - throw new RuntimeException( - "No SHA1 algorithm, unable to compute SHA1"); + throw new RuntimeException("No SHA1 algorithm, unable to compute SHA1"); } } /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. */ - private DigestUtils() { - } - + private DigestUtils() {} } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java index 2fcac96935..eaac7ae0c1 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java @@ -1,29 +1,26 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.data.Digest; import org.restlet.security.LocalVerifier; import org.restlet.security.SecretVerifier; -import java.util.logging.Level; - /** - * Wrapper verifier that can verify digested secrets. If the provided secret is - * a digest, then the local secret must either be a digest of the same algorithm - * or the wrapped verifier must be a {@link LocalVerifier} returning secrets in - * clear.
+ * Wrapper verifier that can verify digested secrets. If the provided secret is a digest, then the + * local secret must either be a digest of the same algorithm or the wrapped verifier must be a + * {@link LocalVerifier} returning secrets in clear.
*
- * If the provided secret is a regular secret, then the local secret can be in - * any digest algorithm or a regular secret. + * If the provided secret is a regular secret, then the local secret can be in any digest algorithm + * or a regular secret. * * @see Digest * @see DigestAuthenticator @@ -43,34 +40,25 @@ public class DigestVerifier extends SecretVerifier { /** * Constructor. * - * @param algorithm - * The digest algorithm of provided secrets. - * @param wrappedVerifier - * The wrapped secret verifier. - * @param wrappedAlgorithm - * The digest algorithm of secrets provided by the wrapped - * verifier. + * @param algorithm The digest algorithm of provided secrets. + * @param wrappedVerifier The wrapped secret verifier. + * @param wrappedAlgorithm The digest algorithm of secrets provided by the wrapped verifier. * @see Digest */ - public DigestVerifier(String algorithm, T wrappedVerifier, - String wrappedAlgorithm) { + public DigestVerifier(String algorithm, T wrappedVerifier, String wrappedAlgorithm) { this.algorithm = algorithm; this.wrappedAlgorithm = wrappedAlgorithm; this.wrappedVerifier = wrappedVerifier; } /** - * Computes the digest of a secret according to a specified algorithm. By - * default, MD5 hashes (represented as a sequence of 32 hexadecimal digits) - * and SHA-1 hashes are supported. For additional algorithm, override this - * method. + * Computes the digest of a secret according to a specified algorithm. By default, MD5 hashes + * (represented as a sequence of 32 hexadecimal digits) and SHA-1 hashes are supported. For + * additional algorithm, override this method. * - * @param identifier - * The user identifier. - * @param secret - * The regular secret to digest. - * @param algorithm - * The digest algorithm to use. + * @param identifier The user identifier. + * @param secret The regular secret to digest. + * @param algorithm The digest algorithm to use. * @return The digested secret. * @see Digest */ @@ -79,8 +67,8 @@ protected char[] digest(String identifier, char[] secret, String algorithm) { } /** - * Returns the digest algorithm of provided secrets. Provided secrets are - * the ones sent by clients when attempting to authenticate. + * Returns the digest algorithm of provided secrets. Provided secrets are the ones sent by + * clients when attempting to authenticate. * * @return The digest algorithm of input secrets. */ @@ -89,11 +77,10 @@ public String getAlgorithm() { } /** - * Sets the digest algorithm of provided secrets. Provided secrets are the - * ones sent by clients when attempting to authenticate. + * Sets the digest algorithm of provided secrets. Provided secrets are the ones sent by clients + * when attempting to authenticate. * - * @param algorithm - * The digest algorithm of secrets provided by the user. + * @param algorithm The digest algorithm of secrets provided by the user. * @see Digest */ public void setAlgorithm(String algorithm) { @@ -101,9 +88,9 @@ public void setAlgorithm(String algorithm) { } /** - * Returns the digest algorithm of secrets returned by the wrapped verifier. - * The secrets from the wrapped verifier are the ones used by the verifier - * to compare those sent by clients when attempting to authenticate. + * Returns the digest algorithm of secrets returned by the wrapped verifier. The secrets from + * the wrapped verifier are the ones used by the verifier to compare those sent by clients when + * attempting to authenticate. * * @return The digest algorithm of secrets returned by the wrapped verifier. */ @@ -112,13 +99,11 @@ public String getWrappedAlgorithm() { } /** - * Sets the digest algorithm of secrets returned by the wrapped verifier. - * The secrets from the wrapped verifier are the ones used by the verifier - * to compare those sent by clients when attempting to authenticate. + * Sets the digest algorithm of secrets returned by the wrapped verifier. The secrets from the + * wrapped verifier are the ones used by the verifier to compare those sent by clients when + * attempting to authenticate. * - * @param wrappedAlgorithm - * The digest algorithm of secrets returned by the wrapped - * verifier. + * @param wrappedAlgorithm The digest algorithm of secrets returned by the wrapped verifier. * @see Digest */ public void setWrappedAlgorithm(String wrappedAlgorithm) { @@ -126,12 +111,11 @@ public void setWrappedAlgorithm(String wrappedAlgorithm) { } /** - * Returns the wrapped secret associated to a given identifier. This method - * can only be called if the wrapped verifier is a {@link LocalVerifier}. + * Returns the wrapped secret associated with a given identifier. This method can only be called + * if the wrapped verifier is a {@link LocalVerifier}. * - * @param identifier - * The identifier to lookup. - * @return The secret associated to the identifier or null. + * @param identifier The identifier to lookup. + * @return The secret associated with the identifier or null. */ public char[] getWrappedSecret(String identifier) { char[] result = null; @@ -140,7 +124,8 @@ public char[] getWrappedSecret(String identifier) { result = localVerifier.getLocalSecret(identifier); } else { Context.getCurrentLogger() - .log(Level.WARNING, + .log( + Level.WARNING, "The wrapped verifier must be a LocalVerifier to allow digesting of wrapped secrets."); } @@ -148,26 +133,24 @@ public char[] getWrappedSecret(String identifier) { } /** - * Returns the digest of the wrapped secret associated to a given - * identifier. If the wrapped algorithm is null it returns the digest of the - * wrapped secret, otherwise the algorithms must be identical. This method - * can only be called if the wrapped verifier is a {@link LocalVerifier}. + * Returns the digest of the wrapped secret associated with a given identifier. If the wrapped + * algorithm is null it returns the digest of the wrapped secret, otherwise the algorithms must + * be identical. This method can only be called if the wrapped verifier is a {@link + * LocalVerifier}. * - * @param identifier - * The identifier to lookup. - * @return The secret associated to the identifier or null. + * @param identifier The identifier to lookup. + * @return The secret associated with the identifier or null. */ public char[] getWrappedSecretDigest(String identifier) { char[] result = null; if (getWrappedAlgorithm() == null) { - result = digest(identifier, getWrappedSecret(identifier), - getAlgorithm()); + result = digest(identifier, getWrappedSecret(identifier), getAlgorithm()); } else if (getAlgorithm().equals(getWrappedAlgorithm())) { result = getWrappedSecret(identifier); } else { - Context.getCurrentLogger().log(Level.WARNING, - "The digest algorithms can't be different."); + Context.getCurrentLogger() + .log(Level.WARNING, "The digest algorithms can't be different."); } return result; @@ -185,8 +168,7 @@ public T getWrappedVerifier() { /** * Sets the wrapped secret verifier. * - * @param wrappedVerifier - * The wrapped secret verifier. + * @param wrappedVerifier The wrapped secret verifier. */ public void setWrappedVerifier(T wrappedVerifier) { this.wrappedVerifier = wrappedVerifier; @@ -207,15 +189,16 @@ public int verify(String identifier, char[] secret) { result = getWrappedVerifier().verify(identifier, secretDigest); } else { if (getWrappedAlgorithm() == null) { - result = compare(secretDigest, getWrappedSecretDigest(identifier)) - ? RESULT_VALID - : RESULT_INVALID; + result = + compare(secretDigest, getWrappedSecretDigest(identifier)) + ? RESULT_VALID + : RESULT_INVALID; } else if (getAlgorithm().equals(getWrappedAlgorithm())) { result = getWrappedVerifier().verify(identifier, secretDigest); } else { result = RESULT_UNSUPPORTED; - Context.getCurrentLogger().log(Level.WARNING, - "The input and output algorithms can't be different."); + Context.getCurrentLogger() + .log(Level.WARNING, "The input and output algorithms can't be different."); } } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java index 120129c7ac..182895ff1f 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import java.util.Base64; @@ -18,7 +17,6 @@ import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.restlet.Request; import org.restlet.data.Header; import org.restlet.data.Method; @@ -32,25 +30,21 @@ import org.restlet.util.Series; /** - * Provides utility functions for implementing the Amazon S3 Authentication - * scheme. - * + * Provides utility functions for implementing the Amazon S3 Authentication scheme. + * * @author Jean-Philippe Steinmetz - * @see - * Authenticating REST Requests + * @see + * Authenticating REST Requests */ public class AwsUtils { /** * Returns the canonicalized AMZ headers. - * - * @param requestHeaders - * The list of request headers. + * + * @param requestHeaders The list of request headers. * @return The canonicalized AMZ headers. */ - public static String getCanonicalizedAmzHeaders( - Series

requestHeaders) { + public static String getCanonicalizedAmzHeaders(Series
requestHeaders) { StringBuilder sb = new StringBuilder(); Pattern spacePattern = Pattern.compile("\\s+"); @@ -66,8 +60,7 @@ public static String getCanonicalizedAmzHeaders( if (amzHeaders.containsKey(name)) value = amzHeaders.get(name) + "," + header.getValue(); - else - value = header.getValue(); + else value = header.getValue(); // All newlines and multiple spaces must be replaced with a // single space character. @@ -81,8 +74,7 @@ public static String getCanonicalizedAmzHeaders( // Concatenate all AMZ headers for (Entry entry : amzHeaders.entrySet()) { - sb.append(entry.getKey()).append(':').append(entry.getValue()) - .append("\n"); + sb.append(entry.getKey()).append(':').append(entry.getValue()).append("\n"); } return sb.toString(); @@ -90,121 +82,97 @@ public static String getCanonicalizedAmzHeaders( /** * Returns the canonicalized resource name. - * - * @param reference - * The resource reference + * + * @param reference The resource reference * @return The canonicalized resource name. */ public static String getCanonicalizedResourceName(Reference reference) { String hostName = reference.getHostDomain(); String path = reference.getPath(); - Pattern hostNamePattern = Pattern - .compile("s3[a-z0-1\\-]*.amazonaws.com"); + Pattern hostNamePattern = Pattern.compile("s3[a-z0-1\\-]*.amazonaws.com"); StringBuilder sb = new StringBuilder(); // Append the bucket if (hostName != null) { // If the host name contains a port number, remove it - if (hostName.contains(":")) - hostName = hostName.substring(0, hostName.indexOf(":")); + if (hostName.contains(":")) hostName = hostName.substring(0, hostName.indexOf(':')); Matcher hostNameMatcher = hostNamePattern.matcher(hostName); if (hostName.endsWith(".s3.amazonaws.com")) { - String bucketName = hostName.substring(0, - hostName.length() - 17); + String bucketName = hostName.substring(0, hostName.length() - 17); sb.append("/").append(bucketName); } else if (!hostNameMatcher.matches()) { sb.append("/").append(hostName); } } - int queryIdx = path.indexOf("?"); + int queryIdx = path.indexOf('?'); // Append the resource path - if (queryIdx >= 0) - sb.append(path, 0, queryIdx); - else - sb.append(path); + if (queryIdx >= 0) sb.append(path, 0, queryIdx); + else sb.append(path); // Append the AWS sub-resource if (queryIdx >= 0) { String query = path.substring(queryIdx - 1); - if (query.contains("?acl")) - sb.append("?acl"); - else if (query.contains("?location")) - sb.append("?location"); - else if (query.contains("?logging")) - sb.append("?logging"); - else if (query.contains("?torrent")) - sb.append("?torrent"); + if (query.contains("?acl")) sb.append("?acl"); + else if (query.contains("?location")) sb.append("?location"); + else if (query.contains("?logging")) sb.append("?logging"); + else if (query.contains("?torrent")) sb.append("?torrent"); } return sb.toString(); } /** - * Returns the AWS authentication compatible signature for the given string - * to sign and secret. - * - * @param stringToSign - * The string to sign. - * @param secret - * The user secret to sign with + * Returns the AWS authentication compatible signature for the given string to sign and secret. + * + * @param stringToSign The string to sign. + * @param secret The user secret to sign with * @return The AWS compatible signature */ public static String getHmacSha1Signature(String stringToSign, char[] secret) { - return Base64.getEncoder().encodeToString( - DigestUtils.toHMacSha1(stringToSign, - IoUtils.toByteArray(secret))); + return Base64.getEncoder() + .encodeToString(DigestUtils.toHMacSha1(stringToSign, IoUtils.toByteArray(secret))); } /** - * Returns the AWS authentication compatible signature for the given string - * to sign and secret. - * - * @param stringToSign - * The string to sign. - * @param secret - * The user secret to sign with + * Returns the AWS authentication compatible signature for the given string to sign and secret. + * + * @param stringToSign The string to sign. + * @param secret The user secret to sign with * @return The AWS compatible signature */ - public static String getHmacSha256Signature(String stringToSign, - char[] secret) { - return Base64.getEncoder().encodeToString( - DigestUtils.toHMacSha256(stringToSign, - IoUtils.toByteArray(secret))); + public static String getHmacSha256Signature(String stringToSign, char[] secret) { + return Base64.getEncoder() + .encodeToString( + DigestUtils.toHMacSha256(stringToSign, IoUtils.toByteArray(secret))); } /** - * Returns the AWS SimpleDB authentication compatible signature for the - * given request and secret. - * - * @param method - * The request method. - * @param resourceRef - * The target resource reference. - * @param params - * The request parameters. - * @param secret - * The user secret to sign with + * Returns the AWS SimpleDB authentication compatible signature for the given request and + * secret. + * + * @param method The request method. + * @param resourceRef The target resource reference. + * @param params The request parameters. + * @param secret The user secret to sign with * @return The AWS SimpleDB compatible signature */ - public static String getQuerySignature(Method method, - Reference resourceRef, List params, char[] secret) { - return getHmacSha256Signature( - getQueryStringToSign(method, resourceRef, params), secret); + public static String getQuerySignature( + Method method, Reference resourceRef, List params, char[] secret) { + return getHmacSha256Signature(getQueryStringToSign(method, resourceRef, params), secret); } /** * Returns the SimpleDB string to sign. - * - * @param resourceRef - * The target resource reference. + * + * @param resourceRef The target resource reference. * @return The string to sign. */ - public static String getQueryStringToSign(Method method, - Reference resourceRef, List params) { + public static String getQueryStringToSign( + Method method, Reference resourceRef, List params) { StringBuilder toSign = new StringBuilder(); // Append HTTP method @@ -232,8 +200,7 @@ public static String getQueryStringToSign(Method method, toSign.append(Reference.encode(param.getName())); if (param.getValue() != null) { - toSign.append('=').append( - Reference.encode(param.getValue(), true)); + toSign.append('=').append(Reference.encode(param.getValue(), true)); } } @@ -241,86 +208,77 @@ public static String getQueryStringToSign(Method method, } /** - * Returns the AWS S3 authentication compatible signature for the given - * request and secret. - * - * @param request - * The request to create the signature for - * @param secret - * The user secret to sign with + * Returns the AWS S3 authentication compatible signature for the given request and secret. + * + * @param request The request to create the signature for + * @param secret The user secret to sign with * @return The AWS S3 compatible signature */ public static String getS3Signature(Request request, char[] secret) { @SuppressWarnings("unchecked") - Series
headers = (Series
) request.getAttributes().get( - HeaderConstants.ATTRIBUTE_HEADERS); + Series
headers = + (Series
) request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); return getS3Signature(request, headers, secret); } /** - * Returns the AWS S3 authentication compatible signature for the given - * request and secret. - * - * @param request - * The request to create the signature for - * @param headers - * The HTTP headers associated with the request - * @param secret - * The user secret to sign with + * Returns the AWS S3 authentication compatible signature for the given request and secret. + * + * @param request The request to create the signature for + * @param headers The HTTP headers associated with the request + * @param secret The user secret to sign with * @return The AWS S3 compatible signature */ - public static String getS3Signature(Request request, - Series
headers, char[] secret) { + public static String getS3Signature(Request request, Series
headers, char[] secret) { return getHmacSha1Signature(getS3StringToSign(request, headers), secret); } /** * Returns the string to sign. - * - * @param request - * The request to generate the signature string from + * + * @param request The request to generate the signature string from * @return The string to sign */ public static String getS3StringToSign(Request request) { @SuppressWarnings("unchecked") - Series
headers = (Series
) request.getAttributes().get( - HeaderConstants.ATTRIBUTE_HEADERS); + Series
headers = + (Series
) request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); return getS3StringToSign(request, headers); } /** * Returns the S3 string to sign. - * - * @param request - * The request to generate the signature string from - * @param headers - * The HTTP headers associated with the request + * + * @param request The request to generate the signature string from + * @param headers The HTTP headers associated with the request * @return The string to sign */ - public static String getS3StringToSign(Request request, - Series
headers) { + public static String getS3StringToSign(Request request, Series
headers) { String canonicalizedAmzHeaders = getCanonicalizedAmzHeaders(headers); - String canonicalizedResource = getCanonicalizedResourceName(request - .getResourceRef()); - String contentMD5 = (headers == null) ? null : headers.getFirstValue( - HeaderConstants.HEADER_CONTENT_MD5, true); - String contentType = (headers == null) ? null : headers.getFirstValue( - HeaderConstants.HEADER_CONTENT_TYPE, true); - String date = (headers == null) ? null : headers.getFirstValue( - "X-Amz-Date", true); + String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); + String contentMD5 = + (headers == null) + ? null + : headers.getFirstValue(HeaderConstants.HEADER_CONTENT_MD5, true); + String contentType = + (headers == null) + ? null + : headers.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE, true); + String date = (headers == null) ? null : headers.getFirstValue("X-Amz-Date", true); String method = request.getMethod().getName(); // If amazon's date header wasn't found, try to grab the regular date // header if (date == null || (date.isEmpty())) { - date = (headers == null) ? null : headers.getFirstValue( - HeaderConstants.HEADER_DATE, true); + date = + (headers == null) + ? null + : headers.getFirstValue(HeaderConstants.HEADER_DATE, true); } // If no date header exists, make one if (date == null || (date.isEmpty())) { - date = DateUtils.format(new Date(), - DateUtils.FORMAT_RFC_1123.get(0)); + date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.get(0)); if (headers != null) { headers.add(HeaderConstants.HEADER_DATE, date); } @@ -331,12 +289,9 @@ public static String getS3StringToSign(Request request, // This patch seems to apply to Sun JVM only. final String jvmVendor = System.getProperty("java.vm.vendor"); - if ((jvmVendor != null) - && (jvmVendor.toLowerCase()).startsWith("sun")) { - final int majorVersionNumber = SystemUtils - .getJavaMajorVersion(); - final int minorVersionNumber = SystemUtils - .getJavaMinorVersion(); + if ((jvmVendor != null) && (jvmVendor.toLowerCase()).startsWith("sun")) { + final int majorVersionNumber = SystemUtils.getJavaMajorVersion(); + final int minorVersionNumber = SystemUtils.getJavaMinorVersion(); if (majorVersionNumber == 1) { if (minorVersionNumber < 5) { @@ -357,10 +312,8 @@ public static String getS3StringToSign(Request request, toSign.append(contentMD5 != null ? contentMD5 : "").append("\n"); toSign.append(contentType != null ? contentType : "").append("\n"); toSign.append(date != null ? date : "").append("\n"); - toSign.append(canonicalizedAmzHeaders != null ? canonicalizedAmzHeaders - : ""); - toSign.append(canonicalizedResource != null ? canonicalizedResource - : ""); + toSign.append(canonicalizedAmzHeaders != null ? canonicalizedAmzHeaders : ""); + toSign.append(canonicalizedResource != null ? canonicalizedResource : ""); return toSign.toString(); } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java index 9a598d797e..859620d4f8 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import org.restlet.Request; @@ -20,31 +19,23 @@ import org.restlet.util.Series; /** - * Wrapped verifier that can verify HTTP requests using the Amazon S3 - * authentication scheme. Verifies the user by computing the request signature - * using the local secret and comparing it to the signature provided in the - * request. - *

- * Per the Amazon S3 specification the {@code Date} header is required. If the - * {@code Date} header is missing or the request is older than the allowed time - * limit, specified by the {@code maxRequestAge} property, the request fails - * verification. - * + * Wrapped verifier that can verify HTTP requests using the Amazon S3 authentication scheme. + * Verifies the user by computing the request signature using the local secret and comparing it to + * the signature provided in the request. + * + *

Per the Amazon S3 specification the {@code Date} header is required. If the {@code Date} + * header is missing or the request is older than the allowed time limit, specified by the {@code + * maxRequestAge} property, the request fails verification. + * * @author Jean-Philippe Steinmetz - * @see - * Authenticating REST Requests + * @see + * Authenticating REST Requests */ public class AwsVerifier extends SecretVerifier { - /** - * Default maximum request age (15 minutes) - */ + /** Default maximum request age (15 minutes) */ private static final long DEFAULT_MAX_REQUEST_AGE = 15 * 60 * 1000L; - /** - * The maximum age of a request, in milliseconds, before it is considered - * stale. - */ + /** The maximum age of a request, in milliseconds, before it is considered stale. */ private long maxRequestAge; /** The local secret verifier. */ @@ -52,10 +43,8 @@ public class AwsVerifier extends SecretVerifier { /** * Creates a new HttpAwsS3Verifier instance. - * - * @param wrappedVerifier - * The wrapped verifier containing local identifier/secret - * couples + * + * @param wrappedVerifier The wrapped verifier containing local identifier/secret couples */ public AwsVerifier(LocalVerifier wrappedVerifier) { this(wrappedVerifier, DEFAULT_MAX_REQUEST_AGE); @@ -63,13 +52,10 @@ public AwsVerifier(LocalVerifier wrappedVerifier) { /** * Creates a new HttpAwsS3Verifier instance. - * - * @param wrappedVerifier - * The wrapped verifier containing local identifier/secret - * couples - * @param maxRequestAge - * The maximum age of a request, in milliseconds, before it is - * considered stale + * + * @param wrappedVerifier The wrapped verifier containing local identifier/secret couples + * @param maxRequestAge The maximum age of a request, in milliseconds, before it is considered + * stale */ public AwsVerifier(LocalVerifier wrappedVerifier, long maxRequestAge) { super(); @@ -78,34 +64,27 @@ public AwsVerifier(LocalVerifier wrappedVerifier, long maxRequestAge) { } /** - * Returns the user identifier portion of an Amazon S3 compatible - * {@code Authorization} header. - *

- * An Amazon S3 compatible {@code Authorization} header has the following - * pattern.
+ * Returns the user identifier portion of an Amazon S3 compatible {@code Authorization} header. + * + *

An Amazon S3 compatible {@code Authorization} header has the following pattern.
* {@code Authorization: AWS id:signature} */ @Override protected String getIdentifier(Request request, Response response) { if (request.getChallengeResponse() == null - || request.getChallengeResponse().getRawValue() == null) - return null; + || request.getChallengeResponse().getRawValue() == null) return null; - String[] parts = request.getChallengeResponse().getRawValue() - .split(":"); + String[] parts = request.getChallengeResponse().getRawValue().split(":"); - if (parts != null && parts.length == 2) - return parts[0]; - else - return null; + if (parts != null && parts.length == 2) return parts[0]; + else return null; } /** - * Returns the local secret associated to a given identifier. - * - * @param identifier - * The identifier to lookup. - * @return The secret associated to the identifier or null. + * Returns the local secret associated with a given identifier. + * + * @param identifier The identifier to lookup. + * @return The secret associated with the identifier or null. */ public char[] getLocalSecret(String identifier) { char[] result = null; @@ -114,42 +93,34 @@ public char[] getLocalSecret(String identifier) { } /** - * Returns the maximum age of a request, in milliseconds, before it is - * considered stale. - *

- * A negative or zero value indicates no age restriction. The default value - * is 15 minutes. + * Returns the maximum age of a request, in milliseconds, before it is considered stale. + * + *

A negative or zero value indicates no age restriction. The default value is 15 minutes. */ public long getMaxRequestAge() { return this.maxRequestAge; } /** - * Returns the signature portion of an Amazon S3 compatible - * {@code Authorization} header. - *

- * An Amazon S3 compatible {@code Authorization} header has the following - * pattern.
+ * Returns the signature portion of an Amazon S3 compatible {@code Authorization} header. + * + *

An Amazon S3 compatible {@code Authorization} header has the following pattern.
* {@code Authorization: AWS id:signature} */ @Override protected char[] getSecret(Request request, Response response) { if (request.getChallengeResponse() == null - || request.getChallengeResponse().getRawValue() == null) - return null; + || request.getChallengeResponse().getRawValue() == null) return null; - String[] parts = request.getChallengeResponse().getRawValue() - .split(":"); + String[] parts = request.getChallengeResponse().getRawValue().split(":"); - if (parts != null && parts.length == 2) - return parts[1].toCharArray(); - else - return null; + if (parts != null && parts.length == 2) return parts[1].toCharArray(); + else return null; } /** * Returns the wrapped local secret verifier. - * + * * @return The local secret verifier. */ public LocalVerifier getWrappedVerifier() { @@ -157,23 +128,19 @@ public LocalVerifier getWrappedVerifier() { } /** - * Sets the maximum age of a request, in milliseconds, before it is - * considered stale. - *

- * A negative or zero value indicates no age restriction. The default value - * is 15 minutes. + * Sets the maximum age of a request, in milliseconds, before it is considered stale. + * + *

A negative or zero value indicates no age restriction. The default value is 15 minutes. */ public void setMaxRequestAge(long value) { - if (value < 0) - value = 0; + if (value < 0) value = 0; this.maxRequestAge = value; } /** * Sets the wrapped local secret verifier. - * - * @param wrappedVerifier - * The local secret verifier. + * + * @param wrappedVerifier The local secret verifier. */ public void setWrappedVerifier(LocalVerifier wrappedVerifier) { this.wrappedVerifier = wrappedVerifier; @@ -181,37 +148,32 @@ public void setWrappedVerifier(LocalVerifier wrappedVerifier) { @Override public int verify(Request request, Response response) { - if (request.getChallengeResponse() == null) - return RESULT_MISSING; + if (request.getChallengeResponse() == null) return RESULT_MISSING; @SuppressWarnings("unchecked") - Series

headers = (Series
) request.getAttributes().get( - HeaderConstants.ATTRIBUTE_HEADERS); + Series
headers = + (Series
) request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); String userId = getIdentifier(request, response); - if (userId == null || (userId.length() == 0)) - return RESULT_MISSING; + if (userId == null || (userId.isEmpty())) return RESULT_MISSING; // A date header is always required - if (headers.getFirstValue(HeaderConstants.HEADER_DATE, true) == null) - return RESULT_INVALID; + if (headers.getFirstValue(HeaderConstants.HEADER_DATE, true) == null) return RESULT_INVALID; // Make sure the date is not stale if (getMaxRequestAge() > 0) { - Long date = DateUtils.parse( - headers.getFirstValue(HeaderConstants.HEADER_DATE, true)) - .getTime(); + Long date = + DateUtils.parse(headers.getFirstValue(HeaderConstants.HEADER_DATE, true)) + .getTime(); Long now = System.currentTimeMillis(); - if (now - date > getMaxRequestAge()) - return RESULT_STALE; + if (now - date > getMaxRequestAge()) return RESULT_STALE; } char[] userSecret = getLocalSecret(userId); char[] signature = getSecret(request, response); String sigToCompare = AwsUtils.getS3Signature(request, userSecret); - if (!compare(signature, sigToCompare.toCharArray())) - return RESULT_INVALID; + if (!compare(signature, sigToCompare.toCharArray())) return RESULT_INVALID; request.getClientInfo().setUser(new User(userId)); @@ -219,13 +181,11 @@ public int verify(Request request, Response response) { } /** - * This function is not implemented because the authorization scheme - * requires direct access to the request. See - * {@link #verify(Request, Response)}. + * This function is not implemented because the authorization scheme requires direct access to + * the request. See {@link #verify(Request, Response)}. */ @Override - public int verify(String identifier, char[] secret) - throws IllegalArgumentException { + public int verify(String identifier, char[] secret) throws IllegalArgumentException { throw new RuntimeException("Method not implemented"); } } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java index 5e548499f7..67451b910c 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java @@ -1,26 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.util.Base64; - import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; - import org.restlet.ext.crypto.DigestUtils; /** * Simple usage of standard cipher features from JRE. - * + * * @author Remi Dewitte * @author Jerome Louvel */ @@ -28,19 +25,16 @@ public final class CryptoUtils { /** * Creates a cipher for a given algorithm and secret. - * - * @param algorithm - * The cryptographic algorithm. - * @param secretKey - * The cryptographic secret. - * @param mode - * The cipher mode, either {@link Cipher#ENCRYPT_MODE} or - * {@link Cipher#DECRYPT_MODE}. + * + * @param algorithm The cryptographic algorithm. + * @param secretKey The cryptographic secret. + * @param mode The cipher mode, either {@link Cipher#ENCRYPT_MODE} or {@link + * Cipher#DECRYPT_MODE}. * @return The new cipher. * @throws GeneralSecurityException */ - private static Cipher createCipher(String algorithm, byte[] secretKey, - int mode) throws GeneralSecurityException { + private static Cipher createCipher(String algorithm, byte[] secretKey, int mode) + throws GeneralSecurityException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(mode, new SecretKeySpec(secretKey, algorithm)); return cipher; @@ -48,117 +42,99 @@ private static Cipher createCipher(String algorithm, byte[] secretKey, /** * Decrypts a bytes array. - * - * @param algo - * The cryptographic algorithm. - * @param secretKey - * The cryptographic secret key. - * @param encrypted - * The encrypted bytes. + * + * @param algo The cryptographic algorithm. + * @param secretKey The cryptographic secret key. + * @param encrypted The encrypted bytes. * @return The decrypted content string. * @throws GeneralSecurityException */ public static String decrypt(String algo, byte[] secretKey, byte[] encrypted) throws GeneralSecurityException { - byte[] original = doFinal(algo, secretKey, Cipher.DECRYPT_MODE, - encrypted); + byte[] original = doFinal(algo, secretKey, Cipher.DECRYPT_MODE, encrypted); return new String(original, Charset.forName("UTF-8")); } /** * Decrypts a bytes array. - * - * @param algo - * The cryptographic algorithm. - * @param base64Secret - * The cryptographic secret key, encoded as a Base64 string. - * @param encrypted - * The encrypted bytes. + * + * @param algo The cryptographic algorithm. + * @param base64Secret The cryptographic secret key, encoded as a Base64 string. + * @param encrypted The encrypted bytes. * @return The decrypted content string. * @throws GeneralSecurityException */ - public static String decrypt(String algo, String base64Secret, - byte[] encrypted) throws GeneralSecurityException { + public static String decrypt(String algo, String base64Secret, byte[] encrypted) + throws GeneralSecurityException { return decrypt(algo, Base64.getDecoder().decode(base64Secret), encrypted); } /** * Does final processing. - * - * @param algo - * The cryptographic algorithm. - * @param secretKey - * The cryptographic secret key. - * @param mode - * The processing mode, either {@link Cipher#DECRYPT_MODE} or - * {@link Cipher#ENCRYPT_MODE}. - * @param what - * The byte array to process. + * + * @param algo The cryptographic algorithm. + * @param secretKey The cryptographic secret key. + * @param mode The processing mode, either {@link Cipher#DECRYPT_MODE} or {@link + * Cipher#ENCRYPT_MODE}. + * @param what The byte array to process. * @return The processed byte array. * @throws GeneralSecurityException */ - private static byte[] doFinal(String algo, byte[] secretKey, int mode, - byte[] what) throws GeneralSecurityException { + private static byte[] doFinal(String algo, byte[] secretKey, int mode, byte[] what) + throws GeneralSecurityException { return createCipher(algo, secretKey, mode).doFinal(what); } /** * Encrypts a content string. - * - * @param algo - * The cryptographic algorithm. - * @param secretKey - * The cryptographic secret key. - * @param content - * The content string to encrypt. + * + * @param algo The cryptographic algorithm. + * @param secretKey The cryptographic secret key. + * @param content The content string to encrypt. * @return The encrypted bytes. * @throws GeneralSecurityException */ public static byte[] encrypt(String algo, byte[] secretKey, String content) throws GeneralSecurityException { - return doFinal(algo, secretKey, Cipher.ENCRYPT_MODE, content.getBytes(Charset.forName("UTF-8"))); + return doFinal( + algo, secretKey, Cipher.ENCRYPT_MODE, content.getBytes(Charset.forName("UTF-8"))); } /** * Encrypts a content string. - * - * @param algo - * The cryptographic algorithm. - * @param base64Secret - * The cryptographic secret, encoded as a Base64 string. - * @param content - * The content string to encrypt. + * + * @param algo The cryptographic algorithm. + * @param base64Secret The cryptographic secret, encoded as a Base64 string. + * @param content The content string to encrypt. * @return The encrypted bytes. * @throws GeneralSecurityException */ - public static byte[] encrypt(String algo, String base64Secret, - String content) throws GeneralSecurityException { + public static byte[] encrypt(String algo, String base64Secret, String content) + throws GeneralSecurityException { return encrypt(algo, Base64.getDecoder().decode(base64Secret), content); } /** - * Generates a nonce as recommended in section 3.2.1 of RFC-2617, but - * without the ETag field. The format is:

+     * Generates a nonce as recommended in section 3.2.1 of RFC-2617, but without the ETag field.
+     * The format is: 
      * Base64.encodeBytes(currentTimeMS + ":"
      *         + md5String(currentTimeMS + ":" + secretKey))
      * 
- * - * @param secretKey - * a secret value known only to the creator of the nonce. It's - * inserted into the nonce, and can be used later to validate the - * nonce. + * + * @param secretKey a secret value known only to the creator of the nonce. It's inserted into + * the nonce, and can be used later to validate the nonce. */ public static String makeNonce(String secretKey) { final long currentTimeMS = System.currentTimeMillis(); - return Base64.getEncoder().encodeToString( - (currentTimeMS + ":" + DigestUtils.toMd5(currentTimeMS + ":" - + secretKey)).getBytes()); + return Base64.getEncoder() + .encodeToString( + (currentTimeMS + ":" + DigestUtils.toMd5(currentTimeMS + ":" + secretKey)) + .getBytes()); } /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. */ - private CryptoUtils() { - } + private CryptoUtils() {} } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java index 3f5f5ccf4e..bbaf095d79 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java @@ -1,16 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import java.util.Date; - import org.restlet.Request; import org.restlet.data.ChallengeResponse; import org.restlet.data.ChallengeScheme; @@ -21,21 +19,19 @@ /** * Implements the HTTP authentication for the Amazon Web Services. - * + * * @author Jerome Louvel */ public class HttpAwsQueryHelper extends AuthenticatorHelper { - /** - * Constructor. - */ + /** Constructor. */ public HttpAwsQueryHelper() { super(ChallengeScheme.HTTP_AWS_QUERY, true, false); } @Override - public Reference updateReference(Reference resourceRef, - ChallengeResponse challengeResponse, Request request) { + public Reference updateReference( + Reference resourceRef, ChallengeResponse challengeResponse, Request request) { Reference result = resourceRef; Form query = result.getQueryAsForm(); @@ -48,9 +44,12 @@ public Reference updateReference(Reference resourceRef, query.add("Timestamp", df); // Compute then add the signature parameter - String signature = AwsUtils.getQuerySignature(request.getMethod(), - request.getResourceRef(), query, request - .getChallengeResponse().getSecret()); + String signature = + AwsUtils.getQuerySignature( + request.getMethod(), + request.getResourceRef(), + query, + request.getChallengeResponse().getSecret()); query.add("Signature", signature); result = new Reference(resourceRef); result.setQuery(query.getQueryString()); diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java index 23be92e432..a9e66ffac6 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import org.restlet.Request; @@ -19,26 +18,25 @@ /** * Implements the HTTP authentication for the Amazon S3 service. - * + * * @author Jerome Louvel */ public class HttpAwsS3Helper extends AuthenticatorHelper { - /** - * Constructor. - */ + /** Constructor. */ public HttpAwsS3Helper() { super(ChallengeScheme.HTTP_AWS_S3, true, true); } @Override - public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, - Request request, Series

httpHeaders) { + public void formatResponse( + ChallengeWriter cw, + ChallengeResponse challenge, + Request request, + Series
httpHeaders) { // Append the AWS credentials cw.append(challenge.getIdentifier()) .append(':') - .append(AwsUtils.getS3Signature(request, httpHeaders, - challenge.getSecret())); + .append(AwsUtils.getS3Signature(request, httpHeaders, challenge.getSecret())); } - } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java index 63635b8667..333ffb2bd2 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import java.util.Base64; @@ -14,7 +13,6 @@ import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; - import org.restlet.Request; import org.restlet.data.ChallengeResponse; import org.restlet.data.ChallengeScheme; @@ -33,25 +31,23 @@ import org.restlet.util.Series; /** - * Implements the Shared Key authentication for Azure services. This concerns - * Blob and Queues on Azure Storage.
+ * Implements the Shared Key authentication for Azure services. This concerns Blob and Queues on + * Azure Storage.
*
* More documentation is available here - * + * * @author Thierry Boileau */ public class HttpAzureSharedKeyHelper extends AuthenticatorHelper { /** * Returns the canonicalized Azure headers. - * - * @param requestHeaders - * The list of request headers. + * + * @param requestHeaders The list of request headers. * @return The canonicalized Azure headers. */ - private static String getCanonicalizedAzureHeaders( - Series

requestHeaders) { + private static String getCanonicalizedAzureHeaders(Series
requestHeaders) { // Filter out all the Azure headers required for SharedKey // authentication SortedMap azureHeaders = new TreeMap(); @@ -62,8 +58,7 @@ private static String getCanonicalizedAzureHeaders( if (headerName.startsWith("x-ms-")) { if (!azureHeaders.containsKey(headerName)) { - azureHeaders.put(headerName, - requestHeaders.getValues(headerName)); + azureHeaders.put(headerName, requestHeaders.getValues(headerName)); } } } @@ -71,11 +66,9 @@ private static String getCanonicalizedAzureHeaders( // Concatenate all Azure headers StringBuilder sb = new StringBuilder(); - for (Iterator iterator = azureHeaders.keySet().iterator(); iterator - .hasNext();) { + for (Iterator iterator = azureHeaders.keySet().iterator(); iterator.hasNext(); ) { String key = iterator.next(); - sb.append(key).append(':').append(azureHeaders.get(key)) - .append("\n"); + sb.append(key).append(':').append(azureHeaders.get(key)).append("\n"); } return sb.toString(); @@ -83,9 +76,8 @@ private static String getCanonicalizedAzureHeaders( /** * Returns the canonicalized resource name. - * - * @param resourceRef - * The resource reference. + * + * @param resourceRef The resource reference. * @return The canonicalized resource name. */ private static String getCanonicalizedResourceName(Reference resourceRef) { @@ -94,23 +86,23 @@ private static String getCanonicalizedResourceName(Reference resourceRef) { if (param != null) { StringBuilder sb = new StringBuilder(resourceRef.getPath()); - return sb.append("?").append("comp=").append(param.getValue()) - .toString(); + return sb.append("?").append("comp=").append(param.getValue()).toString(); } return resourceRef.getPath(); } - /** - * Constructor. - */ + /** Constructor. */ public HttpAzureSharedKeyHelper() { super(ChallengeScheme.HTTP_AZURE_SHAREDKEY, true, false); } @Override - public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, - Request request, Series
httpHeaders) { + public void formatResponse( + ChallengeWriter cw, + ChallengeResponse challenge, + Request request, + Series
httpHeaders) { // Setup the method name final String methodName = request.getMethod().getName(); @@ -123,32 +115,26 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, date = httpHeaders.getFirstValue(HeaderConstants.HEADER_DATE, true); if (date == null) { // Add a fresh Date header - date = DateUtils.format(new Date(), - DateUtils.FORMAT_RFC_1123.get(0)); + date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.get(0)); httpHeaders.add(HeaderConstants.HEADER_DATE, date); } } // Setup the ContentType header - String contentMd5 = httpHeaders.getFirstValue( - HeaderConstants.HEADER_CONTENT_MD5, true); + String contentMd5 = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_MD5, true); if (contentMd5 == null) { contentMd5 = ""; } // Setup the ContentType header - String contentType = httpHeaders.getFirstValue( - HeaderConstants.HEADER_CONTENT_TYPE, true); + String contentType = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE, true); if (contentType == null) { boolean applyPatch = false; // This patch seems to apply to Sun JVM only. final String jvmVendor = System.getProperty("java.vm.vendor"); - if ((jvmVendor != null) - && (jvmVendor.toLowerCase()).startsWith("sun")) { - final int majorVersionNumber = SystemUtils - .getJavaMajorVersion(); - final int minorVersionNumber = SystemUtils - .getJavaMinorVersion(); + if ((jvmVendor != null) && (jvmVendor.toLowerCase()).startsWith("sun")) { + final int majorVersionNumber = SystemUtils.getJavaMajorVersion(); + final int minorVersionNumber = SystemUtils.getJavaMinorVersion(); if (majorVersionNumber == 1) { if (minorVersionNumber < 5) { @@ -171,22 +157,34 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, final String canonicalizedAzureHeaders = getCanonicalizedAzureHeaders(httpHeaders); // Setup the canonicalized path - final String canonicalizedResource = getCanonicalizedResourceName(request - .getResourceRef()); + final String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); // Setup the message part final StringBuilder rest = new StringBuilder(); - rest.append(methodName).append('\n').append(contentMd5).append('\n') - .append(contentType).append('\n').append(date).append('\n') - .append(canonicalizedAzureHeaders).append('/') + rest.append(methodName) + .append('\n') + .append(contentMd5) + .append('\n') + .append(contentType) + .append('\n') + .append(date) + .append('\n') + .append(canonicalizedAzureHeaders) + .append('/') .append(challenge.getIdentifier()) .append(canonicalizedResource); // Append the SharedKey credentials cw.append(challenge.getIdentifier()) .append(':') - .append(Base64.getEncoder().encodeToString( - DigestUtils.toHMacSha256(rest.toString(), - Base64.getDecoder().decode(IoUtils.toByteArray(challenge.getSecret()))))); + .append( + Base64.getEncoder() + .encodeToString( + DigestUtils.toHMacSha256( + rest.toString(), + Base64.getDecoder() + .decode( + IoUtils.toByteArray( + challenge.getSecret()))))); } } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java index 12d87aadbf..835d39cca7 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import java.util.Base64; import java.util.Date; - import org.restlet.Request; import org.restlet.data.ChallengeResponse; import org.restlet.data.ChallengeScheme; @@ -28,21 +26,20 @@ import org.restlet.util.Series; /** - * Implements the Shared Key Lite authentication for Azure services. This - * concerns Table storage on Azure Storage.
+ * Implements the Shared Key Lite authentication for Azure services. This concerns Table storage on + * Azure Storage.
*
* More documentation is available here - * + * * @author Thierry Boileau */ public class HttpAzureSharedKeyLiteHelper extends AuthenticatorHelper { /** * Returns the canonicalized resource name. - * - * @param resourceRef - * The resource reference. + * + * @param resourceRef The resource reference. * @return The canonicalized resource name. */ private static String getCanonicalizedResourceName(Reference resourceRef) { @@ -51,23 +48,23 @@ private static String getCanonicalizedResourceName(Reference resourceRef) { if (param != null) { StringBuilder sb = new StringBuilder(resourceRef.getPath()); - return sb.append("?").append("comp=").append(param.getValue()) - .toString(); + return sb.append("?").append("comp=").append(param.getValue()).toString(); } return resourceRef.getPath(); } - /** - * Constructor. - */ + /** Constructor. */ public HttpAzureSharedKeyLiteHelper() { super(ChallengeScheme.HTTP_AZURE_SHAREDKEY_LITE, true, false); } @Override - public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, - Request request, Series

httpHeaders) { + public void formatResponse( + ChallengeWriter cw, + ChallengeResponse challenge, + Request request, + Series
httpHeaders) { // Setup the Date header String date = ""; @@ -78,8 +75,7 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, if (date == null) { // Add a fresh Date header - date = DateUtils.format(new Date(), - DateUtils.FORMAT_RFC_1123.get(0)); + date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.get(0)); httpHeaders.add(HeaderConstants.HEADER_DATE, date); } } else { @@ -87,20 +83,27 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, } // Setup the canonicalized path - String canonicalizedResource = getCanonicalizedResourceName(request - .getResourceRef()); + String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); // Setup the message part StringBuilder rest = new StringBuilder(); - rest.append(date).append('\n').append('/') + rest.append(date) + .append('\n') + .append('/') .append(challenge.getIdentifier()) .append(canonicalizedResource); // Append the SharedKey credentials cw.append(challenge.getIdentifier()) .append(':') - .append(Base64.getEncoder().encodeToString( - DigestUtils.toHMacSha256(rest.toString(), - Base64.getDecoder().decode(IoUtils.toByteArray(challenge.getSecret()))))); + .append( + Base64.getEncoder() + .encodeToString( + DigestUtils.toHMacSha256( + rest.toString(), + Base64.getDecoder() + .decode( + IoUtils.toByteArray( + challenge.getSecret()))))); } } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java index 4a79b533c5..94fb549138 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java @@ -1,18 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import java.io.IOException; import java.util.Base64; import java.util.logging.Level; - import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -33,38 +31,30 @@ /** * Implements the HTTP DIGEST authentication. - * + * * @author Jerome Louvel */ public class HttpDigestHelper extends AuthenticatorHelper { /** - * Checks whether the specified nonce is valid with respect to the specified - * secretKey, and further confirms that the nonce was generated less than - * lifespanMillis milliseconds ago - * - * @param nonce - * The nonce value. - * @param secretKey - * The same secret value that was inserted into the nonce when it - * was generated - * @param lifespan - * The nonce lifespan in milliseconds. - * @return True if the nonce was generated less than lifespan milliseconds - * ago, false otherwise. - * @throws Exception - * If the nonce does not match the specified secretKey, or if it - * can't be parsed + * Checks whether the specified nonce is valid with respect to the specified secretKey, and + * further confirms that the nonce was generated less than lifespanMillis milliseconds ago + * + * @param nonce The nonce value. + * @param secretKey The same secret value that was inserted into the nonce when it was generated + * @param lifespan The nonce lifespan in milliseconds. + * @return True if the nonce was generated less than lifespan milliseconds ago, false otherwise. + * @throws Exception If the nonce does not match the specified secretKey, or if it can't be + * parsed */ - public static boolean isNonceValid(String nonce, String secretKey, - long lifespan) throws Exception { + public static boolean isNonceValid(String nonce, String secretKey, long lifespan) + throws Exception { try { String decodedNonce = new String(Base64.getDecoder().decode(nonce)); - long nonceTimeMS = Long.parseLong(decodedNonce.substring(0, - decodedNonce.indexOf(':'))); + long nonceTimeMS = Long.parseLong(decodedNonce.substring(0, decodedNonce.indexOf(':'))); - if (decodedNonce.equals(nonceTimeMS + ":" - + DigestUtils.toMd5(nonceTimeMS + ":" + secretKey))) { + if (decodedNonce.equals( + nonceTimeMS + ":" + DigestUtils.toMd5(nonceTimeMS + ":" + secretKey))) { // Valid with regard to the secretKey, now check lifespan return lifespan > (System.currentTimeMillis() - nonceTimeMS); } @@ -75,16 +65,18 @@ public static boolean isNonceValid(String nonce, String secretKey, throw new Exception("The nonce does not match secretKey"); } - /** - * Constructor. - */ + /** Constructor. */ public HttpDigestHelper() { super(ChallengeScheme.HTTP_DIGEST, true, true); } @Override - public void formatRequest(ChallengeWriter cw, ChallengeRequest challenge, - Response response, Series

httpHeaders) throws IOException { + public void formatRequest( + ChallengeWriter cw, + ChallengeRequest challenge, + Response response, + Series
httpHeaders) + throws IOException { if (challenge.getRealm() != null) { cw.appendQuotedChallengeParameter("realm", challenge.getRealm()); @@ -109,8 +101,7 @@ public void formatRequest(ChallengeWriter cw, ChallengeRequest challenge, } if (challenge.getServerNonce() != null) { - cw.appendQuotedChallengeParameter("nonce", - challenge.getServerNonce()); + cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce()); } if (challenge.getOpaque() != null) { @@ -122,8 +113,7 @@ public void formatRequest(ChallengeWriter cw, ChallengeRequest challenge, } if (challenge.getDigestAlgorithm() != null) { - cw.appendChallengeParameter("algorithm", - challenge.getDigestAlgorithm()); + cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm()); } if (!challenge.getQualityOptions().isEmpty()) { @@ -150,12 +140,14 @@ public void formatRequest(ChallengeWriter cw, ChallengeRequest challenge, } @Override - public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, - Request request, Series
httpHeaders) { + public void formatResponse( + ChallengeWriter cw, + ChallengeResponse challenge, + Request request, + Series
httpHeaders) { if (challenge.getIdentifier() != null) { - cw.appendQuotedChallengeParameter("username", - challenge.getIdentifier()); + cw.appendQuotedChallengeParameter("username", challenge.getIdentifier()); } if (challenge.getRealm() != null) { @@ -163,33 +155,27 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, } if (challenge.getServerNonce() != null) { - cw.appendQuotedChallengeParameter("nonce", - challenge.getServerNonce()); + cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce()); } if (challenge.getDigestRef() != null) { - challenge.setDigestRef(new Reference(request.getResourceRef() - .getPath())); - cw.appendQuotedChallengeParameter("uri", challenge.getDigestRef() - .toString()); + challenge.setDigestRef(new Reference(request.getResourceRef().getPath())); + cw.appendQuotedChallengeParameter("uri", challenge.getDigestRef().toString()); } char[] responseDigest = formatResponseDigest(challenge, request); if (responseDigest != null) { - cw.appendQuotedChallengeParameter("response", new String( - responseDigest)); + cw.appendQuotedChallengeParameter("response", new String(responseDigest)); } if ((challenge.getDigestAlgorithm() != null) && !Digest.ALGORITHM_MD5.equals(challenge.getDigestAlgorithm())) { - cw.appendChallengeParameter("algorithm", - challenge.getDigestAlgorithm()); + cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm()); } if (challenge.getClientNonce() != null) { - cw.appendQuotedChallengeParameter("cnonce", - challenge.getClientNonce()); + cw.appendQuotedChallengeParameter("cnonce", challenge.getClientNonce()); } if (challenge.getOpaque() != null) { @@ -200,10 +186,8 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, cw.appendChallengeParameter("qop", challenge.getQuality()); } - if ((challenge.getQuality() != null) - && (challenge.getServerNonceCount() > 0)) { - cw.appendChallengeParameter("nc", - challenge.getServerNonceCountAsHex()); + if ((challenge.getQuality() != null) && (challenge.getServerNonceCount() > 0)) { + cw.appendChallengeParameter("nc", challenge.getServerNonceCountAsHex()); } for (Parameter param : challenge.getParameters()) { @@ -217,48 +201,57 @@ public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, /** * Formats the response digest. - * - * @param challengeResponse - * The challenge response. - * @param request - * The request if available. + * + * @param challengeResponse The challenge response. + * @param request The request if available. * @return The formatted secret of a challenge response. */ - public char[] formatResponseDigest(ChallengeResponse challengeResponse, - Request request) { + public char[] formatResponseDigest(ChallengeResponse challengeResponse, Request request) { String a1 = null; - if (!Digest.ALGORITHM_HTTP_DIGEST.equals(challengeResponse - .getSecretAlgorithm())) { - if (!AuthenticatorUtils - .anyNull(challengeResponse.getIdentifier(), - challengeResponse.getSecret(), - challengeResponse.getRealm())) { - a1 = DigestUtils.toHttpDigest( - challengeResponse.getIdentifier(), - challengeResponse.getSecret(), - challengeResponse.getRealm()); + if (!Digest.ALGORITHM_HTTP_DIGEST.equals(challengeResponse.getSecretAlgorithm())) { + if (!AuthenticatorUtils.anyNull( + challengeResponse.getIdentifier(), + challengeResponse.getSecret(), + challengeResponse.getRealm())) { + a1 = + DigestUtils.toHttpDigest( + challengeResponse.getIdentifier(), + challengeResponse.getSecret(), + challengeResponse.getRealm()); } } else { a1 = new String(challengeResponse.getSecret()); } if (a1 != null - && !AuthenticatorUtils.anyNull(request.getMethod(), - challengeResponse.getDigestRef())) { - StringBuilder sb = new StringBuilder().append(a1).append(':') - .append(challengeResponse.getServerNonce()); - - if (!AuthenticatorUtils.anyNull(challengeResponse.getQuality(), + && !AuthenticatorUtils.anyNull( + request.getMethod(), challengeResponse.getDigestRef())) { + StringBuilder sb = + new StringBuilder() + .append(a1) + .append(':') + .append(challengeResponse.getServerNonce()); + + if (!AuthenticatorUtils.anyNull( + challengeResponse.getQuality(), challengeResponse.getClientNonce(), challengeResponse.getServerNonceCount())) { - sb.append(':').append(AuthenticatorUtils.formatNonceCount(challengeResponse.getServerNonceCount())) - .append(':').append(challengeResponse.getClientNonce()) - .append(':').append(challengeResponse.getQuality()); + sb.append(':') + .append( + AuthenticatorUtils.formatNonceCount( + challengeResponse.getServerNonceCount())) + .append(':') + .append(challengeResponse.getClientNonce()) + .append(':') + .append(challengeResponse.getQuality()); } - String a2 = DigestUtils.toMd5(request.getMethod().toString() + ":" - + challengeResponse.getDigestRef().toString()); + String a2 = + DigestUtils.toMd5( + request.getMethod().toString() + + ":" + + challengeResponse.getDigestRef().toString()); sb.append(':').append(a2); return DigestUtils.toMd5(sb.toString()).toCharArray(); @@ -268,11 +261,10 @@ public char[] formatResponseDigest(ChallengeResponse challengeResponse, } @Override - public void parseRequest(ChallengeRequest challenge, Response response, - Series
httpHeaders) { + public void parseRequest( + ChallengeRequest challenge, Response response, Series
httpHeaders) { if (challenge.getRawValue() != null) { - HeaderReader hr = new HeaderReader( - challenge.getRawValue()); + HeaderReader hr = new HeaderReader(challenge.getRawValue()); try { Parameter param = hr.readParameter(); @@ -282,15 +274,13 @@ public void parseRequest(ChallengeRequest challenge, Response response, if ("realm".equals(param.getName())) { challenge.setRealm(param.getValue()); } else if ("domain".equals(param.getName())) { - challenge.getDomainRefs().add( - new Reference(param.getValue())); + challenge.getDomainRefs().add(new Reference(param.getValue())); } else if ("nonce".equals(param.getName())) { challenge.setServerNonce(param.getValue()); } else if ("opaque".equals(param.getName())) { challenge.setOpaque(param.getValue()); } else if ("stale".equals(param.getName())) { - challenge - .setStale(Boolean.valueOf(param.getValue())); + challenge.setStale(Boolean.valueOf(param.getValue())); } else if ("algorithm".equals(param.getName())) { challenge.setDigestAlgorithm(param.getValue()); } else if ("qop".equals(param.getName())) { @@ -306,14 +296,16 @@ public void parseRequest(ChallengeRequest challenge, Response response, } } catch (Exception e) { Context.getCurrentLogger() - .log(Level.WARNING, + .log( + Level.WARNING, "Unable to parse the challenge request header parameter", e); } } } catch (Exception e) { Context.getCurrentLogger() - .log(Level.WARNING, + .log( + Level.WARNING, "Unable to parse the challenge request header parameter", e); } @@ -321,11 +313,10 @@ public void parseRequest(ChallengeRequest challenge, Response response, } @Override - public void parseResponse(ChallengeResponse challenge, Request request, - Series
httpHeaders) { + public void parseResponse( + ChallengeResponse challenge, Request request, Series
httpHeaders) { if (challenge.getRawValue() != null) { - HeaderReader hr = new HeaderReader( - challenge.getRawValue()); + HeaderReader hr = new HeaderReader(challenge.getRawValue()); try { Parameter param = hr.readParameter(); @@ -339,8 +330,7 @@ public void parseResponse(ChallengeResponse challenge, Request request, } else if ("nonce".equals(param.getName())) { challenge.setServerNonce(param.getValue()); } else if ("uri".equals(param.getName())) { - challenge.setDigestRef(new Reference(param - .getValue())); + challenge.setDigestRef(new Reference(param.getValue())); } else if ("response".equals(param.getName())) { challenge.setSecret(param.getValue()); } else if ("algorithm".equals(param.getName())) { @@ -352,14 +342,14 @@ public void parseResponse(ChallengeResponse challenge, Request request, } else if ("qop".equals(param.getName())) { challenge.setQuality(param.getValue()); } else if ("nc".equals(param.getName())) { - challenge.setServerNonceCount(Integer.valueOf( - param.getValue(), 16)); + challenge.setServerNonceCount(Integer.valueOf(param.getValue(), 16)); } else { challenge.getParameters().add(param); } } catch (Throwable e) { Context.getCurrentLogger() - .log(Level.WARNING, + .log( + Level.WARNING, "Unable to parse the challenge request header parameter", e); } @@ -371,11 +361,11 @@ public void parseResponse(ChallengeResponse challenge, Request request, } } catch (Exception e) { Context.getCurrentLogger() - .log(Level.WARNING, + .log( + Level.WARNING, "Unable to parse the challenge request header parameter", e); } } } - } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java index c9a0c82fa4..1fe159b728 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; import org.restlet.Request; @@ -21,45 +20,42 @@ import org.restlet.security.User; /** - * Verifier for the HTTP DIGEST authentication scheme. Note that the "A1" hash - * specified in RFC 2617 is available via the - * {@link #getWrappedSecretDigest(String)} method. - * + * Verifier for the HTTP DIGEST authentication scheme. Note that the "A1" hash specified in RFC 2617 + * is available via the {@link #getWrappedSecretDigest(String)} method. + * * @author Jerome Louvel */ -public class HttpDigestVerifier extends - org.restlet.ext.crypto.DigestVerifier { +public class HttpDigestVerifier extends org.restlet.ext.crypto.DigestVerifier { /** The associated digest authenticator. */ private DigestAuthenticator digestAuthenticator; /** * Constructor. - * - * @param digestAuthenticator - * The associated digest authenticator. - * @param wrappedAlgorithm - * The digest algorithm of secrets provided by the wrapped - * verifier. - * @param wrappedVerifier - * The wrapped secret verifier. + * + * @param digestAuthenticator The associated digest authenticator. + * @param wrappedAlgorithm The digest algorithm of secrets provided by the wrapped verifier. + * @param wrappedVerifier The wrapped secret verifier. */ - public HttpDigestVerifier(DigestAuthenticator digestAuthenticator, - LocalVerifier wrappedVerifier, String wrappedAlgorithm) { + public HttpDigestVerifier( + DigestAuthenticator digestAuthenticator, + LocalVerifier wrappedVerifier, + String wrappedAlgorithm) { super(Digest.ALGORITHM_HTTP_DIGEST, wrappedVerifier, wrappedAlgorithm); this.digestAuthenticator = digestAuthenticator; } /** - * If the algorithm is {@link Digest#ALGORITHM_HTTP_DIGEST}, then is - * retrieves the realm for {@link #getDigestAuthenticator()} to compute the - * digest, otherwise, it keeps the default behavior. + * If the algorithm is {@link Digest#ALGORITHM_HTTP_DIGEST}, then is retrieves the realm for + * {@link #getDigestAuthenticator()} to compute the digest, otherwise, it keeps the default + * behavior. */ @Override protected char[] digest(String identifier, char[] secret, String algorithm) { if (Digest.ALGORITHM_HTTP_DIGEST.equals(algorithm)) { - String result = DigestUtils.toHttpDigest(identifier, secret, - getDigestAuthenticator().getRealm()); + String result = + DigestUtils.toHttpDigest( + identifier, secret, getDigestAuthenticator().getRealm()); if (result != null) { return result.toCharArray(); } @@ -72,7 +68,7 @@ protected char[] digest(String identifier, char[] secret, String algorithm) { /** * Returns the associated digest authenticator. - * + * * @return The associated digest authenticator. */ public DigestAuthenticator getDigestAuthenticator() { @@ -81,9 +77,8 @@ public DigestAuthenticator getDigestAuthenticator() { /** * Sets the associated digest authenticator. - * - * @param digestAuthenticator - * The associated digest authenticator. + * + * @param digestAuthenticator The associated digest authenticator. */ public void setDigestAuthenticator(DigestAuthenticator digestAuthenticator) { this.digestAuthenticator = digestAuthenticator; @@ -113,7 +108,8 @@ public int verify(Request request, Response response) { } try { - if (!HttpDigestHelper.isNonceValid(serverNonce, + if (!HttpDigestHelper.isNonceValid( + serverNonce, getDigestAuthenticator().getServerKey(), getDigestAuthenticator().getMaxServerNonceAge())) { // Nonce expired, send challenge request with stale=true @@ -131,8 +127,7 @@ public int verify(Request request, Response response) { Reference resourceRef = request.getResourceRef(); String requestUri = resourceRef.getPath(); - if ((resourceRef.getQuery() != null) - && (uri.indexOf('?') > -1)) { + if ((resourceRef.getQuery() != null) && (uri.indexOf('?') > -1)) { // IE neglects to include the query string, so // the workaround is to leave it off // unless both the calculated URI and the @@ -143,14 +138,20 @@ public int verify(Request request, Response response) { if (uri.equals(requestUri)) { char[] a1 = getWrappedSecretDigest(username); if (a1 != null) { - StringBuilder expectedResponse = new StringBuilder().append(a1).append(':').append(serverNonce); + StringBuilder expectedResponse = + new StringBuilder().append(a1).append(':').append(serverNonce); if (!AuthenticatorUtils.anyNull(qop, clientNonce, nc)) { expectedResponse - .append(':').append(AuthenticatorUtils.formatNonceCount(nc)) - .append(':').append(clientNonce) - .append(':').append(qop); + .append(':') + .append(AuthenticatorUtils.formatNonceCount(nc)) + .append(':') + .append(clientNonce) + .append(':') + .append(qop); } - String a2 = DigestUtils.toMd5(request.getMethod().toString() + ":" + requestUri); + String a2 = + DigestUtils.toMd5( + request.getMethod().toString() + ":" + requestUri); expectedResponse.append(':').append(a2); if (!DigestUtils.toMd5(expectedResponse.toString()).equals(cresponse)) { @@ -174,5 +175,4 @@ public int verify(Request request, Response response) { return result; } - } diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java index 4c74e7e91f..9ca3492a3a 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java @@ -1,16 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + import org.junit.jupiter.api.Test; -import org.restlet.*; +import org.restlet.Application; +import org.restlet.Component; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.CookieSetting; import org.restlet.data.Form; import org.restlet.data.MediaType; @@ -19,11 +26,9 @@ import org.restlet.resource.ResourceException; import org.restlet.security.MapVerifier; -import static org.junit.jupiter.api.Assertions.*; - /** * Unit test for the {@link CookieAuthenticator} class. - * + * * @author Jerome Louvel */ public class CookieAuthenticatorTestCase { @@ -32,21 +37,22 @@ public static class CookieGuardedApplication extends Application { @Override public Restlet createInboundRoot() { - CookieAuthenticator co = new CookieAuthenticator(getContext(), - false, "My cookie realm", "MyExtraSecretKey".getBytes()); + CookieAuthenticator co = + new CookieAuthenticator( + getContext(), false, "My cookie realm", "MyExtraSecretKey".getBytes()); MapVerifier mapVerifier = new MapVerifier(); mapVerifier.getLocalSecrets().put("scott", "tiger".toCharArray()); co.setVerifier(mapVerifier); - Restlet hr = new Restlet() { - - @Override - public void handle(Request request, Response response) { - response.setEntity("Hello, world!", MediaType.TEXT_PLAIN); - } + Restlet hr = + new Restlet() { - }; + @Override + public void handle(Request request, Response response) { + response.setEntity("Hello, world!", MediaType.TEXT_PLAIN); + } + }; co.setNext(hr); return co; @@ -71,8 +77,8 @@ public void testCookieAuth1() { loginForm.add("login", "scott"); loginForm.add("password", "titi"); - ResourceException exception = assertThrows(ResourceException.class, - () -> loginCr.post(loginForm)); + ResourceException exception = + assertThrows(ResourceException.class, () -> loginCr.post(loginForm)); assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, exception.getStatus()); // 3) Login with right credentials diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java index 2fda07938c..dea8f70856 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java @@ -1,24 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.restlet.data.Digest; import org.restlet.security.MapVerifier; import org.restlet.security.Verifier; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Restlet unit tests for the DigestVerifierTestCase class. - * + * * @author Jerome Louvel */ public class DigestVerifierTestCase { @@ -28,46 +27,39 @@ public void test1() { MapVerifier mv = new MapVerifier(); mv.getLocalSecrets().put("scott", "tiger".toCharArray()); - DigestVerifier sdv = new DigestVerifier<>( - Digest.ALGORITHM_SHA_1, mv, null); + DigestVerifier sdv = new DigestVerifier<>(Digest.ALGORITHM_SHA_1, mv, null); assertEquals( Verifier.RESULT_VALID, - sdv.verify("scott", - "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray())); + sdv.verify("scott", "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray())); } @Test public void test2() { MapVerifier mv = new MapVerifier(); - mv.getLocalSecrets().put("scott", - "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray()); + mv.getLocalSecrets().put("scott", "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray()); - DigestVerifier sdv = new DigestVerifier<>( - Digest.ALGORITHM_SHA_1, mv, Digest.ALGORITHM_SHA_1); + DigestVerifier sdv = + new DigestVerifier<>(Digest.ALGORITHM_SHA_1, mv, Digest.ALGORITHM_SHA_1); assertEquals( Verifier.RESULT_VALID, - sdv.verify("scott", - "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray())); + sdv.verify("scott", "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray())); - assertEquals(Verifier.RESULT_INVALID, - sdv.verify("scott", "xxxxx".toCharArray())); + assertEquals(Verifier.RESULT_INVALID, sdv.verify("scott", "xxxxx".toCharArray())); - assertEquals(Verifier.RESULT_INVALID, + assertEquals( + Verifier.RESULT_INVALID, sdv.verify("tom", "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray())); } @Test public void test3() { MapVerifier mv = new MapVerifier(); - mv.getLocalSecrets().put("scott", - "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray()); + mv.getLocalSecrets().put("scott", "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray()); - DigestVerifier sdv = new DigestVerifier<>(null, - mv, Digest.ALGORITHM_SHA_1); + DigestVerifier sdv = new DigestVerifier<>(null, mv, Digest.ALGORITHM_SHA_1); - assertEquals(Verifier.RESULT_VALID, - sdv.verify("scott", "tiger".toCharArray())); + assertEquals(Verifier.RESULT_VALID, sdv.verify("scott", "tiger".toCharArray())); } } diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java index a60bf758a3..6655877873 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java @@ -1,30 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.restlet.data.ChallengeScheme.HTTP_DIGEST; +import static org.restlet.engine.security.AuthenticatorUtils.formatResponse; +import static org.restlet.engine.security.AuthenticatorUtils.parseResponse; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.restlet.Application; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; -import org.restlet.data.*; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Status; import org.restlet.engine.Engine; import org.restlet.routing.Router; import org.restlet.security.MapVerifier; -import static org.junit.jupiter.api.Assertions.*; -import static org.restlet.data.ChallengeScheme.HTTP_DIGEST; -import static org.restlet.engine.security.AuthenticatorUtils.formatResponse; -import static org.restlet.engine.security.AuthenticatorUtils.parseResponse; - /** * Restlet unit tests for HTTP DIGEST authentication client/server. * @@ -40,10 +45,11 @@ public void testDigest() { Response response = testApplication.handle(request); assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); - ChallengeRequest httpDigestChallengeRequest = response.getChallengeRequests() - .stream().filter(cr -> HTTP_DIGEST.equals(cr.getScheme())) - .findFirst() - .orElse(null); + ChallengeRequest httpDigestChallengeRequest = + response.getChallengeRequests().stream() + .filter(cr -> HTTP_DIGEST.equals(cr.getScheme())) + .findFirst() + .orElse(null); assertNotNull(httpDigestChallengeRequest); String realm = httpDigestChallengeRequest.getRealm(); @@ -57,10 +63,13 @@ public void testDigest() { // Try authenticated request request = new Request(Method.GET, "/"); - ChallengeResponse challengeResponseAsDefinedByClient = new ChallengeResponse(httpDigestChallengeRequest, response, - "scott", "tiger".toCharArray()); - String authHeaderAsSentByHttpClient = formatResponse(challengeResponseAsDefinedByClient, request, request.getHeaders()); - ChallengeResponse challengeResponseAsParsedByServer = parseResponse(request, authHeaderAsSentByHttpClient, request.getHeaders()); + ChallengeResponse challengeResponseAsDefinedByClient = + new ChallengeResponse( + httpDigestChallengeRequest, response, "scott", "tiger".toCharArray()); + String authHeaderAsSentByHttpClient = + formatResponse(challengeResponseAsDefinedByClient, request, request.getHeaders()); + ChallengeResponse challengeResponseAsParsedByServer = + parseResponse(request, authHeaderAsSentByHttpClient, request.getHeaders()); request.setChallengeResponse(challengeResponseAsParsedByServer); response = testApplication.handle(request); @@ -80,21 +89,22 @@ private static class MyApplication extends Application { public Restlet createInboundRoot() { Router router = new Router(getContext()); - DigestAuthenticator authenticator = new DigestAuthenticator(getContext(),"TestRealm", "mySecretServerKey"); + DigestAuthenticator authenticator = + new DigestAuthenticator(getContext(), "TestRealm", "mySecretServerKey"); MapVerifier mapVerifier = new MapVerifier(); mapVerifier.getLocalSecrets().put("scott", "tiger".toCharArray()); authenticator.setWrappedVerifier(mapVerifier); - Restlet restlet = new Restlet(getContext()) { - @Override - public void handle(Request request, Response response) { - response.setEntity("hello, world", MediaType.TEXT_PLAIN); - } - }; + Restlet restlet = + new Restlet(getContext()) { + @Override + public void handle(Request request, Response response) { + response.setEntity("hello, world", MediaType.TEXT_PLAIN); + } + }; authenticator.setNext(restlet); router.attach("/", authenticator); return router; } } - } diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java index 2ce0e0b6eb..02a2a7342c 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.restlet.Request; import org.restlet.data.ChallengeResponse; @@ -19,40 +20,34 @@ import org.restlet.engine.header.HeaderConstants; import org.restlet.util.Series; -import static org.junit.jupiter.api.Assertions.assertEquals; - public class HttpAwsS3HelperTestCase { - /** - * Test Amazon S3 authentication. - */ + /** Test Amazon S3 authentication. */ @Test public void testAwsS3() { HttpAwsS3Helper helper = new HttpAwsS3Helper(); // Example Object GET ChallengeWriter cw = new ChallengeWriter(); - ChallengeResponse challenge = new ChallengeResponse( - ChallengeScheme.HTTP_AWS_S3, "0PN5J17HBGZHT7JJ3X82", - "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"); - Request request = new Request(Method.GET, - "http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); + ChallengeResponse challenge = + new ChallengeResponse( + ChallengeScheme.HTTP_AWS_S3, + "0PN5J17HBGZHT7JJ3X82", + "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"); + Request request = + new Request(Method.GET, "http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); Series

httpHeaders = new Series<>(Header.class); - httpHeaders.add(HeaderConstants.HEADER_DATE, - "Tue, 27 Mar 2007 19:36:42 +0000"); + httpHeaders.add(HeaderConstants.HEADER_DATE, "Tue, 27 Mar 2007 19:36:42 +0000"); helper.formatResponse(cw, challenge, request, httpHeaders); - assertEquals("0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=", - cw.toString()); + assertEquals("0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=", cw.toString()); // Example Object PUT cw = new ChallengeWriter(); request.setMethod(Method.PUT); - httpHeaders.set(HeaderConstants.HEADER_DATE, - "Tue, 27 Mar 2007 21:15:45 +0000", true); + httpHeaders.set(HeaderConstants.HEADER_DATE, "Tue, 27 Mar 2007 21:15:45 +0000", true); httpHeaders.add(HeaderConstants.HEADER_CONTENT_LENGTH, "94328"); httpHeaders.add(HeaderConstants.HEADER_CONTENT_TYPE, "image/jpeg"); helper.formatResponse(cw, challenge, request, httpHeaders); - assertEquals("0PN5J17HBGZHT7JJ3X82:hcicpDDvL9SsO6AkvxqmIWkmOuQ=", - cw.toString()); + assertEquals("0PN5J17HBGZHT7JJ3X82:hcicpDDvL9SsO6AkvxqmIWkmOuQ=", cw.toString()); } } diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java index 4eb78b3a22..7f37b39837 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java @@ -1,31 +1,31 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.restlet.data.Reference; -import static org.junit.jupiter.api.Assertions.assertEquals; - public class HttpAwsS3HostNameTestCase { private String checkAddress(final String host, final String path) { - return AwsUtils.getCanonicalizedResourceName(new Reference() { - public String getHostDomain() { - return host; - } - - public String getPath() { - return path; - } - }); + return AwsUtils.getCanonicalizedResourceName( + new Reference() { + public String getHostDomain() { + return host; + } + + public String getPath() { + return path; + } + }); } @Test @@ -33,8 +33,8 @@ public void testGetCanonicalizedResourceName1() { // http://s3-website-eu-west-1.amazonaws.com/reiabucket/louvel_cover150.jpg assertEquals( "/reiabucket/louvel_cover150.jpg", - checkAddress("s3-website-eu-west-1.amazonaws.com", - "/reiabucket/louvel_cover150.jpg")); + checkAddress( + "s3-website-eu-west-1.amazonaws.com", "/reiabucket/louvel_cover150.jpg")); } @Test @@ -42,8 +42,7 @@ public void testGetCanonicalizedResourceName2() { // http://reiabucket.s3.amazonaws.com/louvel_cover150.jpg assertEquals( "/reiabucket/louvel_cover150.jpg", - checkAddress("reiabucket.s3.amazonaws.com", - "/louvel_cover150.jpg")); + checkAddress("reiabucket.s3.amazonaws.com", "/louvel_cover150.jpg")); } @Test @@ -51,8 +50,6 @@ public void testGetCanonicalizedResourceName3() { // http://s3.amazonaws.com/reiabucket/louvel_cover150.jpg assertEquals( "/reiabucket/louvel_cover150.jpg", - checkAddress("s3.amazonaws.com", - "/reiabucket/louvel_cover150.jpg")); + checkAddress("s3.amazonaws.com", "/reiabucket/louvel_cover150.jpg")); } - } diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java index f712db4ea4..6cebc54389 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -18,11 +19,8 @@ import org.restlet.engine.header.HeaderConstants; import org.restlet.util.Series; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** - * Unit test for {@link AwsUtils}. Test cases are taken from the examples - * provided from Authenticating REST Requests * @@ -41,40 +39,29 @@ public class HttpAwsS3SigningTestCase { public void setUpEach() throws Exception { getRequest = new Request(); Series

headers = new Series
(Header.class); - getRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, - headers); - headers.add(HeaderConstants.HEADER_DATE, - "Tue, 27 Mar 2007 19:36:42 +0000"); + getRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers); + headers.add(HeaderConstants.HEADER_DATE, "Tue, 27 Mar 2007 19:36:42 +0000"); getRequest.setMethod(Method.GET); - getRequest - .setResourceRef("http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); + getRequest.setResourceRef("http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); putRequest = new Request(); headers = new Series
(Header.class); - putRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, - headers); + putRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers); headers.add(HeaderConstants.HEADER_CONTENT_LENGTH, "94328"); headers.add(HeaderConstants.HEADER_CONTENT_TYPE, "image/jpeg"); - headers.add(HeaderConstants.HEADER_DATE, - "Tue, 27 Mar 2007 21:15:45 +0000"); + headers.add(HeaderConstants.HEADER_DATE, "Tue, 27 Mar 2007 21:15:45 +0000"); putRequest.setMethod(Method.PUT); - putRequest - .setResourceRef("http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); + putRequest.setResourceRef("http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); uploadRequest = new Request(); headers = new Series
(Header.class); - uploadRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, - headers); + uploadRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers); headers.add(HeaderConstants.HEADER_CONTENT_LENGTH, "5913339"); - headers.add(HeaderConstants.HEADER_CONTENT_MD5, - "4gJE4saaMU4BqNR0kLY+lw=="); - headers.add(HeaderConstants.HEADER_CONTENT_TYPE, - "application/x-download"); - headers.add(HeaderConstants.HEADER_DATE, - "Tue, 27 Mar 2007 21:06:08 +0000"); + headers.add(HeaderConstants.HEADER_CONTENT_MD5, "4gJE4saaMU4BqNR0kLY+lw=="); + headers.add(HeaderConstants.HEADER_CONTENT_TYPE, "application/x-download"); + headers.add(HeaderConstants.HEADER_DATE, "Tue, 27 Mar 2007 21:06:08 +0000"); uploadRequest.setMethod(Method.PUT); - uploadRequest - .setResourceRef("http://static.johnsmith.net:8080/db-backup.dat.gz"); + uploadRequest.setResourceRef("http://static.johnsmith.net:8080/db-backup.dat.gz"); headers.add("x-amz-acl", "public-read"); headers.add("X-Amz-Meta-ReviewedBy", "joe@johnsmith.net"); headers.add("X-Amz-Meta-ReviewedBy", "jane@johnsmith.net"); @@ -97,58 +84,64 @@ public void testGetCanonicalizedAmzHeaders() { assertEquals(expected, actual); headers = uploadRequest.getHeaders(); - expected = "x-amz-acl:public-read\n" - + "x-amz-meta-checksumalgorithm:crc32\n" - + "x-amz-meta-filechecksum:0x02661779\n" - + "x-amz-meta-reviewedby:joe@johnsmith.net,jane@johnsmith.net\n"; + expected = + "x-amz-acl:public-read\n" + + "x-amz-meta-checksumalgorithm:crc32\n" + + "x-amz-meta-filechecksum:0x02661779\n" + + "x-amz-meta-reviewedby:joe@johnsmith.net,jane@johnsmith.net\n"; actual = AwsUtils.getCanonicalizedAmzHeaders(headers); assertEquals(expected, actual); } @Test public void testGetCanonicalizedResourceName() { - String result = AwsUtils.getCanonicalizedResourceName(getRequest - .getResourceRef()); + String result = AwsUtils.getCanonicalizedResourceName(getRequest.getResourceRef()); assertEquals("/johnsmith/photos/puppy.jpg", result); } @Test public void testGetSignature() { - String result = AwsUtils.getS3Signature(getRequest, - ACCESS_KEY.toCharArray()); + String result = AwsUtils.getS3Signature(getRequest, ACCESS_KEY.toCharArray()); assertEquals("xXjDGYUmKxnwqr5KXNPGldn5LbA=", result); result = AwsUtils.getS3Signature(putRequest, ACCESS_KEY.toCharArray()); assertEquals("hcicpDDvL9SsO6AkvxqmIWkmOuQ=", result); - result = AwsUtils.getS3Signature(uploadRequest, - ACCESS_KEY.toCharArray()); + result = AwsUtils.getS3Signature(uploadRequest, ACCESS_KEY.toCharArray()); assertEquals("C0FlOtU8Ylb9KDTpZqYkZPX91iI=", result); } @Test public void testGetStringToSign() { - String expected = "GET\n" + "\n" + "\n" - + "Tue, 27 Mar 2007 19:36:42 +0000\n" - + "/johnsmith/photos/puppy.jpg"; + String expected = + "GET\n" + + "\n" + + "\n" + + "Tue, 27 Mar 2007 19:36:42 +0000\n" + + "/johnsmith/photos/puppy.jpg"; String actual = AwsUtils.getS3StringToSign(getRequest); assertEquals(expected, actual); - expected = "PUT\n" + "\n" + "image/jpeg\n" - + "Tue, 27 Mar 2007 21:15:45 +0000\n" - + "/johnsmith/photos/puppy.jpg"; + expected = + "PUT\n" + + "\n" + + "image/jpeg\n" + + "Tue, 27 Mar 2007 21:15:45 +0000\n" + + "/johnsmith/photos/puppy.jpg"; actual = AwsUtils.getS3StringToSign(putRequest); assertEquals(expected, actual); - expected = "PUT\n" + "4gJE4saaMU4BqNR0kLY+lw==\n" - + "application/x-download\n" - + "Tue, 27 Mar 2007 21:06:08 +0000\n" - + "x-amz-acl:public-read\n" - + "x-amz-meta-checksumalgorithm:crc32\n" - + "x-amz-meta-filechecksum:0x02661779\n" - + "x-amz-meta-reviewedby:" - + "joe@johnsmith.net,jane@johnsmith.net\n" - + "/static.johnsmith.net/db-backup.dat.gz"; + expected = + "PUT\n" + + "4gJE4saaMU4BqNR0kLY+lw==\n" + + "application/x-download\n" + + "Tue, 27 Mar 2007 21:06:08 +0000\n" + + "x-amz-acl:public-read\n" + + "x-amz-meta-checksumalgorithm:crc32\n" + + "x-amz-meta-filechecksum:0x02661779\n" + + "x-amz-meta-reviewedby:" + + "joe@johnsmith.net,jane@johnsmith.net\n" + + "/static.johnsmith.net/db-backup.dat.gz"; actual = AwsUtils.getS3StringToSign(uploadRequest); assertEquals(expected, actual); } diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java index 9810e20f9a..9f0bee8f21 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,8 +23,6 @@ import org.restlet.security.Verifier; import org.restlet.util.Series; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Unit tests for {@link AwsVerifier}. * @@ -50,15 +49,14 @@ private Request createRequest() { @BeforeEach public void setUpEach() throws Exception { - localVerifier = new LocalVerifier() { - @Override - public char[] getLocalSecret(String identifier) { - if (ACCESS_ID.equals(identifier)) - return ACCESS_KEY.toCharArray(); - else - return "password".toCharArray(); - } - }; + localVerifier = + new LocalVerifier() { + @Override + public char[] getLocalSecret(String identifier) { + if (ACCESS_ID.equals(identifier)) return ACCESS_KEY.toCharArray(); + else return "password".toCharArray(); + } + }; awsVerifier = new AwsVerifier(localVerifier); } @@ -73,22 +71,20 @@ public void tearDownEach() throws Exception { public void testVerify() { Request request = createRequest(); @SuppressWarnings("unchecked") - Series

headers = (Series
) request.getAttributes().get( - HeaderConstants.ATTRIBUTE_HEADERS); + Series
headers = + (Series
) request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); // Test for missing due to no challenge response assertEquals(Verifier.RESULT_MISSING, awsVerifier.verify(request, null)); - ChallengeResponse cr = new ChallengeResponse( - ChallengeScheme.HTTP_AWS_S3); + ChallengeResponse cr = new ChallengeResponse(ChallengeScheme.HTTP_AWS_S3); request.setChallengeResponse(cr); // Test missing due to no identifier assertEquals(Verifier.RESULT_MISSING, awsVerifier.verify(request, null)); // Test authentication with bad credentials - String sig = AwsUtils.getS3Signature(request, - "badpassword".toCharArray()); + String sig = AwsUtils.getS3Signature(request, "badpassword".toCharArray()); cr.setRawValue(ACCESS_ID + ":" + sig); assertEquals(Verifier.RESULT_INVALID, awsVerifier.verify(request, null)); @@ -102,8 +98,7 @@ public void testVerify() { assertEquals(Verifier.RESULT_INVALID, awsVerifier.verify(request, null)); // Test stale due to out of date header - headers.add(HeaderConstants.HEADER_DATE, - "Tue, 27 Mar 1999 19:36:42 +0000"); + headers.add(HeaderConstants.HEADER_DATE, "Tue, 27 Mar 1999 19:36:42 +0000"); assertEquals(Verifier.RESULT_STALE, awsVerifier.verify(request, null)); } } diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java index 2d4e8ac240..01c7abc381 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java @@ -1,65 +1,62 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.crypto.internal; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.Request; -import org.restlet.data.*; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Digest; +import org.restlet.data.Method; +import org.restlet.data.Reference; import org.restlet.engine.Engine; import org.restlet.engine.security.AuthenticatorUtils; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - public class HttpDigestHelperTestCase { - /** - * Tests the authentication parsing for HTTP DIGEST. - * - */ + /** Tests the authentication parsing for HTTP DIGEST. */ @Test public void testParsingDigest() { // make sure the Digest authentication scheme is registered Engine.getInstance().getRegisteredAuthenticators().add(new HttpDigestHelper()); - ChallengeResponse cres1 = new ChallengeResponse( - ChallengeScheme.HTTP_DIGEST, - null, - "admin", - "12345".toCharArray(), - Digest.ALGORITHM_NONE, - null, - "qop", - new Reference("/protected/asdass"), - null, - null, - "MTE3NzEwMzIwMjkwMDoxNmMzODFiYzRjNWRjMmMyOTVkMWFhNDdkMTQ4OGFlMw==", - "MTE3NzEwMzIwMjkwMDoxNmMzODFiYzRjNWRjMmMyOTVkMWFhNDdkMTQ4OGFlMw==", - 1, 0L); - - Request request = new Request(Method.GET, - "http://remote.com/protected/asdass"); - String authorization1 = AuthenticatorUtils.formatResponse(cres1, - request, null); - String authenticate1 = "Digest realm=realm, domain=\"/protected/ /alsoProtected/\", qop=auth, algorithm=MD5, nonce=\"MTE3NzEwMzIwMjg0Mjo2NzFjODQyMjAyOWRlNWQ1YjFjNmEzYzJmOWRlZmE2Mw==\""; - - ChallengeResponse cres = AuthenticatorUtils.parseResponse(null, - authorization1, null); + ChallengeResponse cres1 = + new ChallengeResponse( + ChallengeScheme.HTTP_DIGEST, + null, + "admin", + "12345".toCharArray(), + Digest.ALGORITHM_NONE, + null, + "qop", + new Reference("/protected/asdass"), + null, + null, + "MTE3NzEwMzIwMjkwMDoxNmMzODFiYzRjNWRjMmMyOTVkMWFhNDdkMTQ4OGFlMw==", + "MTE3NzEwMzIwMjkwMDoxNmMzODFiYzRjNWRjMmMyOTVkMWFhNDdkMTQ4OGFlMw==", + 1, + 0L); + + Request request = new Request(Method.GET, "http://remote.com/protected/asdass"); + String authorization1 = AuthenticatorUtils.formatResponse(cres1, request, null); + String authenticate1 = + "Digest realm=realm, domain=\"/protected/ /alsoProtected/\", qop=auth, algorithm=MD5, nonce=\"MTE3NzEwMzIwMjg0Mjo2NzFjODQyMjAyOWRlNWQ1YjFjNmEzYzJmOWRlZmE2Mw==\""; + + ChallengeResponse cres = AuthenticatorUtils.parseResponse(null, authorization1, null); cres.setRawValue(null); - assertEquals(authorization1, - AuthenticatorUtils.formatResponse(cres, request, null)); + assertEquals(authorization1, AuthenticatorUtils.formatResponse(cres, request, null)); - List creq = AuthenticatorUtils.parseRequest(null, - authenticate1, null); + List creq = AuthenticatorUtils.parseRequest(null, authenticate1, null); assertEquals(creq.size(), 1); - assertEquals(authenticate1, - AuthenticatorUtils.formatRequest(creq.get(0), null, null)); + assertEquals(authenticate1, AuthenticatorUtils.formatRequest(creq.get(0), null, null)); } } diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java index c934bf40f3..af5f869a65 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java @@ -1,33 +1,29 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker; +import freemarker.cache.TemplateLoader; +import freemarker.template.Configuration; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.Date; - import org.restlet.Context; import org.restlet.Request; import org.restlet.data.Method; import org.restlet.data.Reference; import org.restlet.representation.Representation; -import freemarker.cache.TemplateLoader; -import freemarker.template.Configuration; - /** - * FreeMarker template loader based on a Context's client dispatcher. You can - * set an instance on a FreeMarker configuration via the - * {@link Configuration#setTemplateLoader(TemplateLoader)} method. - * + * FreeMarker template loader based on a Context's client dispatcher. You can set an instance on a + * FreeMarker configuration via the {@link Configuration#setTemplateLoader(TemplateLoader)} method. + * * @author Jerome Louvel */ public class ContextTemplateLoader implements TemplateLoader { @@ -40,11 +36,9 @@ public class ContextTemplateLoader implements TemplateLoader { /** * Constructor. - * - * @param context - * The Restlet context. - * @param baseRef - * The base reference. + * + * @param context The Restlet context. + * @param baseRef The base reference. */ public ContextTemplateLoader(Context context, Reference baseRef) { this(context, baseRef.toString()); @@ -52,11 +46,9 @@ public ContextTemplateLoader(Context context, Reference baseRef) { /** * Constructor. - * - * @param context - * The Restlet context. - * @param baseUri - * The base URI. + * + * @param context The Restlet context. + * @param baseUri The base URI. */ public ContextTemplateLoader(Context context, String baseUri) { this.context = context; @@ -65,9 +57,8 @@ public ContextTemplateLoader(Context context, String baseUri) { /** * Close the template source. - * - * @param templateSource - * The template source {@link Representation}. + * + * @param templateSource The template source {@link Representation}. */ public void closeTemplateSource(Object templateSource) throws IOException { if (templateSource instanceof Representation) { @@ -76,11 +67,9 @@ public void closeTemplateSource(Object templateSource) throws IOException { } /** - * Finds the object that acts as the source of the template with the given - * name. - * - * @param name - * The template name. + * Finds the object that acts as the source of the template with the given name. + * + * @param name The template name. * @return The template source {@link Representation}. */ public Object findTemplateSource(String name) throws IOException { @@ -92,14 +81,17 @@ public Object findTemplateSource(String name) throws IOException { fullUri = getBaseUri() + "/" + name; } - return (getContext() == null) ? null : getContext() - .getClientDispatcher().handle(new Request(Method.GET, fullUri)) - .getEntity(); + return (getContext() == null) + ? null + : getContext() + .getClientDispatcher() + .handle(new Request(Method.GET, fullUri)) + .getEntity(); } /** * Returns the base URI. - * + * * @return The base URI. */ private String getBaseUri() { @@ -108,7 +100,7 @@ private String getBaseUri() { /** * Returns the Restlet context. - * + * * @return The Restlet context. */ private Context getContext() { @@ -117,29 +109,23 @@ private Context getContext() { /** * Returns the modification time. - * - * @param templateSource - * The template source {@link Representation}. + * + * @param templateSource The template source {@link Representation}. * @return The modification time. */ public long getLastModified(Object templateSource) { - Date lastModified = ((Representation) templateSource) - .getModificationDate(); + Date lastModified = ((Representation) templateSource).getModificationDate(); return (lastModified == null) ? -1L : lastModified.getTime(); } /** * Returns the reader for the template source. - * - * @param templateSource - * The template source {@link Representation}. - * @param characterSet - * The reader character set. + * + * @param templateSource The template source {@link Representation}. + * @param characterSet The reader character set. */ - public Reader getReader(Object templateSource, String characterSet) - throws IOException { + public Reader getReader(Object templateSource, String characterSet) throws IOException { Representation r = (Representation) templateSource; return new InputStreamReader(r.getStream(), characterSet); } - } diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java index eb4c36a21d..7e823de0ea 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker; +import freemarker.template.Template; import java.io.IOException; import java.util.List; - import org.restlet.engine.converter.ConverterHelper; import org.restlet.engine.resource.VariantInfo; import org.restlet.ext.freemarker.internal.ResolverHashModel; @@ -20,12 +19,10 @@ import org.restlet.resource.Resource; import org.restlet.util.Resolver; -import freemarker.template.Template; - /** - * Converter between the FreeMarker Template objects and Representations. The - * adjoined data model is based on the request and response objects. - * + * Converter between the FreeMarker Template objects and Representations. The adjoined data model is + * based on the request and response objects. + * * @author Thierry Boileau. */ public class FreemarkerConverter extends ConverterHelper { @@ -50,30 +47,28 @@ public float score(Object source, Variant target, Resource resource) { } @Override - public float score(Representation source, Class target, - Resource resource) { + public float score(Representation source, Class target, Resource resource) { return -1.0f; } @Override - public T toObject(Representation source, Class target, - Resource resource) throws IOException { + public T toObject(Representation source, Class target, Resource resource) + throws IOException { return null; } @Override - public Representation toRepresentation(Object source, Variant target, - Resource resource) throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { if (source instanceof Template) { - return new TemplateRepresentation((Template) source, - new ResolverHashModel(Resolver.createResolver( - resource.getRequest(), resource.getResponse())), + return new TemplateRepresentation( + (Template) source, + new ResolverHashModel( + Resolver.createResolver(resource.getRequest(), resource.getResponse())), target.getMediaType()); - } return null; } - } diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java index 149ea4d527..dd3a4ce46c 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker; +import freemarker.template.Configuration; +import freemarker.template.TemplateHashModel; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -18,20 +19,16 @@ import org.restlet.routing.Filter; import org.restlet.util.Resolver; -import freemarker.template.Configuration; -import freemarker.template.TemplateHashModel; - /** - * Filter response's entity and wrap it with a FreeMarker's template - * representation. By default, the template representation provides a data model - * based on the request and response objects. In order for the wrapping to - * happen, the representations must have the {@link Encoding#FREEMARKER} + * Filter response's entity and wrap it with a FreeMarker's template representation. By default, the + * template representation provides a data model based on the request and response objects. In order + * for the wrapping to happen, the representations must have the {@link Encoding#FREEMARKER} * encoding set.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Thierry Boileau */ public class TemplateFilter extends Filter { @@ -42,9 +39,7 @@ public class TemplateFilter extends Filter { /** The template's data model. */ private volatile Object dataModel; - /** - * Constructor. - */ + /** Constructor. */ public TemplateFilter() { super(); this.configuration = new Configuration(); @@ -52,9 +47,8 @@ public TemplateFilter() { /** * Constructor. - * - * @param context - * The context. + * + * @param context The context. */ public TemplateFilter(Context context) { super(context); @@ -63,11 +57,9 @@ public TemplateFilter(Context context) { /** * Constructor. - * - * @param context - * The context. - * @param next - * The next Restlet. + * + * @param context The context. + * @param next The next Restlet. */ public TemplateFilter(Context context, Restlet next) { super(context, next); @@ -76,13 +68,10 @@ public TemplateFilter(Context context, Restlet next) { /** * Constructor. - * - * @param context - * The context. - * @param next - * The next Restlet. - * @param dataModel - * The filter's data model. + * + * @param context The context. + * @param next The next Restlet. + * @param dataModel The filter's data model. */ public TemplateFilter(Context context, Restlet next, Object dataModel) { this(context, next); @@ -91,16 +80,12 @@ public TemplateFilter(Context context, Restlet next, Object dataModel) { /** * Constructor. - * - * @param context - * The context. - * @param next - * The next Restlet. - * @param dataModel - * The filter's data model. + * + * @param context The context. + * @param next The next Restlet. + * @param dataModel The filter's data model. */ - public TemplateFilter(Context context, Restlet next, - Resolver dataModel) { + public TemplateFilter(Context context, Restlet next, Resolver dataModel) { this(context, next); this.dataModel = dataModel; } @@ -108,34 +93,31 @@ public TemplateFilter(Context context, Restlet next, @Override protected void afterHandle(Request request, Response response) { if (response.isEntityAvailable() - && response.getEntity().getEncodings() - .contains(Encoding.FREEMARKER)) { - TemplateRepresentation representation = new TemplateRepresentation( - response.getEntity(), this.configuration, response - .getEntity().getMediaType()); + && response.getEntity().getEncodings().contains(Encoding.FREEMARKER)) { + TemplateRepresentation representation = + new TemplateRepresentation( + response.getEntity(), + this.configuration, + response.getEntity().getMediaType()); representation.setDataModel(createDataModel(request, response)); response.setEntity(representation); } } /** - * Creates the FreeMarker data model for a given call. By default, it will - * create a {@link TemplateHashModel} based on the result of - * {@link Resolver#createResolver(Request, Response)}. If the - * {@link #getDataModel()} method has a non null value, it will be used. - * - * @param request - * The handled request. - * @param response - * The handled response. + * Creates the FreeMarker data model for a given call. By default, it will create a {@link + * TemplateHashModel} based on the result of {@link Resolver#createResolver(Request, Response)}. + * If the {@link #getDataModel()} method has a non-null value, it will be used. + * + * @param request The handled request. + * @param response The handled response. * @return The FreeMarker data model for the given call. */ protected Object createDataModel(Request request, Response response) { Object result = null; if (this.dataModel == null) { - result = new ResolverHashModel(Resolver.createResolver(request, - response)); + result = new ResolverHashModel(Resolver.createResolver(request, response)); } else { result = this.dataModel; } @@ -145,7 +127,7 @@ protected Object createDataModel(Request request, Response response) { /** * Returns the FreeMarker configuration. - * + * * @return The FreeMarker configuration. */ public Configuration getConfiguration() { @@ -153,9 +135,9 @@ public Configuration getConfiguration() { } /** - * Returns the template data model common to all calls. If each call should - * have a specific model, you should set this property to null. - * + * Returns the template data model common to all calls. If each call should have a specific + * model, you should set this property to null. + * * @return The template data model common to all calls. */ public Object getDataModel() { @@ -164,23 +146,20 @@ public Object getDataModel() { /** * Sets the FreeMarker configuration. - * - * @param config - * FreeMarker configuration. + * + * @param config FreeMarker configuration. */ public void setConfiguration(Configuration config) { this.configuration = config; } /** - * Sets the template data model common to all calls. If each call should - * have a specific model, you should set this property to null. - * - * @param dataModel - * The template data model common to all calls. + * Sets the template data model common to all calls. If each call should have a specific model, + * you should set this property to null. + * + * @param dataModel The template data model common to all calls. */ public void setDataModel(Object dataModel) { this.dataModel = dataModel; } - } diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java index 822899c020..74d5f781e5 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java @@ -1,17 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; import java.io.IOException; import java.io.Writer; - import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -22,14 +23,9 @@ import org.restlet.representation.WriterRepresentation; import org.restlet.util.Resolver; -import freemarker.template.Configuration; -import freemarker.template.Template; -import freemarker.template.TemplateException; - /** - * FreeMarker template representation. Useful for dynamic string-based - * representations. - * + * FreeMarker template representation. Useful for dynamic string-based representations. + * * @see FreeMarker home page * @author Jerome Louvel */ @@ -37,51 +33,57 @@ public class TemplateRepresentation extends WriterRepresentation { /** * Returns a FreeMarker template from a representation and a configuration. - * - * @param config - * The FreeMarker configuration. - * @param templateRepresentation - * The template representation. + * + * @param config The FreeMarker configuration. + * @param templateRepresentation The template representation. * @return The template or null if not found. */ - public static Template getTemplate(Configuration config, - Representation templateRepresentation) { + public static Template getTemplate( + Configuration config, Representation templateRepresentation) { try { // Instantiate the template with the character set of the template // representation if it has been set, otherwise use UTF-8. if (templateRepresentation.getCharacterSet() != null) { - return new Template("template", - templateRepresentation.getReader(), config, + return new Template( + "template", + templateRepresentation.getReader(), + config, templateRepresentation.getCharacterSet().getName()); } - return new Template("template", templateRepresentation.getReader(), - config, CharacterSet.UTF_8.getName()); + return new Template( + "template", + templateRepresentation.getReader(), + config, + CharacterSet.UTF_8.getName()); } catch (IOException e) { - Context.getCurrentLogger().warning( - "Unable to get the template from the representation " - + templateRepresentation.getLocationRef() - + ". Error message: " + e.getMessage()); + Context.getCurrentLogger() + .warning( + "Unable to get the template from the representation " + + templateRepresentation.getLocationRef() + + ". Error message: " + + e.getMessage()); return null; } } /** * Returns a FreeMarker template from its name and a configuration. - * - * @param config - * The FreeMarker configuration. - * @param templateName - * The template name. + * + * @param config The FreeMarker configuration. + * @param templateName The template name. * @return The template or null if not found. */ public static Template getTemplate(Configuration config, String templateName) { try { return config.getTemplate(templateName); } catch (IOException e) { - Context.getCurrentLogger().warning( - "Unable to get the template " + templateName - + ". Error message: " + e.getMessage()); + Context.getCurrentLogger() + .warning( + "Unable to get the template " + + templateName + + ". Error message: " + + e.getMessage()); return null; } } @@ -94,105 +96,85 @@ public static Template getTemplate(Configuration config, String templateName) { /** * Constructor. - * - * @param templateRepresentation - * The FreeMarker template provided via a representation. - * @param config - * The FreeMarker configuration. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The FreeMarker template provided via a representation. + * @param config The FreeMarker configuration. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(Representation templateRepresentation, - Configuration config, MediaType mediaType) { + public TemplateRepresentation( + Representation templateRepresentation, Configuration config, MediaType mediaType) { this(getTemplate(config, templateRepresentation), mediaType); } /** * Constructor. - * - * @param templateRepresentation - * The FreeMarker template provided via a representation. - * @param config - * The FreeMarker configuration. - * @param dataModel - * The template's data model. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The FreeMarker template provided via a representation. + * @param config The FreeMarker configuration. + * @param dataModel The template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(Representation templateRepresentation, - Configuration config, Object dataModel, MediaType mediaType) { + public TemplateRepresentation( + Representation templateRepresentation, + Configuration config, + Object dataModel, + MediaType mediaType) { this(getTemplate(config, templateRepresentation), dataModel, mediaType); } /** * Constructor. - * - * @param templateRepresentation - * The FreeMarker template provided via a representation. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The FreeMarker template provided via a representation. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(Representation templateRepresentation, - MediaType mediaType) { + public TemplateRepresentation(Representation templateRepresentation, MediaType mediaType) { this(templateRepresentation, new Configuration(), mediaType); } /** * Constructor. Uses a default FreeMarker configuration. - * - * @param templateRepresentation - * The FreeMarker template provided via a representation. - * @param dataModel - * The template's data model. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The FreeMarker template provided via a representation. + * @param dataModel The template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(Representation templateRepresentation, - Object dataModel, MediaType mediaType) { + public TemplateRepresentation( + Representation templateRepresentation, Object dataModel, MediaType mediaType) { this(templateRepresentation, new Configuration(), dataModel, mediaType); } /** * Constructor. - * - * @param templateName - * The FreeMarker template's name. The full path is resolved by - * the configuration. - * @param config - * The FreeMarker configuration. - * @param mediaType - * The representation's media type. + * + * @param templateName The FreeMarker template's name. The full path is resolved by the + * configuration. + * @param config The FreeMarker configuration. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(String templateName, Configuration config, - MediaType mediaType) { + public TemplateRepresentation(String templateName, Configuration config, MediaType mediaType) { this(getTemplate(config, templateName), mediaType); } /** * Constructor. - * - * @param templateName - * The FreeMarker template's name. The full path is resolved by - * the configuration. - * @param config - * The FreeMarker configuration. - * @param dataModel - * The template's data model. - * @param mediaType - * The representation's media type. + * + * @param templateName The FreeMarker template's name. The full path is resolved by the + * configuration. + * @param config The FreeMarker configuration. + * @param dataModel The template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(String templateName, Configuration config, - Object dataModel, MediaType mediaType) { + public TemplateRepresentation( + String templateName, Configuration config, Object dataModel, MediaType mediaType) { this(getTemplate(config, templateName), dataModel, mediaType); } /** * Constructor. - * - * @param template - * The FreeMarker template. - * @param mediaType - * The representation's media type. + * + * @param template The FreeMarker template. + * @param mediaType The representation's media-type. */ public TemplateRepresentation(Template template, MediaType mediaType) { super(mediaType); @@ -201,16 +183,12 @@ public TemplateRepresentation(Template template, MediaType mediaType) { /** * Constructor. - * - * @param template - * The FreeMarker template. - * @param dataModel - * The template's data model. - * @param mediaType - * The representation's media type. + * + * @param template The FreeMarker template. + * @param dataModel The template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(Template template, Object dataModel, - MediaType mediaType) { + public TemplateRepresentation(Template template, Object dataModel, MediaType mediaType) { super(mediaType); this.template = template; this.dataModel = dataModel; @@ -218,7 +196,7 @@ public TemplateRepresentation(Template template, Object dataModel, /** * Returns the template's data model. - * + * * @return The template's data model. */ public Object getDataModel() { @@ -227,7 +205,7 @@ public Object getDataModel() { /** * Returns the FreeMarker template. - * + * * @return The FreeMarker template. */ public Template getTemplate() { @@ -236,9 +214,8 @@ public Template getTemplate() { /** * Sets the template's data model. - * - * @param dataModel - * The template's data model. + * + * @param dataModel The template's data model. * @return The template's data model. */ public Object setDataModel(Object dataModel) { @@ -247,29 +224,24 @@ public Object setDataModel(Object dataModel) { } /** - * Sets the template's data model from a request/response pair. This default - * implementation uses a Resolver. - * + * Sets the template's data model from a request/response pair. This default implementation uses + * a Resolver. + * * @see Resolver * @see Resolver#createResolver(Request, Response) - * - * @param request - * The request where data are located. - * @param response - * The response where data are located. + * @param request The request where data are located. + * @param response The response where data are located. * @return The template's data model. */ public Object setDataModel(Request request, Response response) { - this.dataModel = new ResolverHashModel(Resolver.createResolver(request, - response)); + this.dataModel = new ResolverHashModel(Resolver.createResolver(request, response)); return this.dataModel; } /** * Sets the template's data model from a resolver. - * - * @param resolver - * The resolver. + * + * @param resolver The resolver. * @return The template's data model. */ public Object setDataModel(Resolver resolver) { @@ -279,9 +251,8 @@ public Object setDataModel(Resolver resolver) { /** * Sets the FreeMarker template. - * - * @param template - * The FreeMarker template. + * + * @param template The FreeMarker template. */ public void setTemplate(Template template) { this.template = template; @@ -289,9 +260,8 @@ public void setTemplate(Template template) { /** * Writes the datum as a stream of characters. - * - * @param writer - * The writer to use when writing. + * + * @param writer The writer to use when writing. */ @Override public void write(Writer writer) throws IOException { @@ -299,14 +269,11 @@ public void write(Writer writer) throws IOException { try { this.template.process(getDataModel(), writer); } catch (TemplateException te) { - throw new IOException("Template processing error " - + te.getMessage()); + throw new IOException("Template processing error " + te.getMessage()); } } else { Context.getCurrentLogger() - .warning( - "Unable to write the template representation. No template found."); + .warning("Unable to write the template representation. No template found."); } } - } diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java index 2a2459e533..dc71e7229c 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java @@ -1,23 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker.internal; -import org.restlet.util.Resolver; - import freemarker.template.TemplateHashModel; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; +import org.restlet.util.Resolver; /** * Template Hash Model based on a Resolver instance. - * + * * @author Jerome Louvel */ public class ResolverHashModel implements TemplateHashModel { @@ -26,19 +24,15 @@ public class ResolverHashModel implements TemplateHashModel { /** * Constructor. - * - * @param resolver - * The inner resolver. + * + * @param resolver The inner resolver. */ public ResolverHashModel(Resolver resolver) { super(); this.resolver = resolver; } - /** - * Returns a scalar model based on the value returned by the resolver - * according to the key. - */ + /** Returns a scalar model based on the value returned by the resolver according to the key. */ public TemplateModel get(String key) throws TemplateModelException { Object value = this.resolver.resolve(key); if (value == null) { @@ -50,11 +44,7 @@ public TemplateModel get(String key) throws TemplateModelException { return new ScalarModel(value); } - /** - * Returns false. - * - * @Return False. - */ + /** Returns false. @Return False. */ public boolean isEmpty() throws TemplateModelException { return false; } diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java index 01b5b67c17..9abfc66a6e 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker.internal; import freemarker.template.TemplateModelException; @@ -14,7 +13,7 @@ /** * Data model that gives access to a Object value. - * + * * @author Jerome Louvel */ class ScalarModel implements TemplateScalarModel { @@ -23,9 +22,8 @@ class ScalarModel implements TemplateScalarModel { /** * Constructor. - * - * @param value - * the provided value of this scalar model. + * + * @param value the provided value of this scalar model. */ public ScalarModel(Object value) { super(); diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java index 013cc4ed5d..78c1b3a0a9 100644 --- a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java @@ -1,29 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker; -import freemarker.template.Configuration; -import org.junit.jupiter.api.Test; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; +import static org.junit.jupiter.api.Assertions.assertEquals; +import freemarker.template.Configuration; import java.io.File; import java.io.FileWriter; import java.nio.file.Files; import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; /** * Unit test for the FreeMarker extension. - * + * * @author Jerome Louvel */ public class FreeMarkerTestCase { @@ -43,11 +41,12 @@ public void testTemplate() throws Exception { fmc.setDirectoryForTemplateLoading(testDir); final Map map = Map.of("value", "myValue"); - final String result = new TemplateRepresentation(testFile.getName(), fmc, map, MediaType.TEXT_PLAIN).getText(); + final String result = + new TemplateRepresentation(testFile.getName(), fmc, map, MediaType.TEXT_PLAIN) + .getText(); assertEquals("Value=myValue", result); // Clean-up IoUtils.delete(testDir, true); } - } diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java index c64de2613c..de2a8f098a 100644 --- a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java @@ -1,25 +1,29 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.freemarker; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.restlet.*; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.LocalReference; import org.restlet.data.Method; import org.restlet.data.Protocol; import org.restlet.engine.Engine; import org.restlet.resource.Directory; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test case for template filters. * @@ -29,7 +33,7 @@ public class TemplateFilterTestCase { @Test public void representationShouldBeUsedAsTemplate() throws Exception { - Request request = new Request(Method.GET,"/template.txt.fmt"); + Request request = new Request(Method.GET, "/template.txt.fmt"); Response response = testApplication.handle(request); assertEquals("Method=GET/Path=/template.txt.fmt", response.getEntity().getText()); } @@ -66,9 +70,12 @@ private static class MyFreemakerApplication extends Application { @Override public Restlet createInboundRoot() { - final Directory directory = new Directory(getContext(), LocalReference.createClapReference(TemplateFilterTestCase.class.getPackage())); + final Directory directory = + new Directory( + getContext(), + LocalReference.createClapReference( + TemplateFilterTestCase.class.getPackage())); return new org.restlet.ext.freemarker.TemplateFilter(getContext(), directory); } } - } diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java index 6190c764df..b5a9560143 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.gson; import java.io.IOException; import java.util.List; - import org.restlet.data.MediaType; import org.restlet.data.Preference; import org.restlet.engine.converter.ConverterHelper; @@ -22,36 +20,31 @@ /** * Converter between the JSON and Representation classe based on Gson library. - * + * * @author Neal Mi */ public class GsonConverter extends ConverterHelper { /** Variant with media type application/json. */ - private static final VariantInfo VARIANT_JSON = new VariantInfo( - MediaType.APPLICATION_JSON); + private static final VariantInfo VARIANT_JSON = new VariantInfo(MediaType.APPLICATION_JSON); /** * Creates the unmarshaling {@link GsonRepresentation}. - * + * * @param - * @param source - * The source representation to unmarshal. - * @param objectClass - * The object class to instantiate. + * @param source The source representation to unmarshal. + * @param objectClass The object class to instantiate. * @return The unmarshaling {@link GsonRepresentation}. */ - protected GsonRepresentation create(Representation source, - Class objectClass) { + protected GsonRepresentation create(Representation source, Class objectClass) { return new GsonRepresentation(source, objectClass); } /** * Creates the marshaling {@link GsonRepresentation}. - * + * * @param - * @param source - * The source object to marshal. + * @param source The source object to marshal. * @return The marshaling {@link GsonRepresentation}. */ protected GsonRepresentation create(T source) { @@ -101,14 +94,12 @@ public float score(Object source, Variant target, Resource resource) { } @Override - public float score(Representation source, Class target, - Resource resource) { + public float score(Representation source, Class target, Resource resource) { float result = -1.0F; if (source instanceof GsonRepresentation) { result = 1.0F; - } else if ((target != null) - && GsonRepresentation.class.isAssignableFrom(target)) { + } else if ((target != null) && GsonRepresentation.class.isAssignableFrom(target)) { result = 1.0F; } else if (VARIANT_JSON.isCompatible(source)) { result = 0.8F; @@ -119,8 +110,8 @@ public float score(Representation source, Class target, @SuppressWarnings("unchecked") @Override - public T toObject(Representation source, Class target, - Resource resource) throws IOException { + public T toObject(Representation source, Class target, Resource resource) + throws IOException { Object result = null; // The source for the gson conversion @@ -134,8 +125,7 @@ public T toObject(Representation source, Class target, if (gsonSource != null) { // Handle the conversion - if ((target != null) - && GsonRepresentation.class.isAssignableFrom(target)) { + if ((target != null) && GsonRepresentation.class.isAssignableFrom(target)) { result = gsonSource; } else { result = gsonSource.getObject(); @@ -146,8 +136,8 @@ public T toObject(Representation source, Class target, } @Override - public Representation toRepresentation(Object source, Variant target, - Resource resource) throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { Representation result = null; if (source instanceof GsonRepresentation) { @@ -167,9 +157,7 @@ public Representation toRepresentation(Object source, Variant target, } @Override - public void updatePreferences(List> preferences, - Class entity) { + public void updatePreferences(List> preferences, Class entity) { updatePreferences(preferences, MediaType.APPLICATION_JSON, 1.0F); } - } diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java index ecbda03833..446fb95e84 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java @@ -1,25 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.gson; -import java.io.IOException; -import java.io.Writer; -import java.lang.reflect.Type; -import java.text.DateFormat; -import java.util.Date; - -import org.joda.time.DateTime; -import org.restlet.data.MediaType; -import org.restlet.representation.Representation; -import org.restlet.representation.WriterRepresentation; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; @@ -31,11 +19,20 @@ import com.google.gson.JsonSerializer; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.util.Date; +import org.joda.time.DateTime; +import org.restlet.data.MediaType; +import org.restlet.representation.Representation; +import org.restlet.representation.WriterRepresentation; /** - * Representation based on a JSON document. JSON stands for JavaScript Object - * Notation and is a lightweight data-interchange format. - * + * Representation based on a JSON document. JSON stands for JavaScript Object Notation and is a + * lightweight data-interchange format. + * * @author Neal Mi * @see Gson project */ @@ -43,25 +40,23 @@ public class GsonRepresentation extends WriterRepresentation { /** * Custom deserializer for {@link Date} instances. - * + * * @author Neal Mi. */ private static class ISODateDeserializer implements JsonDeserializer { - public Date deserialize(JsonElement json, Type typeOfT, - JsonDeserializationContext context) throws JsonParseException { - return new DateTime(json.getAsJsonPrimitive().getAsString()) - .toDate(); + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return new DateTime(json.getAsJsonPrimitive().getAsString()).toDate(); } } /** * Custom serializer for {@link Date} instances. - * + * * @author Neal Mi. */ private static class ISODateSerializer implements JsonSerializer { - public JsonElement serialize(Date src, Type typeOfSrc, - JsonSerializationContext context) { + public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { DateTime dt = new DateTime(src); // DateTime dtz = dt.withZone(DateTimeZone.forOffsetHours(-8)); return new JsonPrimitive(dt.toString()); @@ -82,14 +77,11 @@ public JsonElement serialize(Date src, Type typeOfSrc, /** * Constructor. - * - * @param representation - * The representation to parse. - * @param objectClass - * The object class to instantiate. + * + * @param representation The representation to parse. + * @param objectClass The object class to instantiate. */ - public GsonRepresentation(Representation representation, - Class objectClass) { + public GsonRepresentation(Representation representation, Class objectClass) { super(representation.getMediaType()); this.object = null; this.objectClass = objectClass; @@ -99,23 +91,21 @@ public GsonRepresentation(Representation representation, /** * Constructor for the JSON media type. - * - * @param object - * The object to format. + * + * @param object The object to format. */ @SuppressWarnings("unchecked") public GsonRepresentation(T object) { super(MediaType.APPLICATION_JSON); this.object = object; - this.objectClass = ((Class) ((object == null) ? null : object - .getClass())); + this.objectClass = ((Class) ((object == null) ? null : object.getClass())); this.jsonRepresentation = null; this.builder = null; } /** * Returns a new instance of the builder for Gson instances. - * + * * @return a new instance of builder for Gson instances. */ protected GsonBuilder createBuilder() { @@ -126,22 +116,22 @@ protected GsonBuilder createBuilder() { /** * Returns the builder for Gson instances. - * + * * @return The builder for Gson instances. */ public GsonBuilder getBuilder() { if (builder == null) { - builder = createBuilder().registerTypeAdapter(Date.class, - new ISODateSerializer()).registerTypeAdapter(Date.class, - new ISODateDeserializer()); + builder = + createBuilder() + .registerTypeAdapter(Date.class, new ISODateSerializer()) + .registerTypeAdapter(Date.class, new ISODateDeserializer()); } return builder; } /** - * Returns the wrapped object, deserializing the representation with Gson if - * necessary. - * + * Returns the wrapped object, deserializing the representation with Gson if necessary. + * * @return The wrapped object. * @throws IOException */ @@ -152,9 +142,8 @@ public T getObject() throws IOException { result = this.object; } else if (this.jsonRepresentation != null) { Gson gson = getBuilder().create(); - result = gson.fromJson( - new JsonReader(jsonRepresentation.getReader()), - this.objectClass); + result = + gson.fromJson(new JsonReader(jsonRepresentation.getReader()), this.objectClass); } return result; @@ -162,7 +151,7 @@ public T getObject() throws IOException { /** * Returns the object class to instantiate. - * + * * @return The object class to instantiate. */ public Class getObjectClass() { @@ -171,9 +160,8 @@ public Class getObjectClass() { /** * Sets the Gson builder. - * - * @param builder - * The Gson builder. + * + * @param builder The Gson builder. */ public void setBuilder(GsonBuilder builder) { this.builder = builder; @@ -181,9 +169,8 @@ public void setBuilder(GsonBuilder builder) { /** * Sets the object to format. - * - * @param object - * The object to format. + * + * @param object The object to format. */ public void setObject(T object) { this.object = object; @@ -191,9 +178,8 @@ public void setObject(T object) { /** * Sets the object class to instantiate. - * - * @param objectClass - * The object class to instantiate. + * + * @param objectClass The object class to instantiate. */ public void setObjectClass(Class objectClass) { this.objectClass = objectClass; @@ -208,5 +194,4 @@ public void write(Writer writer) throws IOException { gson.toJson(object, objectClass, new JsonWriter(writer)); } } - } diff --git a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java index 9399532611..6f7f448465 100644 --- a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java +++ b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java @@ -1,16 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.gson; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.gson.annotations.Since; +import java.io.IOException; +import java.util.Date; import org.joda.time.DateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -20,11 +26,6 @@ import org.restlet.representation.StringRepresentation; import org.restlet.representation.Variant; -import java.io.IOException; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - /** * Unit test for the Gson extension. * @@ -46,8 +47,13 @@ private static class User { private final int rate; - public User(String loginId, String password, int rate, boolean active, - Date createAt, Date lastLogin) { + public User( + String loginId, + String password, + int rate, + boolean active, + Date createAt, + Date lastLogin) { super(); this.loginId = loginId; this.password = password; @@ -111,8 +117,10 @@ public final void testCreateRepresentationClassOfT() { @Test public final void testGsonRepresentationRead() throws IOException { - final String userAsJsonString = "{\"loginId\":\"hello\",\"password\":\"secret\",\"rate\":1,\"active\":true,\"createAt\":\"2012-05-20T15:41:01.489+08:00\",\"lastLogin\":\"2012-05-20T15:41:01.489+08:00\"}"; - Representation source = new StringRepresentation(userAsJsonString, MediaType.APPLICATION_JSON); + final String userAsJsonString = + "{\"loginId\":\"hello\",\"password\":\"secret\",\"rate\":1,\"active\":true,\"createAt\":\"2012-05-20T15:41:01.489+08:00\",\"lastLogin\":\"2012-05-20T15:41:01.489+08:00\"}"; + Representation source = + new StringRepresentation(userAsJsonString, MediaType.APPLICATION_JSON); GsonRepresentation gsonRep = new GsonRepresentation<>(source, User.class); User user = gsonRep.getObject(); @@ -166,7 +174,7 @@ public final void testScoreObjectVariantResource() { assertEquals(0.8F, score); float score1 = gsonConverter.score(source, v, null); - assertEquals(1.0F , score1); + assertEquals(1.0F, score1); } @Test @@ -183,8 +191,7 @@ public final void testScoreRepresentationClassOfTResource() { } @Test() - public final void testToObjectRepresentationClassOfTResource() - throws IOException { + public final void testToObjectRepresentationClassOfTResource() throws IOException { Representation source = new GsonRepresentation<>(user); User u = gsonConverter.toObject(source, User.class, null); @@ -202,8 +209,7 @@ public final void testToObjectRepresentationClassOfTResource() } @Test - public final void testToRepresentationObjectVariantResource() - throws IOException { + public final void testToRepresentationObjectVariantResource() throws IOException { Variant v = new Variant(MediaType.APPLICATION_JSON); Representation rep = gsonConverter.toRepresentation(user, v, null); assertNotNull(rep); @@ -213,5 +219,4 @@ public final void testToRepresentationObjectVariantResource() Representation rep1 = gsonConverter.toRepresentation(user, v1, null); assertNull(rep1); } - } diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java index 6a6deb4e79..c5f2eac3a4 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java @@ -1,28 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jaas; import java.io.IOException; - import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; - import org.restlet.Request; import org.restlet.Response; /** - * JAAS callback handler that automatically provides the identifier and secret - * when asked by login modules. - * + * JAAS callback handler that automatically provides the identifier and secret when asked by login + * modules. + * * @author Jerome Louvel */ public class ChallengeCallbackHandler implements CallbackHandler { @@ -35,11 +32,9 @@ public class ChallengeCallbackHandler implements CallbackHandler { /** * Constructor. - * - * @param request - * The handled request. - * @param response - * The handled response. + * + * @param request The handled request. + * @param response The handled response. */ public ChallengeCallbackHandler(Request request, Response response) { this.request = request; @@ -48,7 +43,7 @@ public ChallengeCallbackHandler(Request request, Response response) { /** * Returns the handled request. - * + * * @return The handled request. */ public Request getRequest() { @@ -57,7 +52,7 @@ public Request getRequest() { /** * Returns the handled response. - * + * * @return The handled response. */ public Response getResponse() { @@ -65,18 +60,17 @@ public Response getResponse() { } /** - * Handles a callback. The default implementation automatically sets the - * identifier on {@link javax.security.auth.callback.NameCallback} instances - * and the secret on {@link PasswordCallback}. - * - * @param callback - * The callback to handle. + * Handles a callback. The default implementation automatically sets the identifier on {@link + * javax.security.auth.callback.NameCallback} instances and the secret on {@link + * PasswordCallback}. + * + * @param callback The callback to handle. * @throws UnsupportedCallbackException */ - protected void handle(Callback callback) - throws UnsupportedCallbackException { + protected void handle(Callback callback) throws UnsupportedCallbackException { if (callback instanceof javax.security.auth.callback.NameCallback) { - javax.security.auth.callback.NameCallback nc = (javax.security.auth.callback.NameCallback) callback; + javax.security.auth.callback.NameCallback nc = + (javax.security.auth.callback.NameCallback) callback; if (getRequest().getChallengeResponse() != null) { nc.setName(getRequest().getChallengeResponse().getIdentifier()); @@ -88,20 +82,17 @@ protected void handle(Callback callback) pc.setPassword(getRequest().getChallengeResponse().getSecret()); } } else { - throw new UnsupportedCallbackException(callback, - "Unrecognized Callback"); + throw new UnsupportedCallbackException(callback, "Unrecognized Callback"); } } /** - * Handles the callbacks. The default implementation delegates the handling - * to the {@link #handle(Callback)} method. - * - * @param callbacks - * The callbacks to handle. + * Handles the callbacks. The default implementation delegates the handling to the {@link + * #handle(Callback)} method. + * + * @param callbacks The callbacks to handle. */ - public void handle(Callback[] callbacks) throws IOException, - UnsupportedCallbackException { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { if (callbacks != null) { for (Callback callback : callbacks) { @@ -112,9 +103,8 @@ public void handle(Callback[] callbacks) throws IOException, /** * Sets the handled request. - * - * @param request - * The handled request. + * + * @param request The handled request. */ public void setRequest(Request request) { this.request = request; @@ -122,12 +112,10 @@ public void setRequest(Request request) { /** * Sets the handled response. - * - * @param response - * The handled response. + * + * @param response The handled response. */ public void setResponse(Response response) { this.response = response; } - } diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java index 578a240681..033bef4d8a 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java @@ -1,38 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jaas; import java.security.AccessControlContext; import java.security.Principal; import java.security.PrivilegedAction; - import javax.security.auth.Subject; - import org.restlet.data.ClientInfo; import org.restlet.security.Role; /** * Utility class to facilitate integration between the Restlet and JAAS APIs. - * + * * @author Jerome Louvel */ public final class JaasUtils { /** - * Creates a JAAS subject based on a given {@link ClientInfo}. It adds a - * {@link ClientInfo#getUser()}, all the entries in - * {@link ClientInfo#getRoles()} and all other principals in - * {@link ClientInfo#getPrincipals()}. - * - * @param clientInfo - * The client info to expose as a subject. + * Creates a JAAS subject based on a given {@link ClientInfo}. It adds a {@link + * ClientInfo#getUser()}, all the entries in {@link ClientInfo#getRoles()} and all other + * principals in {@link ClientInfo#getPrincipals()}. + * + * @param clientInfo The client info to expose as a subject. * @return The populated JAAS subject. */ public static Subject createSubject(ClientInfo clientInfo) { @@ -56,43 +51,31 @@ public static Subject createSubject(ClientInfo clientInfo) { } /** - * Creates a JAAS subject on the {@link ClientInfo} and uses it to run the - * action, using - * {@link Subject#doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)} - * . This uses a null {@link AccessControlContext}. - * - * @param - * the return type of the action. - * @param clientInfo - * the client info from which to build as a subject. - * @param action - * the code to be run as the specified Subject. + * Creates a JAAS subject on the {@link ClientInfo} and uses it to run the action, using {@link + * Subject#doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)} . This uses a null + * {@link AccessControlContext}. + * + * @param the return type of the action. + * @param clientInfo the client info from which to build as a subject. + * @param action the code to be run as the specified Subject. * @return the value returned by the action. */ - public static T doAsPriviledged(ClientInfo clientInfo, - PrivilegedAction action) { + public static T doAsPriviledged(ClientInfo clientInfo, PrivilegedAction action) { return doAsPriviledged(clientInfo, action, null); } /** - * Creates a JAAS subject on the {@link ClientInfo} and uses it to run the - * action, using - * {@link Subject#doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)} - * . - * - * @param - * the return type of the action. - * @param clientInfo - * the client info from which to build a subject. - * @param action - * the code to be run as the specified Subject. - * @param acc - * the AccessControlContext to be tied to the specified subject - * and action. + * Creates a JAAS subject on the {@link ClientInfo} and uses it to run the action, using {@link + * Subject#doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)} . + * + * @param the return type of the action. + * @param clientInfo the client info from which to build a subject. + * @param action the code to be run as the specified Subject. + * @param acc the AccessControlContext to be tied to the specified subject and action. * @return the value returned by the action. */ - public static T doAsPriviledged(ClientInfo clientInfo, - PrivilegedAction action, AccessControlContext acc) { + public static T doAsPriviledged( + ClientInfo clientInfo, PrivilegedAction action, AccessControlContext acc) { Subject subject = JaasUtils.createSubject(clientInfo); T result = Subject.doAsPrivileged(subject, action, acc); return result; diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java index e32b4bf13c..899c9ffec1 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java @@ -1,22 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jaas; import java.security.Principal; - import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; - import org.restlet.Request; import org.restlet.Response; import org.restlet.data.ClientInfo; @@ -25,12 +22,14 @@ /** * Verifier that leverages the JAAS pluggable authentication mechanism. - * + * * @author Jerome Louvel - * @see JAAS - * Tutorials - * @see JAAS Reference - * Guide + * @see JAAS + * Tutorials + * @see JAAS + * Reference Guide */ public class JaasVerifier implements Verifier { @@ -45,28 +44,26 @@ public class JaasVerifier implements Verifier { /** * Constructor. - * - * @param name - * The JAAS login context name. + * + * @param name The JAAS login context name. */ public JaasVerifier(String name) { this.name = name; } /** - * Creates a callback handler for the given parameters. By default it - * returns one handler that handles name and password JAAS callbacks. - * + * Creates a callback handler for the given parameters. By default it returns one handler that + * handles name and password JAAS callbacks. + * * @return The callback handler created. */ - protected CallbackHandler createCallbackHandler(Request request, - Response response) { + protected CallbackHandler createCallbackHandler(Request request, Response response) { return new ChallengeCallbackHandler(request, response); } /** * Returns the optional JAAS login configuration. - * + * * @return The optional JAAS login configuration. */ public Configuration getConfiguration() { @@ -75,7 +72,7 @@ public Configuration getConfiguration() { /** * Returns the JAAS login context name. - * + * * @return The JAAS login context name. */ public String getName() { @@ -84,7 +81,7 @@ public String getName() { /** * Gets the user principal class name. - * + * * @return the user principal class name. */ public String getUserPrincipalClassName() { @@ -93,9 +90,8 @@ public String getUserPrincipalClassName() { /** * Sets the optional JAAS login configuration. - * - * @param configuration - * The optional JAAS login configuration. + * + * @param configuration The optional JAAS login configuration. */ public void setConfiguration(Configuration configuration) { this.configuration = configuration; @@ -103,38 +99,33 @@ public void setConfiguration(Configuration configuration) { /** * Sets the JAAS login context name. - * - * @param contextName - * The JAAS login context name. + * + * @param contextName The JAAS login context name. */ public void setName(String contextName) { this.name = contextName; } /** - * Sets the user principal class name. If a {@link User} is not associated - * with the {@link Request}'s {@link ClientInfo} and if one of the - * principals returned after the JAAS login is of this type, a new {@link User} will be associated with the - * {@link ClientInfo} using its + * Sets the user principal class name. If a {@link User} is not associated with the {@link + * Request}'s {@link ClientInfo} and if one of the principals returned after the JAAS login is + * of this type, a new {@link User} will be associated with the {@link ClientInfo} using its * name. - * - * @param userPrincipalClassName - * the user principal class name. + * + * @param userPrincipalClassName the user principal class name. */ public void setUserPrincipalClassName(String userPrincipalClassName) { this.userPrincipalClassName = userPrincipalClassName; } /** - * Verifies that the proposed secret is correct for the specified - * identifier. By default, it creates a JAAS login context with the callback - * handler obtained by {@link #createCallbackHandler(Request, Response)} and - * calls the {@link LoginContext#login()} method on it. - * - * @param request - * The request sent. - * @param response - * The response to update. + * Verifies that the proposed secret is correct for the specified identifier. By default, it + * creates a JAAS login context with the callback handler obtained by {@link + * #createCallbackHandler(Request, Response)} and calls the {@link LoginContext#login()} method + * on it. + * + * @param request The request sent. + * @param response The response to update. * @return Result of the verification based on the RESULT_* constants. */ public int verify(Request request, Response response) { @@ -146,15 +137,16 @@ public int verify(Request request, Response response) { subject.getPrincipals().add(request.getClientInfo().getUser()); } if (request.getClientInfo().getRoles() != null) { - subject.getPrincipals().addAll( - request.getClientInfo().getRoles()); + subject.getPrincipals().addAll(request.getClientInfo().getRoles()); } - subject.getPrincipals().addAll( - request.getClientInfo().getPrincipals()); - - LoginContext loginContext = new LoginContext(getName(), subject, - createCallbackHandler(request, response), - getConfiguration()); + subject.getPrincipals().addAll(request.getClientInfo().getPrincipals()); + + LoginContext loginContext = + new LoginContext( + getName(), + subject, + createCallbackHandler(request, response), + getConfiguration()); loginContext.login(); /* diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java index 5a89cfde64..6e4fe1b215 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jackson; import java.io.IOException; import java.util.List; - import org.restlet.data.MediaType; import org.restlet.data.Preference; import org.restlet.engine.converter.ConverterHelper; @@ -21,51 +19,44 @@ import org.restlet.resource.Resource; /** - * Converter between the JSON, JSON Smile, CSV, XML, YAML and Representation - * classes based on Jackson. - * + * Converter between the JSON, JSON Smile, CSV, XML, YAML and Representation classes based on + * Jackson. + * * @author Jerome Louvel * @author Thierry Boileau */ public class JacksonConverter extends ConverterHelper { /** Variant with media type application/xml. */ - private static final VariantInfo VARIANT_APPLICATION_XML = new VariantInfo( - MediaType.APPLICATION_XML); + private static final VariantInfo VARIANT_APPLICATION_XML = + new VariantInfo(MediaType.APPLICATION_XML); /** Variant with media type application/yaml. */ - private static final VariantInfo VARIANT_APPLICATION_YAML = new VariantInfo( - MediaType.APPLICATION_YAML); + private static final VariantInfo VARIANT_APPLICATION_YAML = + new VariantInfo(MediaType.APPLICATION_YAML); /** Variant with media type application/json. */ - private static final VariantInfo VARIANT_JSON = new VariantInfo( - MediaType.APPLICATION_JSON); + private static final VariantInfo VARIANT_JSON = new VariantInfo(MediaType.APPLICATION_JSON); /** Variant with media type application/x-json-smile. */ - private static final VariantInfo VARIANT_JSON_SMILE = new VariantInfo( - MediaType.APPLICATION_JSON_SMILE); + private static final VariantInfo VARIANT_JSON_SMILE = + new VariantInfo(MediaType.APPLICATION_JSON_SMILE); /** Variant with media type text/csv. */ - private static final VariantInfo VARIANT_TEXT_CSV = new VariantInfo( - MediaType.TEXT_CSV); + private static final VariantInfo VARIANT_TEXT_CSV = new VariantInfo(MediaType.TEXT_CSV); /** Variant with media type text/xml. */ - private static final VariantInfo VARIANT_TEXT_XML = new VariantInfo( - MediaType.TEXT_XML); + private static final VariantInfo VARIANT_TEXT_XML = new VariantInfo(MediaType.TEXT_XML); /** Variant with media type text/yaml. */ - private static final VariantInfo VARIANT_TEXT_YAML = new VariantInfo( - MediaType.TEXT_YAML); + private static final VariantInfo VARIANT_TEXT_YAML = new VariantInfo(MediaType.TEXT_YAML); /** * Creates the marshaling {@link JacksonRepresentation}. - * - * @param - * The expected class of the representation Java object. - * @param mediaType - * The target media type. - * @param source - * The source object to marshal. + * + * @param The expected class of the representation Java object. + * @param mediaType The target media type. + * @param source The source object to marshal. * @return The marshaling {@link JacksonRepresentation}. */ protected JacksonRepresentation create(MediaType mediaType, T source) { @@ -74,17 +65,13 @@ protected JacksonRepresentation create(MediaType mediaType, T source) { /** * Creates the unmarshaling {@link JacksonRepresentation}. - * - * @param - * The expected class of the representation Java object. - * @param source - * The source representation to unmarshal. - * @param objectClass - * The object class to instantiate. + * + * @param The expected class of the representation Java object. + * @param source The source representation to unmarshal. + * @param objectClass The object class to instantiate. * @return The unmarshaling {@link JacksonRepresentation}. */ - protected JacksonRepresentation create(Representation source, - Class objectClass) { + protected JacksonRepresentation create(Representation source, Class objectClass) { return new JacksonRepresentation(source, objectClass); } @@ -118,13 +105,12 @@ public List getVariants(Class source) { } /** - * Indicates if the given variant is compatible with the media types - * supported by this converter. - * - * @param variant - * The variant. - * @return True if the given variant is compatible with the media types - * supported by this converter. + * Indicates if the given variant is compatible with the media types supported by this + * converter. + * + * @param variant The variant. + * @return True if the given variant is compatible with the media types supported by this + * converter. */ protected boolean isCompatible(Variant variant) { return (variant != null) @@ -133,8 +119,8 @@ protected boolean isCompatible(Variant variant) { || (VARIANT_APPLICATION_XML.isCompatible(variant)) || (VARIANT_TEXT_XML.isCompatible(variant)) || VARIANT_APPLICATION_YAML.isCompatible(variant) - || VARIANT_TEXT_YAML.isCompatible(variant) || VARIANT_TEXT_CSV - .isCompatible(variant)); + || VARIANT_TEXT_YAML.isCompatible(variant) + || VARIANT_TEXT_CSV.isCompatible(variant)); } @Override @@ -157,14 +143,12 @@ public float score(Object source, Variant target, Resource resource) { } @Override - public float score(Representation source, Class target, - Resource resource) { + public float score(Representation source, Class target, Resource resource) { float result = -1.0F; if (source instanceof JacksonRepresentation) { result = 1.0F; - } else if ((target != null) - && JacksonRepresentation.class.isAssignableFrom(target)) { + } else if ((target != null) && JacksonRepresentation.class.isAssignableFrom(target)) { result = 1.0F; } else if (isCompatible(source)) { result = 0.8F; @@ -175,8 +159,8 @@ public float score(Representation source, Class target, @SuppressWarnings("unchecked") @Override - public T toObject(Representation source, Class target, - Resource resource) throws IOException { + public T toObject(Representation source, Class target, Resource resource) + throws IOException { Object result = null; // The source for the Jackson conversion @@ -189,8 +173,7 @@ public T toObject(Representation source, Class target, if (jacksonSource != null) { // Handle the conversion - if ((target != null) - && JacksonRepresentation.class.isAssignableFrom(target)) { + if ((target != null) && JacksonRepresentation.class.isAssignableFrom(target)) { result = jacksonSource; } else { result = jacksonSource.getObject(); @@ -201,8 +184,7 @@ public T toObject(Representation source, Class target, } @Override - public Representation toRepresentation(Object source, Variant target, - Resource resource) { + public Representation toRepresentation(Object source, Variant target, Resource resource) { Representation result = null; if (source instanceof JacksonRepresentation) { @@ -220,8 +202,7 @@ public Representation toRepresentation(Object source, Variant target, } @Override - public void updatePreferences(List> preferences, - Class entity) { + public void updatePreferences(List> preferences, Class entity) { updatePreferences(preferences, MediaType.APPLICATION_JSON, 1.0F); updatePreferences(preferences, MediaType.APPLICATION_JSON_SMILE, 1.0F); updatePreferences(preferences, MediaType.APPLICATION_XML, 1.0F); @@ -230,5 +211,4 @@ public void updatePreferences(List> preferences, updatePreferences(preferences, MediaType.TEXT_YAML, 1.0F); updatePreferences(preferences, MediaType.TEXT_CSV, 1.0F); } - } diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java index aa930b3931..d71a07b5e5 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java @@ -1,23 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jackson; -import java.io.IOException; -import java.io.OutputStream; - -import org.restlet.data.MediaType; -import org.restlet.engine.Edition; -import org.restlet.ext.jackson.internal.XmlFactoryProvider; -import org.restlet.representation.OutputRepresentation; -import org.restlet.representation.Representation; - import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator.Feature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -30,47 +20,50 @@ import com.fasterxml.jackson.dataformat.xml.XmlFactory; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import java.io.IOException; +import java.io.OutputStream; +import org.restlet.data.MediaType; +import org.restlet.engine.Edition; +import org.restlet.ext.jackson.internal.XmlFactoryProvider; +import org.restlet.representation.OutputRepresentation; +import org.restlet.representation.Representation; /** - * Representation based on the Jackson library. It can serialize and deserialize - * automatically in JSON, JSON binary (Smile), XML, YAML and CSV.
+ * Representation based on the Jackson library. It can serialize and deserialize automatically in + * JSON, JSON binary (Smile), XML, YAML and CSV.
*
- * SECURITY WARNING: Using XML parsers configured to not prevent nor limit - * document type definition (DTD) entity resolution can expose the parser to an - * XML Entity Expansion injection attack. - * + * SECURITY WARNING: Using XML parsers configured to not prevent nor limit document type definition + * (DTD) entity resolution can expose the parser to an XML Entity Expansion injection attack. + * * @see Jackson project * @see XML - * Entity Expansion injection attack + * href="https://github.com/restlet/restlet-framework-java/wiki/XEE-security-enhancements">XML + * Entity Expansion injection attack * @author Jerome Louvel - * @param - * The type to wrap. + * @param The type to wrap. */ public class JacksonRepresentation extends OutputRepresentation { /** - * True for expanding entity references when parsing XML representations. - * Default value provided by system property - * "org.restlet.ext.xml.expandingEntityRefs", false by default. + * True for expanding entity references when parsing XML representations. Default value provided + * by system property "org.restlet.ext.xml.expandingEntityRefs", false by default. */ - public final static boolean XML_EXPANDING_ENTITY_REFS = Boolean - .getBoolean("org.restlet.ext.xml.expandingEntityRefs"); + public static final boolean XML_EXPANDING_ENTITY_REFS = + Boolean.getBoolean("org.restlet.ext.xml.expandingEntityRefs"); /** - * True for validating DTD documents when parsing XML representations. - * Default value provided by system property - * "org.restlet.ext.xml.validatingDtd", false by default. + * True for validating DTD documents when parsing XML representations. Default value provided by + * system property "org.restlet.ext.xml.validatingDtd", false by default. */ - public final static boolean XML_VALIDATING_DTD = Boolean - .getBoolean("org.restlet.ext.xml.validatingDtd"); + public static final boolean XML_VALIDATING_DTD = + Boolean.getBoolean("org.restlet.ext.xml.validatingDtd"); /** The modifiable Jackson CSV schema. */ private CsvSchema csvSchema; /** - * Specifies that the parser will expand entity reference nodes. By default - * the value of this is set to false. + * Specifies that the parser will expand entity reference nodes. By default the value of this is + * set to false. */ private volatile boolean expandingEntityRefs; @@ -93,28 +86,24 @@ public class JacksonRepresentation extends OutputRepresentation { private volatile Representation representation; /** - * Indicates the desire for validating this type of XML representations - * against a DTD. Note that for XML schema or Relax NG validation, use the - * "schema" property instead. - * + * Indicates the desire for validating this type of XML representations against a DTD. Note that + * for XML schema or Relax NG validation, use the "schema" property instead. + * * @see javax.xml.parsers.DocumentBuilderFactory#setValidating(boolean) */ private volatile boolean validatingDtd; /** * Constructor. - * - * @param mediaType - * The target media type. - * @param object - * The object to format. + * + * @param mediaType The target media type. + * @param object The object to format. */ @SuppressWarnings("unchecked") public JacksonRepresentation(MediaType mediaType, T object) { super(mediaType); this.object = object; - this.objectClass = (Class) ((object == null) ? null : object - .getClass()); + this.objectClass = (Class) ((object == null) ? null : object.getClass()); this.representation = null; this.objectMapper = null; this.objectReader = null; @@ -126,14 +115,11 @@ public JacksonRepresentation(MediaType mediaType, T object) { /** * Constructor. - * - * @param representation - * The representation to parse. - * @param objectClass - * The object class to instantiate. + * + * @param representation The representation to parse. + * @param objectClass The object class to instantiate. */ - public JacksonRepresentation(Representation representation, - Class objectClass) { + public JacksonRepresentation(Representation representation, Class objectClass) { super(representation.getMediaType()); this.object = null; this.objectClass = objectClass; @@ -143,25 +129,22 @@ public JacksonRepresentation(Representation representation, this.objectWriter = null; this.csvSchema = null; this.expandingEntityRefs = XML_EXPANDING_ENTITY_REFS; - this.validatingDtd = XML_VALIDATING_DTD; + this.validatingDtd = XML_VALIDATING_DTD; } /** * Constructor for the JSON media type. - * - * @param object - * The object to format. + * + * @param object The object to format. */ public JacksonRepresentation(T object) { this(MediaType.APPLICATION_JSON, object); } /** - * Creates a Jackson CSV schema based on a mapper and the current object - * class. - * - * @param csvMapper - * The source CSV mapper. + * Creates a Jackson CSV schema based on a mapper and the current object class. + * + * @param csvMapper The source CSV mapper. * @return A Jackson CSV schema */ protected CsvSchema createCsvSchema(CsvMapper csvMapper) { @@ -169,9 +152,9 @@ protected CsvSchema createCsvSchema(CsvMapper csvMapper) { } /** - * Creates a Jackson object mapper based on a media type. It supports JSON, - * JSON Smile, XML, YAML and CSV. - * + * Creates a Jackson object mapper based on a media type. It supports JSON, JSON Smile, XML, + * YAML and CSV. + * * @return The Jackson object mapper. */ protected ObjectMapper createObjectMapper() { @@ -188,21 +171,23 @@ protected ObjectMapper createObjectMapper() { } else if (MediaType.APPLICATION_XML.isCompatible(getMediaType()) || MediaType.TEXT_XML.isCompatible(getMediaType())) { - if (Edition.ANDROID.isCurrentEdition() && XmlFactoryProvider.inputFactoryProvider == null) { - XmlFactoryProvider.inputFactoryProvider = new com.ctc.wstx.osgi.InputFactoryProviderImpl(); + if (Edition.ANDROID.isCurrentEdition() + && XmlFactoryProvider.inputFactoryProvider == null) { + XmlFactoryProvider.inputFactoryProvider = + new com.ctc.wstx.osgi.InputFactoryProviderImpl(); } - if (Edition.ANDROID.isCurrentEdition() && XmlFactoryProvider.outputFactoryProvider == null) { - XmlFactoryProvider.outputFactoryProvider = new com.ctc.wstx.osgi.OutputFactoryProviderImpl(); + if (Edition.ANDROID.isCurrentEdition() + && XmlFactoryProvider.outputFactoryProvider == null) { + XmlFactoryProvider.outputFactoryProvider = + new com.ctc.wstx.osgi.OutputFactoryProviderImpl(); } javax.xml.stream.XMLInputFactory xif = XmlFactoryProvider.newInputFactory(); xif.setProperty( javax.xml.stream.XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, isExpandingEntityRefs()); - xif.setProperty(javax.xml.stream.XMLInputFactory.SUPPORT_DTD, - isExpandingEntityRefs()); - xif.setProperty(javax.xml.stream.XMLInputFactory.IS_VALIDATING, - isValidatingDtd()); + xif.setProperty(javax.xml.stream.XMLInputFactory.SUPPORT_DTD, isExpandingEntityRefs()); + xif.setProperty(javax.xml.stream.XMLInputFactory.IS_VALIDATING, isValidatingDtd()); javax.xml.stream.XMLOutputFactory xof = XmlFactoryProvider.newOutputFactory(); XmlFactory xmlFactory = new XmlFactory(xif, xof); xmlFactory.configure(Feature.AUTO_CLOSE_TARGET, false); @@ -227,9 +212,9 @@ protected ObjectMapper createObjectMapper() { } /** - * Creates a Jackson object reader based on a mapper. Has a special handling - * for CSV media types. - * + * Creates a Jackson object reader based on a mapper. Has a special handling for CSV media + * types. + * * @return The Jackson object reader. */ protected ObjectReader createObjectReader() { @@ -247,9 +232,9 @@ protected ObjectReader createObjectReader() { } /** - * Creates a Jackson object writer based on a mapper. Has a special handling - * for CSV media types. - * + * Creates a Jackson object writer based on a mapper. Has a special handling for CSV media + * types. + * * @return The Jackson object writer. */ protected ObjectWriter createObjectWriter() { @@ -268,7 +253,7 @@ protected ObjectWriter createObjectWriter() { /** * Returns the modifiable Jackson CSV schema. - * + * * @return The modifiable Jackson CSV schema. */ public CsvSchema getCsvSchema() { @@ -280,9 +265,8 @@ public CsvSchema getCsvSchema() { } /** - * Returns the wrapped object, deserializing the representation with Jackson - * if necessary. - * + * Returns the wrapped object, deserializing the representation with Jackson if necessary. + * * @return The wrapped object. * @throws IOException */ @@ -292,8 +276,7 @@ public T getObject() throws IOException { if (this.object != null) { result = this.object; } else if (this.representation != null) { - result = getObjectReader().readValue( - this.representation.getStream()); + result = getObjectReader().readValue(this.representation.getStream()); } return result; @@ -301,7 +284,7 @@ public T getObject() throws IOException { /** * Returns the object class to instantiate. - * + * * @return The object class to instantiate. */ public Class getObjectClass() { @@ -309,9 +292,8 @@ public Class getObjectClass() { } /** - * Returns the modifiable Jackson object mapper. Useful to customize - * mappings. - * + * Returns the modifiable Jackson object mapper. Useful to customize mappings. + * * @return The modifiable Jackson object mapper. */ public ObjectMapper getObjectMapper() { @@ -323,9 +305,8 @@ public ObjectMapper getObjectMapper() { } /** - * Returns the modifiable Jackson object reader. Useful to customize - * deserialization. - * + * Returns the modifiable Jackson object reader. Useful to customize deserialization. + * * @return The modifiable Jackson object reader. */ public ObjectReader getObjectReader() { @@ -337,9 +318,8 @@ public ObjectReader getObjectReader() { } /** - * Returns the modifiable Jackson object writer. Useful to customize - * serialization. - * + * Returns the modifiable Jackson object writer. Useful to customize serialization. + * * @return The modifiable Jackson object writer. */ public ObjectWriter getObjectWriter() { @@ -351,19 +331,19 @@ public ObjectWriter getObjectWriter() { } /** - * Indicates if the parser expands entity reference nodes. - * By default, the value of this is set to true. - * + * Indicates if the parser expands entity reference nodes. By default, the value of this is set + * to true. + * * @return True if the parser expands entity reference nodes. */ public boolean isExpandingEntityRefs() { - return expandingEntityRefs; + return expandingEntityRefs; } /** - * Indicates the desire for validating this type of XML representations - * against an XML schema if one is referenced within the contents. - * + * Indicates the desire for validating this type of XML representations against an XML schema if + * one is referenced within the contents. + * * @return True if the schema-based validation is enabled. */ public boolean isValidatingDtd() { @@ -372,20 +352,18 @@ public boolean isValidatingDtd() { /** * Sets the Jackson CSV schema. - * - * @param csvSchema - * The Jackson CSV schema. + * + * @param csvSchema The Jackson CSV schema. */ public void setCsvSchema(CsvSchema csvSchema) { this.csvSchema = csvSchema; } /** - * Indicates if the parser expands entity reference nodes. - * By default, the value of this is set to true. - * - * @param expandEntityRefs - * True if the parser expands entity reference nodes. + * Indicates if the parser expands entity reference nodes. By default, the value of this is set + * to true. + * + * @param expandEntityRefs True if the parser expands entity reference nodes. */ public void setExpandingEntityRefs(boolean expandEntityRefs) { this.expandingEntityRefs = expandEntityRefs; @@ -393,9 +371,8 @@ public void setExpandingEntityRefs(boolean expandEntityRefs) { /** * Sets the object to format. - * - * @param object - * The object to format. + * + * @param object The object to format. */ public void setObject(T object) { this.object = object; @@ -403,9 +380,8 @@ public void setObject(T object) { /** * Sets the object class to instantiate. - * - * @param objectClass - * The object class to instantiate. + * + * @param objectClass The object class to instantiate. */ public void setObjectClass(Class objectClass) { this.objectClass = objectClass; @@ -413,9 +389,8 @@ public void setObjectClass(Class objectClass) { /** * Sets the Jackson object mapper. - * - * @param objectMapper - * The Jackson object mapper. + * + * @param objectMapper The Jackson object mapper. */ public void setObjectMapper(ObjectMapper objectMapper) { this.objectMapper = objectMapper; @@ -423,9 +398,8 @@ public void setObjectMapper(ObjectMapper objectMapper) { /** * Sets the Jackson object reader. - * - * @param objectReader - * The Jackson object reader. + * + * @param objectReader The Jackson object reader. */ public void setObjectReader(ObjectReader objectReader) { this.objectReader = objectReader; @@ -433,20 +407,18 @@ public void setObjectReader(ObjectReader objectReader) { /** * Sets the Jackson object writer. - * - * @param objectWriter - * The Jackson object writer. + * + * @param objectWriter The Jackson object writer. */ public void setObjectWriter(ObjectWriter objectWriter) { this.objectWriter = objectWriter; } /** - * Indicates the desire for validating this type of XML representations - * against an XML schema if one is referenced within the contents. - * - * @param validating - * The new validation flag to set. + * Indicates the desire for validating this type of XML representations against an XML schema if + * one is referenced within the contents. + * + * @param validating The new validation flag to set. */ public void setValidatingDtd(boolean validating) { this.validatingDtd = validating; diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java index 0f1c43a43f..a26c49389f 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java @@ -1,71 +1,65 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jackson.internal; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLOutputFactory; - import org.codehaus.stax2.osgi.Stax2InputFactoryProvider; import org.codehaus.stax2.osgi.Stax2OutputFactoryProvider; /** - * Provides {@link javax.xml.stream.XMLInputFactory} - * and {@link javax.xml.stream.XMLOutputFactory} + * Provides {@link javax.xml.stream.XMLInputFactory} and {@link javax.xml.stream.XMLOutputFactory} * in an OSGI context. * - * In a no-OSGI context, the factories are retrieved with java service loader. + *

In a no-OSGI context, the factories are retrieved with java service loader. * * @author Manuel Boillod */ public class XmlFactoryProvider { /** - * Allow to explicitly set the Stax2InputFactory instance in OSGI context. - * In a no-OSGI context, the factory is retrieved with java service loader. + * Allow to explicitly set the Stax2InputFactory instance in OSGI context. In a no-OSGI context, + * the factory is retrieved with java service loader. * - *

Note: Stax2 implementation is provided by woodstox library which - * is a dependency of Jackson.

+ *

Note: Stax2 implementation is provided by woodstox library which is a dependency of + * Jackson. * * @see org.restlet.ext.jackson.internal.Activator */ public static Stax2InputFactoryProvider inputFactoryProvider = null; /** - * Allow to explicitly set the Stax2OutputFactoryProvider instance in OSGI context. - * In a no-OSGI context, the factory is retrieved with java service loader. + * Allow to explicitly set the Stax2OutputFactoryProvider instance in OSGI context. In a no-OSGI + * context, the factory is retrieved with java service loader. * - *

Note: Stax2 implementation is provided by woodstox library which - * is a dependency of Jackson.

+ *

Note: Stax2 implementation is provided by woodstox library which is a dependency of + * Jackson. * * @see org.restlet.ext.jackson.internal.Activator */ public static Stax2OutputFactoryProvider outputFactoryProvider = null; - /** - * Returns an instance of {@link javax.xml.stream.XMLInputFactory} - * according to the classpath. + * Returns an instance of {@link javax.xml.stream.XMLInputFactory} according to the classpath. */ public static XMLInputFactory newInputFactory() { - return inputFactoryProvider != null ? - inputFactoryProvider.createInputFactory() : - XMLInputFactory.newFactory(); + return inputFactoryProvider != null + ? inputFactoryProvider.createInputFactory() + : XMLInputFactory.newFactory(); } /** - * Returns an instance of {@link javax.xml.stream.XMLInputFactory} - * according to the classpath. + * Returns an instance of {@link javax.xml.stream.XMLInputFactory} according to the classpath. */ public static XMLOutputFactory newOutputFactory() { - return outputFactoryProvider != null ? - outputFactoryProvider.createOutputFactory() : - XMLOutputFactory.newFactory(); + return outputFactoryProvider != null + ? outputFactoryProvider.createOutputFactory() + : XMLOutputFactory.newFactory(); } } diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java index c7d1e90011..afa70a5709 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jackson; import java.util.List; @@ -39,5 +38,4 @@ public void setLastName(String lastName) { public List getInvoices() { return invoices; } - } diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java index 1151102489..0c46b7a397 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jackson; import java.util.Date; @@ -42,5 +41,4 @@ public void setDate(Date date) { public void setPaid(boolean paid) { this.paid = paid; } - } diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java index 1ec6a46c68..b5e7a122aa 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java @@ -1,15 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jackson; +import static org.junit.jupiter.api.Assertions.assertThrows; + import com.fasterxml.jackson.core.JsonParseException; +import java.util.Date; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; @@ -17,10 +19,6 @@ import org.restlet.representation.StringRepresentation; import org.restlet.resource.ClientResource; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.assertThrows; - /** * Unit test for the Jackson extension. * @@ -62,10 +60,13 @@ protected Invoice createInvoice() { @Test public void testCsv() throws Exception { Invoice invoice = createInvoice(); - JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.TEXT_CSV, invoice); + JacksonRepresentation rep = + new JacksonRepresentation<>(MediaType.TEXT_CSV, invoice); String text = rep.getText(); Assertions.assertEquals("12456,1356533333882,false\n", text); - rep = new JacksonRepresentation<>(new StringRepresentation(text, rep.getMediaType()), Invoice.class); + rep = + new JacksonRepresentation<>( + new StringRepresentation(text, rep.getMediaType()), Invoice.class); assertEquals(invoice, rep.getObject()); } @@ -78,30 +79,38 @@ public void testException() throws Exception { // Unless we are in debug mode, hide those properties me.setStackTrace(new StackTraceElement[0]); - JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_JSON, me); + JacksonRepresentation rep = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON, me); - rep = new JacksonRepresentation<>(new StringRepresentation(rep.getText(), rep.getMediaType()), MyException.class); + rep = + new JacksonRepresentation<>( + new StringRepresentation(rep.getText(), rep.getMediaType()), + MyException.class); assertEquals(me, rep.getObject()); } @Test public void testJson() throws Exception { Customer customer = createCustomer(); - JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_JSON, customer); + JacksonRepresentation rep = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON, customer); String text = rep.getText(); Assertions.assertEquals( "{\"firstName\":\"Foo\",\"lastName\":\"Bar\",\"invoices\":[{\"date\":1356533333882,\"amount\":12456,\"paid\":false},{\"date\":1356533333882,\"amount\":7890,\"paid\":true}]}", text); - rep = new JacksonRepresentation<>(new StringRepresentation(text, rep.getMediaType()), Customer.class); + rep = + new JacksonRepresentation<>( + new StringRepresentation(text, rep.getMediaType()), Customer.class); assertEquals(customer, rep.getObject()); } @Test public void testSmile() throws Exception { Customer customer = createCustomer(); - JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_JSON_SMILE, customer); + JacksonRepresentation rep = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON_SMILE, customer); rep = new JacksonRepresentation<>(rep, Customer.class); assertEquals(customer, rep.getObject()); } @@ -109,25 +118,32 @@ public void testSmile() throws Exception { @Test public void testXml() throws Exception { Customer customer = createCustomer(); - JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_XML, customer); + JacksonRepresentation rep = + new JacksonRepresentation<>(MediaType.APPLICATION_XML, customer); String text = rep.getText(); Assertions.assertEquals( "FooBar135653333388212456false13565333338827890true", text); - rep = new JacksonRepresentation<>(new StringRepresentation(text, rep.getMediaType()), Customer.class); + rep = + new JacksonRepresentation<>( + new StringRepresentation(text, rep.getMediaType()), Customer.class); assertEquals(customer, rep.getObject()); } @Test public void testXmlBomb() { - ClientResource cr = new ClientResource("clap://class/org/restlet/ext/jackson/jacksonBomb.xml"); + ClientResource cr = + new ClientResource("clap://class/org/restlet/ext/jackson/jacksonBomb.xml"); Representation xmlRep = cr.get(); xmlRep.setMediaType(MediaType.APPLICATION_XML); - Exception exception = assertThrows(JsonParseException.class, - () -> new JacksonRepresentation<>(xmlRep, Customer.class).getObject()); - String expected = """ + Exception exception = + assertThrows( + JsonParseException.class, + () -> new JacksonRepresentation<>(xmlRep, Customer.class).getObject()); + String expected = + """ Undeclared general entity "lol10" at [row,col {unknown-source}]: [14,31] at [Source: (BufferedInputStream); line: 14, column: 32]"""; @@ -137,10 +153,12 @@ public void testXmlBomb() { @Test public void testYaml() throws Exception { Customer customer = createCustomer(); - JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_YAML, customer); + JacksonRepresentation rep = + new JacksonRepresentation<>(MediaType.APPLICATION_YAML, customer); String text = rep.getText(); - Assertions.assertEquals(""" + Assertions.assertEquals( + """ --- firstName: "Foo" lastName: "Bar" @@ -151,9 +169,12 @@ public void testYaml() throws Exception { - date: 1356533333882 amount: 7890 paid: true - """, text); + """, + text); - rep = new JacksonRepresentation<>(new StringRepresentation(text, rep.getMediaType()), Customer.class); + rep = + new JacksonRepresentation<>( + new StringRepresentation(text, rep.getMediaType()), Customer.class); assertEquals(customer, rep.getObject()); } @@ -161,14 +182,16 @@ protected void assertEquals(Customer customer1, Customer customer2) { Assertions.assertEquals(customer1.getFirstName(), customer2.getFirstName()); Assertions.assertEquals(customer1.getLastName(), customer2.getLastName()); Assertions.assertEquals(customer1.getInvoices().size(), customer2.getInvoices().size()); - Assertions.assertEquals(customer1.getInvoices().get(0).getAmount(), customer2 - .getInvoices().get(0).getAmount()); - Assertions.assertEquals(customer1.getInvoices().get(1).getAmount(), customer2 - .getInvoices().get(1).getAmount()); - Assertions.assertEquals(customer1.getInvoices().get(0).getDate(), customer2 - .getInvoices().get(0).getDate()); - Assertions.assertEquals(customer1.getInvoices().get(1).getDate(), customer2 - .getInvoices().get(1).getDate()); + Assertions.assertEquals( + customer1.getInvoices().get(0).getAmount(), + customer2.getInvoices().get(0).getAmount()); + Assertions.assertEquals( + customer1.getInvoices().get(1).getAmount(), + customer2.getInvoices().get(1).getAmount()); + Assertions.assertEquals( + customer1.getInvoices().get(0).getDate(), customer2.getInvoices().get(0).getDate()); + Assertions.assertEquals( + customer1.getInvoices().get(1).getDate(), customer2.getInvoices().get(1).getDate()); } protected void assertEquals(Invoice invoice1, Invoice invoice2) { diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java index f900aa764d..a89a5119d8 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.jackson; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -@JsonIgnoreProperties({ "cause", "localizedMessage", "suppressed", "message" }) +@JsonIgnoreProperties({"cause", "localizedMessage", "suppressed", "message"}) public class MyException extends Exception { private static final long serialVersionUID = 1L; @@ -20,16 +19,18 @@ public class MyException extends Exception { private String errorCode; - public MyException() { - } + public MyException() {} public MyException(Customer customer, String errorCode) { - this(customer, errorCode, "Customer exception detected", null, true, - true); + this(customer, errorCode, "Customer exception detected", null, true, true); } - public MyException(Customer customer, String errorCode, String message, - Throwable cause, boolean enableSuppression, + public MyException( + Customer customer, + String errorCode, + String message, + Throwable cause, + boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); this.customer = customer; @@ -51,5 +52,4 @@ public void setCustomer(Customer customer) { public void setErrorCode(String errorCode) { this.errorCode = errorCode; } - } diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java index fc9016babe..a08024fa08 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.json; import java.io.IOException; import java.util.List; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -25,16 +23,14 @@ import org.restlet.resource.Resource; /** - * Converter between the JSON API (more precisely {@link JSONArray}, - * {@link JSONObject} and {@link JSONTokener} instances) and Representation - * classes. - * + * Converter between the JSON API (more precisely {@link JSONArray}, {@link JSONObject} and {@link + * JSONTokener} instances) and Representation classes. + * * @author Jerome Louvel */ public class JsonConverter extends ConverterHelper { - private static final VariantInfo VARIANT_JSON = new VariantInfo( - MediaType.APPLICATION_JSON); + private static final VariantInfo VARIANT_JSON = new VariantInfo(MediaType.APPLICATION_JSON); @Override public List> getObjectClasses(Variant source) { @@ -68,12 +64,12 @@ public List getVariants(Class source) { public float score(Object source, Variant target, Resource resource) { float result = -1.0F; - if ((source instanceof JSONArray) || (source instanceof JSONObject) + if ((source instanceof JSONArray) + || (source instanceof JSONObject) || (source instanceof JSONTokener)) { if (target == null) { result = 0.5F; - } else if (MediaType.APPLICATION_JSON.isCompatible(target - .getMediaType())) { + } else if (MediaType.APPLICATION_JSON.isCompatible(target.getMediaType())) { result = 1.0F; } else { result = 0.5F; @@ -84,30 +80,26 @@ public float score(Object source, Variant target, Resource resource) { } @Override - public float score(Representation source, Class target, - Resource resource) { + public float score(Representation source, Class target, Resource resource) { float result = -1.0F; if (target != null) { if (JsonRepresentation.class.isAssignableFrom(target)) { result = 1.0F; } else if (JSONArray.class.isAssignableFrom(target)) { - if (MediaType.APPLICATION_JSON.isCompatible(source - .getMediaType())) { + if (MediaType.APPLICATION_JSON.isCompatible(source.getMediaType())) { result = 1.0F; } else { result = 0.5F; } } else if (JSONObject.class.isAssignableFrom(target)) { - if (MediaType.APPLICATION_JSON.isCompatible(source - .getMediaType())) { + if (MediaType.APPLICATION_JSON.isCompatible(source.getMediaType())) { result = 1.0F; } else { result = 0.5F; } } else if (JSONTokener.class.isAssignableFrom(target)) { - if (MediaType.APPLICATION_JSON.isCompatible(source - .getMediaType())) { + if (MediaType.APPLICATION_JSON.isCompatible(source.getMediaType())) { result = 1.0F; } else { result = 0.5F; @@ -119,8 +111,8 @@ public float score(Representation source, Class target, } @Override - public T toObject(Representation source, Class target, - Resource resource) throws IOException { + public T toObject(Representation source, Class target, Resource resource) + throws IOException { JsonRepresentation jsonSource = null; if (source instanceof JsonRepresentation) { jsonSource = (JsonRepresentation) source; @@ -134,16 +126,14 @@ public T toObject(Representation source, Class target, try { result = target.cast(jsonSource.getJsonArray()); } catch (JSONException e) { - IOException ioe = new IOException( - "Unable to convert to JSON array"); + IOException ioe = new IOException("Unable to convert to JSON array"); ioe.initCause(e); } } else if (JSONObject.class.isAssignableFrom(target)) { try { result = target.cast(jsonSource.getJsonObject()); } catch (JSONException e) { - IOException ioe = new IOException( - "Unable to convert to JSON object"); + IOException ioe = new IOException("Unable to convert to JSON object"); ioe.initCause(e); throw ioe; } @@ -151,8 +141,7 @@ public T toObject(Representation source, Class target, try { result = target.cast(jsonSource.getJsonTokener()); } catch (JSONException e) { - IOException ioe = new IOException( - "Unable to convert to JSON tokener"); + IOException ioe = new IOException("Unable to convert to JSON tokener"); ioe.initCause(e); throw ioe; } @@ -165,8 +154,7 @@ public T toObject(Representation source, Class target, } @Override - public Representation toRepresentation(Object source, Variant target, - Resource resource) { + public Representation toRepresentation(Object source, Variant target, Resource resource) { Representation result = null; if (source instanceof JSONArray) { @@ -178,17 +166,14 @@ public Representation toRepresentation(Object source, Variant target, } return result; - } @Override - public void updatePreferences(List> preferences, - Class entity) { + public void updatePreferences(List> preferences, Class entity) { if (JSONArray.class.isAssignableFrom(entity) || JSONObject.class.isAssignableFrom(entity) || JSONTokener.class.isAssignableFrom(entity)) { updatePreferences(preferences, MediaType.APPLICATION_JSON, 1.0F); } } - } diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java index f7e95a68ee..40fc922a2b 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java @@ -1,18 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.json; import java.io.IOException; import java.io.Writer; import java.util.Map; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -25,9 +23,9 @@ import org.restlet.representation.WriterRepresentation; /** - * Representation based on a JSON document. JSON stands for JavaScript Object - * Notation and is a lightweight data-interchange format. - * + * Representation based on a JSON document. JSON stands for JavaScript Object Notation and is a + * lightweight data-interchange format. + * * @author Jerome Louvel * @see JSON home */ @@ -47,9 +45,8 @@ public class JsonRepresentation extends WriterRepresentation { /** * Constructor from a JSON array. - * - * @param jsonArray - * The JSON array. + * + * @param jsonArray The JSON array. */ public JsonRepresentation(JSONArray jsonArray) { super(MediaType.APPLICATION_JSON); @@ -58,9 +55,8 @@ public JsonRepresentation(JSONArray jsonArray) { /** * Constructor from a JSON object. - * - * @param jsonObject - * The JSON object. + * + * @param jsonObject The JSON object. */ public JsonRepresentation(JSONObject jsonObject) { super(MediaType.APPLICATION_JSON); @@ -69,9 +65,8 @@ public JsonRepresentation(JSONObject jsonObject) { /** * Constructor from a JSON stringer. - * - * @param jsonStringer - * The JSON stringer. + * + * @param jsonStringer The JSON stringer. */ public JsonRepresentation(JSONStringer jsonStringer) { super(MediaType.APPLICATION_JSON); @@ -80,9 +75,8 @@ public JsonRepresentation(JSONStringer jsonStringer) { /** * Constructor from a JSON tokener. - * - * @param jsonTokener - * The JSON tokener. + * + * @param jsonTokener The JSON tokener. */ public JsonRepresentation(JSONTokener jsonTokener) { super(MediaType.APPLICATION_JSON); @@ -91,9 +85,8 @@ public JsonRepresentation(JSONTokener jsonTokener) { /** * Constructor from a map object. - * - * @param map - * The map to convert to JSON. + * + * @param map The map to convert to JSON. * @see org.json.JSONObject#JSONObject(Map) */ public JsonRepresentation(Map map) { @@ -102,9 +95,8 @@ public JsonRepresentation(Map map) { /** * Constructor from a bean using reflection to generate JSON names. - * - * @param bean - * The bean to convert to JSON. + * + * @param bean The bean to convert to JSON. * @see org.json.JSONObject#JSONObject(Object) */ public JsonRepresentation(Object bean) { @@ -113,21 +105,18 @@ public JsonRepresentation(Object bean) { /** * Constructor. - * - * @param jsonRepresentation - * A source JSON representation to parse. + * + * @param jsonRepresentation A source JSON representation to parse. */ public JsonRepresentation(Representation jsonRepresentation) { - super((jsonRepresentation == null) ? null : jsonRepresentation - .getMediaType()); + super((jsonRepresentation == null) ? null : jsonRepresentation.getMediaType()); this.jsonRepresentation = jsonRepresentation; } /** * Constructor from a JSON string. - * - * @param jsonString - * The JSON string. + * + * @param jsonString The JSON string. */ public JsonRepresentation(String jsonString) { super(MediaType.APPLICATION_JSON); @@ -137,7 +126,7 @@ public JsonRepresentation(String jsonString) { /** * Returns the number of spaces to use for indentation. - * + * * @return The number of spaces to use for indentation. */ public int getIndentingSize() { @@ -145,9 +134,8 @@ public int getIndentingSize() { } /** - * Gets the wrapped JSON array or converts the wrapped representation if - * needed. - * + * Gets the wrapped JSON array or converts the wrapped representation if needed. + * * @return The converted JSON array. * @throws JSONException */ @@ -160,9 +148,8 @@ public JSONArray getJsonArray() throws JSONException { } /** - * Gets the wrapped JSON object or converts the wrapped representation if - * needed. - * + * Gets the wrapped JSON object or converts the wrapped representation if needed. + * * @return The converted JSON object. * @throws JSONException */ @@ -176,7 +163,7 @@ public JSONObject getJsonObject() throws JSONException { /** * Returns the JSON text for the wrapped JSON object or representation. - * + * * @return The JSON text. * @throws JSONException */ @@ -219,9 +206,8 @@ private String getJsonText() throws JSONException { } /** - * Gets the wrapped JSON tokener or converts the wrapped representation if - * needed. - * + * Gets the wrapped JSON tokener or converts the wrapped representation if needed. + * * @return The converted JSON tokener. * @throws JSONException */ @@ -242,7 +228,6 @@ public long getSize() { } /** - * * @param jsonObject */ private void init(Object jsonObject) { @@ -254,7 +239,7 @@ private void init(Object jsonObject) { /** * Indicates if JSON objects and arrays should be indented. - * + * * @return True if JSON objects and arrays should be indented. */ public boolean isIndenting() { @@ -263,9 +248,8 @@ public boolean isIndenting() { /** * Indicates if JSON objects and arrays should be indented. - * - * @param indenting - * True if JSON objects and arrays should be indented. + * + * @param indenting True if JSON objects and arrays should be indented. */ public void setIndenting(boolean indenting) { this.indenting = indenting; @@ -273,9 +257,8 @@ public void setIndenting(boolean indenting) { /** * Sets the number of spaces to use for indentation. - * - * @param indentFactor - * The number of spaces to use for indentation. + * + * @param indentFactor The number of spaces to use for indentation. */ public void setIndentingSize(int indentFactor) { this.indentingSize = indentFactor; diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java index 81b209678a..8ad1e043df 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.json; import org.restlet.Context; @@ -18,49 +17,45 @@ import org.restlet.routing.Filter; /** - * Filter that converts response entity of the JSON media type into a JSONP - * callback document. Make sure that you properly pass a "callback" query - * parameter in the URI query string with the name of your JavaScrip callback - * method. - * - * See {@link JsonpRepresentation} for the actual wrapper representation used - * internally. - * + * Filter that converts response entity of the JSON media type into a JSONP callback document. Make + * sure that you properly pass a "callback" query parameter in the URI query string with the name of + * your JavaScrip callback method. + * + *

See {@link JsonpRepresentation} for the actual wrapper representation used internally. + * * @author Mark Kharitonov */ public class JsonpFilter extends Filter { /** * Constructor. - * - * @param context - * The context. + * + * @param context The context. */ public JsonpFilter(Context context) { super(context); } /** - * Assumes that there is a "callback" query parameter available in the URI - * query string, containing the name of the JavaScript callback method. + * Assumes that there is a "callback" query parameter available in the URI query string, + * containing the name of the JavaScript callback method. */ @Override public void afterHandle(Request request, Response response) { // Check the presence of the callback parameter - String callback = request.getResourceRef().getQueryAsForm() - .getFirstValue("callback"); + String callback = request.getResourceRef().getQueryAsForm().getFirstValue("callback"); if (callback != null) { Representation entity = response.getEntity(); if (entity != null - && ("text".equals(entity.getMediaType().getMainType()) || MediaType.APPLICATION_JSON - .equals(entity.getMediaType()))) { - response.setEntity(new JsonpRepresentation(callback, response - .getStatus(), response.getEntity())); + && ("text".equals(entity.getMediaType().getMainType()) + || MediaType.APPLICATION_JSON.equals(entity.getMediaType()))) { + response.setEntity( + new JsonpRepresentation( + callback, response.getStatus(), response.getEntity())); response.setStatus(Status.SUCCESS_OK); } } } - -} \ No newline at end of file +} diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java index 9e78a938bb..2fd58226ad 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java @@ -1,16 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.json; import java.io.IOException; - import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.engine.io.IoUtils; @@ -18,10 +16,9 @@ import org.restlet.representation.WriterRepresentation; /** - * Wrappers that adds a JSONP header and footer to JSON representations. The - * goal is to make them accessible to web browser without restriction from - * single origin policies. - * + * Wrappers that adds a JSONP header and footer to JSON representations. The goal is to make them + * accessible to web browser without restriction from single origin policies. + * * @author Mark Kharitonov * @author Jerome Louvel */ @@ -37,15 +34,13 @@ public class JsonpRepresentation extends WriterRepresentation { /** * Constructor. - * - * @param callback - * The name of the JavaScript callback method. - * @param status - * The actual status code. + * + * @param callback The name of the JavaScript callback method. + * @param status The actual status code. * @param wrappedRepresentation */ - public JsonpRepresentation(String callback, Status status, - Representation wrappedRepresentation) { + public JsonpRepresentation( + String callback, Status status, Representation wrappedRepresentation) { super(MediaType.APPLICATION_JAVASCRIPT); this.callback = callback; this.status = status; @@ -54,7 +49,7 @@ public JsonpRepresentation(String callback, Status status, /** * Returns the name of the JavaScript callback method. - * + * * @return The name of the JavaScript callback method. */ public String getCallback() { @@ -65,9 +60,7 @@ public String getCallback() { public long getSize() { long result = wrappedRepresentation.getSize(); - if (result > 0 - && MediaType.APPLICATION_JSON.equals(wrappedRepresentation - .getMediaType())) { + if (result > 0 && MediaType.APPLICATION_JSON.equals(wrappedRepresentation.getMediaType())) { try { java.io.StringWriter sw = new java.io.StringWriter(); write(sw); @@ -83,7 +76,7 @@ public long getSize() { /** * Returns the actual status code. - * + * * @return The actual status code. */ public Status getStatus() { @@ -97,8 +90,7 @@ public void write(java.io.Writer writer) throws IOException { writer.write(Integer.toString(getStatus().getCode())); writer.write(",\"body\":"); - if (MediaType.APPLICATION_JSON.equals(wrappedRepresentation - .getMediaType())) { + if (MediaType.APPLICATION_JSON.equals(wrappedRepresentation.getMediaType())) { IoUtils.copy(wrappedRepresentation.getReader(), writer); } else { writer.write("\""); @@ -114,5 +106,4 @@ public void write(java.io.Writer writer) throws IOException { writer.write("});"); } - } diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java index 3b7c19f718..b355045015 100644 --- a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java @@ -1,14 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.json; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.restlet.data.Status.SUCCESS_OK; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.restlet.Request; @@ -19,10 +22,6 @@ import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.restlet.data.Status.SUCCESS_OK; - /** * Test case for the {@link JsonpFilter} class. * @@ -46,8 +45,8 @@ public void testAfterHandle() { filter.afterHandle(request, response); Representation actual = response.getEntity(); - Representation expected = new JsonpRepresentation(callback, SUCCESS_OK, - new JsonRepresentation(jsonString)); + Representation expected = + new JsonpRepresentation(callback, SUCCESS_OK, new JsonRepresentation(jsonString)); assertInstanceOf(JsonpRepresentation.class, actual); assertEquals(expected, actual); @@ -70,8 +69,11 @@ public void testAfterHandleText() { filter.afterHandle(request, response); Representation actual = response.getEntity(); - Representation expected = new JsonpRepresentation(callback, SUCCESS_OK, - new StringRepresentation(jsonString, MediaType.TEXT_HTML)); + Representation expected = + new JsonpRepresentation( + callback, + SUCCESS_OK, + new StringRepresentation(jsonString, MediaType.TEXT_HTML)); assertInstanceOf(JsonpRepresentation.class, actual); assertEquals(expected, actual); @@ -108,8 +110,8 @@ public void testAfterHandle_with_other_mediatype_should_return_entity_unchanged( ref.addQueryParameter(callback, "test"); Request request = new Request(Method.GET, ref); Response response = new Response(request); - final StringRepresentation expected = new StringRepresentation("", - MediaType.APPLICATION_XML); + final StringRepresentation expected = + new StringRepresentation("", MediaType.APPLICATION_XML); response.setEntity(expected); filter.afterHandle(request, response); diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java index b2c966baa7..fe6852972d 100644 --- a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java @@ -1,27 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.json; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.restlet.data.Status.SUCCESS_OK; + +import java.io.ByteArrayOutputStream; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import java.io.ByteArrayOutputStream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.restlet.data.Status.SUCCESS_OK; - /** * Test case for the {@link JsonpRepresentation} class. - * + * * @author Cyril Lakech */ public class JsonpRepresentationTestCase { @@ -34,23 +32,27 @@ public class JsonpRepresentationTestCase { @Test public void testGetSizeJson() { - JsonpRepresentation jsonpRepresentation = new JsonpRepresentation( - CALLBACK, SUCCESS_OK, new JsonRepresentation(JSON_SAMPLE)); + JsonpRepresentation jsonpRepresentation = + new JsonpRepresentation(CALLBACK, SUCCESS_OK, new JsonRepresentation(JSON_SAMPLE)); long actual = jsonpRepresentation.getSize(); - long expected = JSON_SAMPLE.length() - + Integer.toString(SUCCESS_OK.getCode()).length() - + CALLBACK.length() + JSONP_STATUS_BODY.length(); + long expected = + JSON_SAMPLE.length() + + Integer.toString(SUCCESS_OK.getCode()).length() + + CALLBACK.length() + + JSONP_STATUS_BODY.length(); assertEquals(expected, actual); } @Test public void testGetSize_with_text_is_UNKNOWN_SIZE() { - JsonpRepresentation jsonpRepresentation = new JsonpRepresentation( - CALLBACK, SUCCESS_OK, new StringRepresentation(JSON_SAMPLE, - MediaType.TEXT_HTML)); + JsonpRepresentation jsonpRepresentation = + new JsonpRepresentation( + CALLBACK, + SUCCESS_OK, + new StringRepresentation(JSON_SAMPLE, MediaType.TEXT_HTML)); long actual = jsonpRepresentation.getSize(); @@ -61,8 +63,8 @@ CALLBACK, SUCCESS_OK, new StringRepresentation(JSON_SAMPLE, @Test public void testWrite() throws Exception { - JsonpRepresentation jsonpRepresentation = new JsonpRepresentation( - CALLBACK, SUCCESS_OK, new JsonRepresentation(JSON_SAMPLE)); + JsonpRepresentation jsonpRepresentation = + new JsonpRepresentation(CALLBACK, SUCCESS_OK, new JsonRepresentation(JSON_SAMPLE)); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -76,11 +78,13 @@ public void testWrite() throws Exception { // with a text representation, apostrophes are escaped and the text is embedded // between 2 apostrophes @Test - public void testWrite_with_text_then_apostrophe_are_escaped() - throws Exception { - JsonpRepresentation jsonpRepresentation = new JsonpRepresentation( - CALLBACK, SUCCESS_OK, new StringRepresentation( - "whatever\"with\"apostrophe", MediaType.TEXT_HTML)); + public void testWrite_with_text_then_apostrophe_are_escaped() throws Exception { + JsonpRepresentation jsonpRepresentation = + new JsonpRepresentation( + CALLBACK, + SUCCESS_OK, + new StringRepresentation( + "whatever\"with\"apostrophe", MediaType.TEXT_HTML)); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -90,5 +94,4 @@ CALLBACK, SUCCESS_OK, new StringRepresentation( assertEquals(expected, out.toString()); } - } diff --git a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java index 063803e182..508bcca95b 100644 --- a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java +++ b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.slf4j; import java.util.logging.Level; @@ -14,10 +13,9 @@ import java.util.logging.Logger; /** - * JULI logger that efficiently wraps a SLF4J logger. It prevents the creation - * of intermediary {@link LogRecord} objects in favor of direct calls to the - * SLF4J API. - * + * JULI logger that efficiently wraps a SLF4J logger. It prevents the creation of intermediary + * {@link LogRecord} objects in favor of direct calls to the SLF4J API. + * * @author Jerome Louvel */ public class Slf4jLogger extends Logger { @@ -27,9 +25,8 @@ public class Slf4jLogger extends Logger { /** * Constructor. - * - * @param slf4jLogger - * The SLF4J logger to wrap. + * + * @param slf4jLogger The SLF4J logger to wrap. */ public Slf4jLogger(org.slf4j.Logger slf4jLogger) { super(slf4jLogger.getName(), null); @@ -38,22 +35,18 @@ public Slf4jLogger(org.slf4j.Logger slf4jLogger) { /** * Constructor. - * - * @param name - * The logger name. - * @param resourceBundleName - * The optional resource bundle name. + * + * @param name The logger name. + * @param resourceBundleName The optional resource bundle name. */ protected Slf4jLogger(String name, String resourceBundleName) { super(name, resourceBundleName); } /** - * Logs a configuration message. By default, it invokes - * {@link org.slf4j.Logger#debug(String)}. - * - * @param msg - * The message to log. + * Logs a configuration message. By default, it invokes {@link org.slf4j.Logger#debug(String)}. + * + * @param msg The message to log. */ @Override public void config(String msg) { @@ -61,11 +54,9 @@ public void config(String msg) { } /** - * Logs a fine trace. By default, it invokes - * {@link org.slf4j.Logger#debug(String)}. - * - * @param msg - * The message to log. + * Logs a fine trace. By default, it invokes {@link org.slf4j.Logger#debug(String)}. + * + * @param msg The message to log. */ @Override public void fine(String msg) { @@ -73,11 +64,9 @@ public void fine(String msg) { } /** - * Logs a finer trace. By default, it invokes - * {@link org.slf4j.Logger#trace(String)}. - * - * @param msg - * The message to log. + * Logs a finer trace. By default, it invokes {@link org.slf4j.Logger#trace(String)}. + * + * @param msg The message to log. */ @Override public void finer(String msg) { @@ -85,11 +74,9 @@ public void finer(String msg) { } /** - * Logs a finest trace. By default, it invokes - * {@link org.slf4j.Logger#trace(String)}. - * - * @param msg - * The message to log. + * Logs a finest trace. By default, it invokes {@link org.slf4j.Logger#trace(String)}. + * + * @param msg The message to log. */ @Override public void finest(String msg) { @@ -98,7 +85,7 @@ public void finest(String msg) { /** * Returns the wrapped SLF4J logger. - * + * * @return The wrapped SLF4J logger. */ public org.slf4j.Logger getSlf4jLogger() { @@ -106,11 +93,9 @@ public org.slf4j.Logger getSlf4jLogger() { } /** - * Logs an info message. By default, it invokes - * {@link org.slf4j.Logger#info(String)}. - * - * @param msg - * The message to log. + * Logs an info message. By default, it invokes {@link org.slf4j.Logger#info(String)}. + * + * @param msg The message to log. */ @Override public void info(String msg) { @@ -236,20 +221,17 @@ public void log(LogRecord record) { /** * Sets the wrapped SLF4J logger. - * - * @param slf4jLogger - * The wrapped SLF4J logger. + * + * @param slf4jLogger The wrapped SLF4J logger. */ public void setSlf4jLogger(org.slf4j.Logger slf4jLogger) { this.slf4jLogger = slf4jLogger; } /** - * Logs a severe message. By default, it invokes - * {@link org.slf4j.Logger#error(String)}. - * - * @param msg - * The message to log. + * Logs a severe message. By default, it invokes {@link org.slf4j.Logger#error(String)}. + * + * @param msg The message to log. */ @Override public void severe(String msg) { @@ -257,15 +239,12 @@ public void severe(String msg) { } /** - * Logs a warning message. By default, it invokes - * {@link org.slf4j.Logger#warn(String)}. - * - * @param msg - * The message to log. + * Logs a warning message. By default, it invokes {@link org.slf4j.Logger#warn(String)}. + * + * @param msg The message to log. */ @Override public void warning(String msg) { getSlf4jLogger().warn(msg); } - } diff --git a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java index e597b2ed23..6e7e2652c3 100644 --- a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java +++ b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java @@ -1,34 +1,31 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.slf4j; import java.util.logging.Logger; - import org.restlet.engine.log.LoggerFacade; import org.slf4j.LoggerFactory; /** - * Restlet log facade for the SLF4J {@link LoggerFactory}. In order to use SLF4J - * as the logging facade for Restlet, you need to set the - * "org.restlet.engine.loggerFacadeClass" system property with the + * Restlet log facade for the SLF4J {@link LoggerFactory}. To use SLF4J as the logging facade for + * Restlet, you need to set the "org.restlet.engine.loggerFacadeClass" system property with the * "org.restlet.ext.slf4j.Slf4jLoggerFacade" value. - * + * * @see Slf4jLogger * @author Jerome Louvel */ public class Slf4jLoggerFacade extends LoggerFacade { /** - * Returns an instance of {@link Slf4jLogger}, wrapping the result of - * {@link LoggerFactory#getLogger(String)} where the logger name is "". - * + * Returns an instance of {@link Slf4jLogger}, wrapping the result of {@link + * LoggerFactory#getLogger(String)} where the logger name is "". + * * @return An anonymous logger. */ @Override @@ -37,16 +34,14 @@ public Logger getAnonymousLogger() { } /** - * Returns an instance of {@link Slf4jLogger}, wrapping the result of - * {@link LoggerFactory#getLogger(String)} with the logger name. - * - * @param loggerName - * The logger name. + * Returns an instance of {@link Slf4jLogger}, wrapping the result of {@link + * LoggerFactory#getLogger(String)} with the logger name. + * + * @param loggerName The logger name. * @return An anonymous logger. */ @Override public Logger getLogger(String loggerName) { return new Slf4jLogger(LoggerFactory.getLogger(loggerName)); } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java index ef7d2ffcf9..3dba7e7376 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import org.restlet.Context; @@ -18,21 +17,20 @@ import org.springframework.context.ApplicationContextAware; /** - * An alternative to {@link SpringFinder} which uses Spring's BeanFactory - * mechanism to load a prototype bean by name. - * - * If both a {@link BeanFactory} and a {@link ApplicationContext} are provided, - * the bean will be looked up first in the application context and then in the - * bean factory. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * An alternative to {@link SpringFinder} which uses Spring's BeanFactory mechanism to load a + * prototype bean by name. + * + *

If both a {@link BeanFactory} and a {@link ApplicationContext} are provided, the bean will be + * looked up first in the application context and then in the bean factory. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Rhett Sutphin */ -public class SpringBeanFinder extends SpringFinder implements BeanFactoryAware, - ApplicationContextAware { +public class SpringBeanFinder extends SpringFinder + implements BeanFactoryAware, ApplicationContextAware { /** The parent application context. */ private volatile ApplicationContext applicationContext; @@ -46,24 +44,17 @@ public class SpringBeanFinder extends SpringFinder implements BeanFactoryAware, /** The associated router. */ private volatile Router router; - /** - * Default constructor. - */ - public SpringBeanFinder() { - } + /** Default constructor. */ + public SpringBeanFinder() {} /** * Constructor. - * - * @param router - * The associated router used to retrieve the context. - * @param beanFactory - * The Spring bean factory. - * @param beanName - * The bean name. + * + * @param router The associated router used to retrieve the context. + * @param beanFactory The Spring bean factory. + * @param beanName The bean name. */ - public SpringBeanFinder(Router router, BeanFactory beanFactory, - String beanName) { + public SpringBeanFinder(Router router, BeanFactory beanFactory, String beanName) { this.router = router; setBeanFactory(beanFactory); setBeanName(beanName); @@ -74,9 +65,10 @@ public ServerResource create() { final Object resource = findBean(); if (!(resource instanceof ServerResource)) { - throw new ClassCastException(getBeanName() - + " does not resolve to an instance of " - + org.restlet.resource.ServerResource.class.getName()); + throw new ClassCastException( + getBeanName() + + " does not resolve to an instance of " + + org.restlet.resource.ServerResource.class.getName()); } return (org.restlet.resource.ServerResource) resource; @@ -89,18 +81,17 @@ private Object findBean() { } else if (getApplicationContext() != null && getApplicationContext().containsBean(getBeanName())) { return getApplicationContext().getBean(getBeanName()); - } else if (getBeanFactory() != null - && getBeanFactory().containsBean(getBeanName())) { + } else if (getBeanFactory() != null && getBeanFactory().containsBean(getBeanName())) { return getBeanFactory().getBean(getBeanName()); } else { - throw new IllegalStateException(String.format( - "No bean named %s present.", getBeanName())); + throw new IllegalStateException( + String.format("No bean named %s present.", getBeanName())); } } /** * Returns the parent application context. - * + * * @return The parent context. */ public ApplicationContext getApplicationContext() { @@ -109,7 +100,7 @@ public ApplicationContext getApplicationContext() { /** * Returns the parent bean factory. - * + * * @return The parent bean factory. */ public BeanFactory getBeanFactory() { @@ -118,7 +109,7 @@ public BeanFactory getBeanFactory() { /** * Returns the bean name. - * + * * @return The bean name. */ public String getBeanName() { @@ -127,13 +118,12 @@ public String getBeanName() { @Override public Context getContext() { - return (getRouter() == null) ? Context.getCurrent() : getRouter() - .getContext(); + return (getRouter() == null) ? Context.getCurrent() : getRouter().getContext(); } /** * Returns the associated router. - * + * * @return The associated router. */ public Router getRouter() { @@ -142,9 +132,8 @@ public Router getRouter() { /** * Sets the parent application context - * - * @param applicationContext - * The parent context. + * + * @param applicationContext The parent context. */ public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; @@ -152,9 +141,8 @@ public void setApplicationContext(ApplicationContext applicationContext) { /** * Sets the parent bean factory. - * - * @param beanFactory - * The parent bean factory. + * + * @param beanFactory The parent bean factory. */ public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; @@ -162,9 +150,8 @@ public void setBeanFactory(BeanFactory beanFactory) { /** * Sets the bean name. - * - * @param beanName - * The bean name. + * + * @param beanName The bean name. */ public void setBeanName(String beanName) { this.beanName = beanName; @@ -172,12 +159,10 @@ public void setBeanName(String beanName) { /** * Sets the associated router. - * - * @param router - * The associated router. + * + * @param router The associated router. */ public void setRouter(Router router) { this.router = router; } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java index a25f23c26d..702f97d7e7 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java @@ -1,16 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.util.Map; - import org.restlet.Context; import org.restlet.Restlet; import org.restlet.resource.Finder; @@ -26,48 +24,46 @@ import org.springframework.context.ApplicationContextAware; /** - * Restlet {@link Router} which behaves like Spring's - * {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}. It - * takes every bean of type {@link org.restlet.resource.ServerResource} or - * {@link Restlet} defined in a particular context and examines its aliases - * (generally speaking, its name and id). If one of the aliases begins with a - * forward slash, the resource will be attached to that URI. - *

- * Example: - * + * Restlet {@link Router} which behaves like Spring's {@link + * org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}. It takes every bean of type + * {@link org.restlet.resource.ServerResource} or {@link Restlet} defined in a particular context + * and examines its aliases (generally speaking, its name and id). If one of the aliases begins with + * a forward slash, the resource will be attached to that URI. + * + *

Example: + * *

  * <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" >
  *    <!-- Singleton instance of this class -->
  *    <bean name="router" class="org.restlet.ext.spring.SpringBeanRouter"/>
- * 
+ *
  *    <!-- Prototype beans for the resources -->
  *    <bean name="/studies" id="studiesResource" autowire="byName" scope="prototype" class="edu.northwestern.myapp.StudiesResource" >
  *       <property name="studyDao" ref="studyDao"/>
  *    </bean>
- * 
+ *
  *    <bean name="/studies/{study-identifier}/template" id="templateResource" autowire="byName" scope="prototype" class="edu.northwestern.myapp.TemplateResource" />
- * 
+ *
  *    <!-- Singleton bean for a restlet -->
  *    <bean name="/studies/{study-identifier}/files" id="filesResource" autowire="byName" class="edu.northwestern.myapp.MyDirectory" />
  * </beans>
  * 
- * - * This will route two resources and one restlet: "/studies", - * "/studies/{study-identifier}/template", and - * "/studies/{study-identifier}/files" to the corresponding beans. - * N.b.: Resources must be scoped prototype, since a new instance must be - * created for each request. Restlets may be singletons (this class will only - * ever load one instance for each). - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + * This will route two resources and one restlet: "/studies", + * "/studies/{study-identifier}/template", and "/studies/{study-identifier}/files" + * to the corresponding beans. N.b.: Resources must be scoped prototype, since a new + * instance must be created for each request. Restlets may be singletons (this class will only ever + * load one instance for each). + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Rhett Sutphin * @author James Maki */ -public class SpringBeanRouter extends Router implements - ApplicationContextAware, BeanFactoryPostProcessor { +public class SpringBeanRouter extends Router + implements ApplicationContextAware, BeanFactoryPostProcessor { /** The Spring application context. */ private volatile ApplicationContext applicationContext; @@ -78,97 +74,76 @@ public class SpringBeanRouter extends Router implements /** If beans should be searched for higher up in the BeanFactory hierarchy. */ private volatile boolean findingInAncestors = true; - /** - * Constructor. - */ + /** Constructor. */ public SpringBeanRouter() { super(); } - /** - * Constructor with a parent context. - */ + /** Constructor with a parent context. */ public SpringBeanRouter(Context context) { super(context); } - /** - * Constructor with a parent Restlet. - */ + /** Constructor with a parent Restlet. */ public SpringBeanRouter(Restlet parent) { super(parent.getContext()); } /** * Attaches all the resources. - * - * @param beanFactory - * The Spring bean factory. + * + * @param beanFactory The Spring bean factory. */ private void attachAllResources(ListableBeanFactory beanFactory) { - for (String beanName : getBeanNamesByType( - org.restlet.resource.ServerResource.class, beanFactory)) { + for (String beanName : + getBeanNamesByType(org.restlet.resource.ServerResource.class, beanFactory)) { String uri = resolveUri(beanName, beanFactory); - if (uri != null) - attachResource(uri, beanName, beanFactory); + if (uri != null) attachResource(uri, beanName, beanFactory); } } /** * Attaches all the Restlet instances. - * - * @param beanFactory - * The Spring bean factory. + * + * @param beanFactory The Spring bean factory. */ private void attachAllRestlets(ListableBeanFactory beanFactory) { for (String beanName : getBeanNamesByType(Restlet.class, beanFactory)) { String uri = resolveUri(beanName, beanFactory); - if (uri != null) - attachRestlet(uri, beanName, beanFactory); + if (uri != null) attachRestlet(uri, beanName, beanFactory); } } /** - * Attaches the named resource bean at the given URI, creating a finder for - * it via {@link #createFinder(BeanFactory, String)}. - * - * @param uri - * The attachment URI. - * @param beanName - * The bean name. - * @param beanFactory - * The Spring bean factory. + * Attaches the named resource bean at the given URI, creating a finder for it via {@link + * #createFinder(BeanFactory, String)}. + * + * @param uri The attachment URI. + * @param beanName The bean name. + * @param beanFactory The Spring bean factory. */ - protected void attachResource(String uri, String beanName, - BeanFactory beanFactory) { + protected void attachResource(String uri, String beanName, BeanFactory beanFactory) { attach(uri, createFinder(beanFactory, beanName)); } /** * Attaches the named restlet bean directly at the given URI. - * - * @param uri - * The attachment URI. - * @param beanName - * The bean name. - * @param beanFactory - * The Spring bean factory. + * + * @param uri The attachment URI. + * @param beanName The bean name. + * @param beanFactory The Spring bean factory. */ - protected void attachRestlet(String uri, String beanName, - BeanFactory beanFactory) { + protected void attachRestlet(String uri, String beanName, BeanFactory beanFactory) { attach(uri, (Restlet) beanFactory.getBean(beanName)); } /** - * Creates an instance of {@link SpringBeanFinder}. This can be overridden - * if necessary. - * - * @param beanFactory - * The Spring bean factory. - * @param beanName - * The bean name. + * Creates an instance of {@link SpringBeanFinder}. This can be overridden if necessary. + * + * @param beanFactory The Spring bean factory. + * @param beanName The bean name. * @see #attachResource */ protected Finder createFinder(BeanFactory beanFactory, String beanName) { @@ -177,7 +152,7 @@ protected Finder createFinder(BeanFactory beanFactory, String beanName) { /** * Returns supplemental explicit mappings - * + * * @return Supplemental explicit mappings */ protected Map getAttachments() { @@ -186,72 +161,62 @@ protected Map getAttachments() { /** * Returns the list of bean name for the given type. - * - * @param beanClass - * The bean class to lookup. - * @param beanFactory - * The Spring bean factory. + * + * @param beanClass The bean class to lookup. + * @param beanFactory The Spring bean factory. * @return The array of bean names. */ - private String[] getBeanNamesByType(Class beanClass, - ListableBeanFactory beanFactory) { - return isFindingInAncestors() ? BeanFactoryUtils - .beanNamesForTypeIncludingAncestors(beanFactory, beanClass, - true, true) : beanFactory.getBeanNamesForType( - beanClass, true, true); + private String[] getBeanNamesByType(Class beanClass, ListableBeanFactory beanFactory) { + return isFindingInAncestors() + ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + beanFactory, beanClass, true, true) + : beanFactory.getBeanNamesForType(beanClass, true, true); } /** * Indicates if the attachments contain a mapping for the given URI. - * - * @param name - * The name to test. + * + * @param name The name to test. * @return True if the attachments contain a mapping for the given URI. */ private boolean isAvailableUri(String name) { return name.startsWith("/") - && (getAttachments() == null || !getAttachments().containsKey( - name)); + && (getAttachments() == null || !getAttachments().containsKey(name)); } /** - * Returns true if bean names will be searched for higher up in the - * BeanFactory hierarchy. Default is true. - * - * @return True if bean names will be searched for higher up in the - * BeanFactory hierarchy. + * Returns true if bean names will be searched for higher up in the BeanFactory hierarchy. + * Default is true. + * + * @return True if bean names will be searched for higher up in the BeanFactory hierarchy. */ public boolean isFindingInAncestors() { return this.findingInAncestors; } /** - * Attaches all {@link ServerResource} and {@link Restlet} beans found in - * the surrounding bean factory for which {@link #resolveUri} finds a usable - * URI. Also attaches everything explicitly routed in the attachments - * property. - * - * @param beanFactory - * The Spring bean factory. + * Attaches all {@link ServerResource} and {@link Restlet} beans found in the surrounding bean + * factory for which {@link #resolveUri} finds a usable URI. Also attaches everything explicitly + * routed in the attachments property. + * + * @param beanFactory The Spring bean factory. * @see #setAttachments */ - public void postProcessBeanFactory( - ConfigurableListableBeanFactory beanFactory) throws BeansException { + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException { - ListableBeanFactory source = this.applicationContext == null ? beanFactory - : this.applicationContext; + ListableBeanFactory source = + this.applicationContext == null ? beanFactory : this.applicationContext; attachAllResources(source); attachAllRestlets(source); if (getAttachments() != null) { - for (Map.Entry attachment : getAttachments() - .entrySet()) { + for (Map.Entry attachment : getAttachments().entrySet()) { String uri = attachment.getKey(); String beanName = attachment.getValue(); Class beanType = source.getType(beanName); - if (org.restlet.resource.ServerResource.class - .isAssignableFrom(beanType)) { + if (org.restlet.resource.ServerResource.class.isAssignableFrom(beanType)) { attachResource(uri, beanName, source); } else if (Restlet.class.isAssignableFrom(beanType)) { attachRestlet(uri, beanName, source); @@ -265,16 +230,12 @@ public void postProcessBeanFactory( } /** - * Uses this first alias for this bean that starts with '/' and is not - * mapped in the explicit attachments to another bean. This mimics the - * behavior of - * {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping} - * . - * - * @param beanName - * The bean name to lookup in the bean factory aliases. - * @param beanFactory - * The Spring bean factory. + * Uses this first alias for this bean that starts with '/' and is not mapped in the explicit + * attachments to another bean. This mimics the behavior of {@link + * org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping} . + * + * @param beanName The bean name to lookup in the bean factory aliases. + * @param beanFactory The Spring bean factory. * @return The alias URI. */ protected String resolveUri(String beanName, ListableBeanFactory beanFactory) { @@ -293,23 +254,19 @@ protected String resolveUri(String beanName, ListableBeanFactory beanFactory) { /** * Sets the Spring application context. - * - * @param applicationContext - * The context. + * + * @param applicationContext The context. */ - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } /** - * Sets an explicit mapping of URI templates to bean IDs to use in addition - * to the usual bean name mapping behavior. If a URI template appears in - * both this mapping and as a bean name, the bean it is mapped to here is - * the one that will be used. - * - * @param attachments - * Supplemental explicit mappings. + * Sets an explicit mapping of URI templates to bean IDs to use in addition to the usual bean + * name mapping behavior. If a URI template appears in both this mapping and as a bean name, the + * bean it is mapped to here is the one that will be used. + * + * @param attachments Supplemental explicit mappings. * @see SpringRouter */ public void setAttachments(Map attachments) { @@ -317,14 +274,11 @@ public void setAttachments(Map attachments) { } /** - * Sets if bean names will be searched for higher up in the BeanFactory - * hierarchy. - * - * @param findingInAncestors - * Search for beans higher up in the BeanFactory hierarchy. + * Sets if bean names will be searched for higher up in the BeanFactory hierarchy. + * + * @param findingInAncestors Search for beans higher up in the BeanFactory hierarchy. */ public void setFindingInAncestors(boolean findingInAncestors) { this.findingInAncestors = findingInAncestors; } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java index 241dfbd0e7..cfb211f3d0 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.util.ArrayList; import java.util.List; - import org.restlet.Client; import org.restlet.Restlet; import org.restlet.Server; @@ -19,7 +17,7 @@ /** * Component that is easily configurable from Spring. Here is a usage example: - * + * *

  * <bean id="component"
  *         class="org.restlet.ext.spring.SpringComponent">
@@ -36,10 +34,10 @@
  *                 </list>
  *         </property>
  * </bean>
- * 
+ *
  * <bean id="component.context"
  *         class="org.springframework.beans.factory.config.PropertyPathFactoryBean" />
- * 
+ *
  * <bean id="server" class="org.restlet.ext.spring.SpringServer">
  *         <constructor-arg value="http" />
  *         <constructor-arg value="8111" />
@@ -51,22 +49,21 @@
  *         </property>
  * </bean>
  * 
- * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @see Spring home page * @author Jerome Louvel */ public class SpringComponent extends org.restlet.Component { /** - * Adds a client to the list of connectors. The value can be either a - * protocol name, a Protocol instance or a Client instance. - * - * @param clientInfo - * The client info. + * Adds a client to the list of connectors. The value can be either a protocol name, a Protocol + * instance or a Client instance. + * + * @param clientInfo The client info. */ public void setClient(Object clientInfo) { final List clients = new ArrayList(); @@ -75,11 +72,9 @@ public void setClient(Object clientInfo) { } /** - * Sets the list of clients, either as protocol names, Protocol instances or - * Client instances. - * - * @param clients - * The list of clients. + * Sets the list of clients, either as protocol names, Protocol instances or Client instances. + * + * @param clients The list of clients. */ public synchronized void setClientsList(List clients) { for (final Object client : clients) { @@ -99,20 +94,18 @@ public synchronized void setClientsList(List clients) { /** * Attaches a target Restlet to the default host. - * - * @param target - * The target Restlet. + * + * @param target The target Restlet. */ public void setDefaultTarget(Restlet target) { getDefaultHost().attach(target); } /** - * Adds a server to the list of connectors. The value can be either a - * protocol name, a Protocol instance or a Server instance. - * - * @param serverInfo - * The server info. + * Adds a server to the list of connectors. The value can be either a protocol name, a Protocol + * instance or a Server instance. + * + * @param serverInfo The server info. */ public void setServer(Object serverInfo) { final List servers = new ArrayList(); @@ -121,11 +114,9 @@ public void setServer(Object serverInfo) { } /** - * Sets the list of servers, either as protocol names, Protocol instances or - * Server instances. - * - * @param serversInfo - * The list of servers. + * Sets the list of servers, either as protocol names, Protocol instances or Server instances. + * + * @param serversInfo The list of servers. */ public void setServersList(List serversInfo) { for (final Object serverInfo : serversInfo) { @@ -142,5 +133,4 @@ public void setServersList(List serversInfo) { } } } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java index 97d95f77a3..34a3dc7950 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.util.ArrayList; import java.util.List; - import org.restlet.Context; import org.restlet.Request; import org.restlet.data.Method; @@ -21,9 +19,9 @@ import org.springframework.context.support.GenericApplicationContext; /** - * Spring application context based on a Restlet context. Here is an example - * illustrating the various ways to use this class: - * + * Spring application context based on a Restlet context. Here is an example illustrating the + * various ways to use this class: + * *

  * SpringContext springContext = new SpringContext(getContext());
  * springContext.getPropertyConfigRefs().add("war://config/database.properties");
@@ -33,7 +31,7 @@
  * springContext.getXmlConfigRefs().add(
  *         "clap://thread/config/applicationContext.xml");
  * 
- * + * * @author Jerome Louvel */ public class SpringContext extends GenericApplicationContext { @@ -41,25 +39,20 @@ public class SpringContext extends GenericApplicationContext { private volatile boolean loaded; /** - * The modifiable list of configuration URIs for beans definitions via - * property representations. + * The modifiable list of configuration URIs for beans definitions via property representations. */ private volatile List propertyConfigRefs; /** The parent Restlet context. */ private volatile Context restletContext; - /** - * The modifiable list of configuration URIs for beans definitions via XML - * representations. - */ + /** The modifiable list of configuration URIs for beans definitions via XML representations. */ private volatile List xmlConfigRefs; /** * Constructor. - * - * @param restletContext - * The parent Restlet context. + * + * @param restletContext The parent Restlet context. */ public SpringContext(Context restletContext) { this.restletContext = restletContext; @@ -69,9 +62,9 @@ public SpringContext(Context restletContext) { } /** - * Returns the modifiable list of configuration URIs for beans definitions - * via property representations. - * + * Returns the modifiable list of configuration URIs for beans definitions via property + * representations. + * * @return The modifiable list of configuration URIs. */ public List getPropertyConfigRefs() { @@ -90,7 +83,7 @@ public List getPropertyConfigRefs() { /** * Returns the parent Restlet context. - * + * * @return The parent Restlet context. */ public Context getRestletContext() { @@ -98,9 +91,9 @@ public Context getRestletContext() { } /** - * Returns the modifiable list of configuration URIs for beans definitions - * via XML representations. - * + * Returns the modifiable list of configuration URIs for beans definitions via XML + * representations. + * * @return The modifiable list of configuration URIs. */ public List getXmlConfigRefs() { @@ -127,8 +120,11 @@ public void refresh() { // First, read the bean definitions from properties representations PropertiesBeanDefinitionReader propReader = null; for (final String ref : getPropertyConfigRefs()) { - config = getRestletContext().getClientDispatcher() - .handle(new Request(Method.GET, ref)).getEntity(); + config = + getRestletContext() + .getClientDispatcher() + .handle(new Request(Method.GET, ref)) + .getEntity(); if (config != null) { propReader = new PropertiesBeanDefinitionReader(this); @@ -139,13 +135,15 @@ public void refresh() { // Then, read the bean definitions from XML representations XmlBeanDefinitionReader xmlReader = null; for (final String ref : getXmlConfigRefs()) { - config = getRestletContext().getClientDispatcher() - .handle(new Request(Method.GET, ref)).getEntity(); + config = + getRestletContext() + .getClientDispatcher() + .handle(new Request(Method.GET, ref)) + .getEntity(); if (config != null) { xmlReader = new XmlBeanDefinitionReader(this); - xmlReader - .setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); + xmlReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); xmlReader.loadBeanDefinitions(new SpringResource(config)); } } @@ -154,5 +152,4 @@ public void refresh() { // Now load or refresh super.refresh(); } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java index 87c669c6c5..7d87e744cd 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java @@ -1,16 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.util.logging.Level; - import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -19,49 +17,43 @@ import org.restlet.resource.ServerResource; /** - * Finder that is specialized for easier usage by Spring wiring services. The - * idea is to create a singleton Spring bean based on that SpringFinder and - * configure it using Spring's "lookup-method" element to return instances of a - * "prototype" bean for {@link #create()}. Finally, attach the SpringFinder to - * your Router. When the {@link #create()} method is invoked, a new instance of - * your prototype bean will be created and returned. A sample XML for - * "lookup-method": - * + * Finder that is specialized for easier usage by Spring wiring services. The idea is to create a + * singleton Spring bean based on that SpringFinder and configure it using Spring's "lookup-method" + * element to return instances of a "prototype" bean for {@link #create()}. Finally, attach the + * SpringFinder to your Router. When the {@link #create()} method is invoked, a new instance of your + * prototype bean will be created and returned. A sample XML for "lookup-method": + * *

- *      <bean id="myFinder" class="org.restlet.ext.spring.SpringFinder"> 
- *              <lookup-method name="create" bean="myResource"/> 
+ *      <bean id="myFinder" class="org.restlet.ext.spring.SpringFinder">
+ *              <lookup-method name="create" bean="myResource"/>
  *      </bean>
- *       
- *      <bean id="myResource" class="com.mycompany.rest.resource.MyResource" scope="prototype"> 
- *              <property name="aProperty" value="anotherOne"/> 
+ *
+ *      <bean id="myResource" class="com.mycompany.rest.resource.MyResource" scope="prototype">
+ *              <property name="aProperty" value="anotherOne"/>
  *              <property name="oneMore" value="true"/>
  *      </bean>
  * 
- * - * Note that the Code Generation - * Library (cglib) will be required in order to use the Spring's lookup - * method mechanism.
+ * + * Note that the Code Generation Library (cglib) will be + * required to use the Spring's lookup method mechanism.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class SpringFinder extends Finder { - /** - * Constructor. - */ + /** Constructor. */ public SpringFinder() { super(); } /** * Constructor. - * - * @param context - * The parent context. + * + * @param context The parent context. */ public SpringFinder(Context context) { super(context); @@ -69,32 +61,27 @@ public SpringFinder(Context context) { /** * Constructor. - * - * @param context - * The context. - * @param targetClass - * The target resource class. + * + * @param context The context. + * @param targetClass The target resource class. */ - public SpringFinder(Context context, - Class targetClass) { + public SpringFinder(Context context, Class targetClass) { super(context, targetClass); } /** * Constructor. - * - * @param restlet - * The parent Restlet. + * + * @param restlet The parent Restlet. */ public SpringFinder(Restlet restlet) { super(restlet.getContext()); } /** - * Creates a new instance of the {@link ServerResource} class designated by - * the "targetClass" property. This method is intended to be configured as a - * lookup method in Spring. - * + * Creates a new instance of the {@link ServerResource} class designated by the "targetClass" + * property. This method is intended to be configured as a lookup method in Spring. + * * @return The created resource or null. */ public ServerResource create() { @@ -106,7 +93,8 @@ public ServerResource create() { result = getTargetClass().getDeclaredConstructor().newInstance(); } catch (Exception e) { getLogger() - .log(Level.WARNING, + .log( + Level.WARNING, "Exception while instantiating the target server resource.", e); } @@ -116,20 +104,17 @@ public ServerResource create() { } /** - * Calls the {@link #create()} method that can be configured as a lookup - * method in Spring. Overriding this method was necessary for direct calls - * to it, for example by unit tests. + * Calls the {@link #create()} method that can be configured as a lookup method in Spring. + * Overriding this method was necessary for direct calls to it, for example by unit tests. */ @Override - public ServerResource create(Class targetClass, - Request request, Response response) { + public ServerResource create( + Class targetClass, Request request, Response response) { return create(request, response); } @Override - public org.restlet.resource.ServerResource create(Request request, - Response response) { + public org.restlet.resource.ServerResource create(Request request, Response response) { return create(); } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java index 0fbfc20f02..568dad342f 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java @@ -1,16 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.util.Map; - import org.restlet.Component; import org.restlet.Context; import org.restlet.Restlet; @@ -18,9 +16,8 @@ import org.restlet.routing.VirtualHost; /** - * Virtual host that is easily configurable with Spring. Here is a usage - * example: - * + * Virtual host that is easily configurable with Spring. Here is a usage example: + * *

  *     <bean id="virtualHost" class="org.restlet.ext.spring.SpringHost">
  *         <constructor-arg ref="component" />
@@ -35,20 +32,19 @@
  *         </property>
  *     </bean>
  * 
- * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class SpringHost extends VirtualHost { /** * Constructor. - * - * @param component - * The parent component. + * + * @param component The parent component. */ public SpringHost(Component component) { super(component.getContext()); @@ -56,23 +52,20 @@ public SpringHost(Component component) { /** * Constructor. - * - * @param context - * The parent context. + * + * @param context The parent context. */ public SpringHost(Context context) { super(context); } /** - * Sets a route to attach. The keys is the URI template and the value can be - * either Restlet instance, {@link ServerResource} subclasse (as - * {@link Class} instances or as qualified class names). - * - * @param path - * The attachment URI path. - * @param route - * The route object to attach. + * Sets a route to attach. The keys is the URI template and the value can be either Restlet + * instance, {@link ServerResource} subclasse (as {@link Class} instances or as qualified class + * names). + * + * @param path The attachment URI path. + * @param route The route object to attach. */ public void setAttachment(String path, Object route) { if (route instanceof Restlet) { @@ -83,12 +76,11 @@ public void setAttachment(String path, Object route) { } /** - * Sets the map of routes to attach. The map keys are the URI templates and - * the values can be either Restlet instances, {@link ServerResource} - * subclasses (as {@link Class} instances or as qualified class names). - * - * @param routes - * The map of routes to attach. + * Sets the map of routes to attach. The map keys are the URI templates and the values can be + * either Restlet instances, {@link ServerResource} subclasses (as {@link Class} instances or as + * qualified class names). + * + * @param routes The map of routes to attach. */ public void setAttachments(Map routes) { for (String key : routes.keySet()) { @@ -97,15 +89,12 @@ public void setAttachments(Map routes) { } /** - * Sets the default route to attach. The route can be either Restlet - * instances, {@link ServerResource} subclasses (as {@link Class} instances - * or as qualified class names). - * - * @param route - * The default route to attach. + * Sets the default route to attach. The route can be either Restlet instances, {@link + * ServerResource} subclasses (as {@link Class} instances or as qualified class names). + * + * @param route The default route to attach. */ public void setDefaultAttachment(Object route) { setAttachment("", route); } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java index 30ba70a282..3cef0ecdd2 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java @@ -1,26 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.io.IOException; import java.io.InputStream; - import org.restlet.engine.util.SystemUtils; import org.restlet.representation.Representation; import org.springframework.core.io.AbstractResource; /** - * Spring Resource based on a Restlet Representation. DON'T GET CONFUSED, - * Spring's notion of Resource is different from Restlet's one, actually it's - * closer to Restlet's Representations. - * + * Spring Resource based on a Restlet Representation. DON'T GET CONFUSED, Spring's notion of + * Resource is different from Restlet's one, actually it's closer to Restlet's Representations. + * * @author Jerome Louvel */ public class SpringResource extends AbstractResource { @@ -35,9 +32,8 @@ public class SpringResource extends AbstractResource { /** * Constructor. - * - * @param representation - * The description. + * + * @param representation The description. */ public SpringResource(Representation representation) { this(representation, "Restlet Representation"); @@ -45,34 +41,28 @@ public SpringResource(Representation representation) { /** * Constructor. - * - * @param representation - * The description. - * @param description - * The description. + * + * @param representation The description. + * @param description The description. */ public SpringResource(Representation representation, String description) { if (representation == null) { - throw new IllegalArgumentException( - "Representation must not be null"); + throw new IllegalArgumentException("Representation must not be null"); } this.representation = representation; this.description = (description != null) ? description : ""; } - /** - * This implementation compares the underlying InputStream. - */ + /** This implementation compares the underlying InputStream. */ @Override public boolean equals(Object obj) { - return ((obj == this) || ((obj instanceof SpringResource) && ((SpringResource) obj).representation - .equals(this.representation))); + return ((obj == this) + || ((obj instanceof SpringResource) + && ((SpringResource) obj).representation.equals(this.representation))); } - /** - * This implementation always returns true. - */ + /** This implementation always returns true. */ @Override public boolean exists() { return true; @@ -80,7 +70,7 @@ public boolean exists() { /** * Returns the description. - * + * * @return The description. */ public String getDescription() { @@ -88,11 +78,10 @@ public String getDescription() { } /** - * This implementation throws IllegalStateException if attempting to read - * the underlying stream multiple times. + * This implementation throws IllegalStateException if attempting to read the underlying stream + * multiple times. */ - public InputStream getInputStream() throws IOException, - IllegalStateException { + public InputStream getInputStream() throws IOException, IllegalStateException { if (this.read && this.representation.isTransient()) { throw new IllegalStateException( "Representation has already been read and is transient."); @@ -102,20 +91,15 @@ public InputStream getInputStream() throws IOException, return this.representation.getStream(); } - /** - * This implementation returns the hash code of the underlying InputStream. - */ + /** This implementation returns the hash code of the underlying InputStream. */ @Override public int hashCode() { return SystemUtils.hashCode(this.representation); } - /** - * This implementation always returns true. - */ + /** This implementation always returns true. */ @Override public boolean isOpen() { return true; } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java index dcbdbd2d5c..db64f50a74 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.util.Map; import java.util.logging.Level; - import org.restlet.Context; import org.restlet.Restlet; import org.restlet.engine.Engine; @@ -20,11 +18,11 @@ /** * Router that is easily configurable with Spring. Here is a usage example: - * + * *

  * <bean class="org.restlet.ext.spring.SpringRouter">
  *     <constructor-arg ref="application" />
- * 
+ *
  *     <property name="attachments">
  *         <map>
  *             <entry key="/users/{user}"                  value="org.restlet.example.tutorial.UserResource" />
@@ -34,24 +32,21 @@
  *     </property>
  * </bean>
  * 
- * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class SpringRouter extends Router { /** * Attach a single route. - * - * @param router - * The router to attach to. - * @param path - * The attachment URI path. - * @param route - * The route object to attach. + * + * @param router The router to attach to. + * @param path The attachment URI path. + * @param route The route object to attach. */ @SuppressWarnings("unchecked") public static void setAttachment(Router router, String path, Object route) { @@ -65,18 +60,15 @@ public static void setAttachment(Router router, String path, Object route) { try { resourceClass = Engine.loadClass((String) route); - if (org.restlet.resource.ServerResource.class - .isAssignableFrom(resourceClass)) { - router.attach(path, - (Class) resourceClass); + if (org.restlet.resource.ServerResource.class.isAssignableFrom(resourceClass)) { + router.attach(path, (Class) resourceClass); } else { router.getLogger() .warning( "Unknown class found in the mappings. Only subclasses of org.restlet.resource.Resource and ServerResource are allowed."); } } catch (ClassNotFoundException e) { - router.getLogger().log(Level.WARNING, - "Unable to set the router mappings", e); + router.getLogger().log(Level.WARNING, "Unable to set the router mappings", e); } } else { router.getLogger() @@ -87,11 +79,9 @@ public static void setAttachment(Router router, String path, Object route) { /** * Sets the map of routes to attach. - * - * @param router - * The router to attach to. - * @param routes - * The map of routes to attach + * + * @param router The router to attach to. + * @param routes The map of routes to attach */ public static void setAttachments(Router router, Map routes) { for (String key : routes.keySet()) { @@ -99,18 +89,15 @@ public static void setAttachments(Router router, Map routes) { } } - /** - * Constructor. - */ + /** Constructor. */ public SpringRouter() { super(); } /** * Constructor with a parent context. - * - * @param context - * The parent context. + * + * @param context The parent context. */ public SpringRouter(Context context) { super(context); @@ -118,36 +105,31 @@ public SpringRouter(Context context) { /** * Constructor with a parent Restlet. - * - * @param parent - * The parent Restlet. + * + * @param parent The parent Restlet. */ public SpringRouter(Restlet parent) { super(parent.getContext()); } /** - * Sets the map of routes to attach. The map keys are the URI templates and - * the values can be either Restlet instances, {@link ServerResource} - * subclasses (as {@link Class} instances or as qualified class names). - * - * @param routes - * The map of routes to attach. + * Sets the map of routes to attach. The map keys are the URI templates and the values can be + * either Restlet instances, {@link ServerResource} subclasses (as {@link Class} instances or as + * qualified class names). + * + * @param routes The map of routes to attach. */ public void setAttachments(Map routes) { setAttachments(this, routes); } /** - * Sets the default route to attach. The route can be either Restlet - * instances, {@link ServerResource} subclasses (as {@link Class} instances - * or as qualified class names). - * - * @param route - * The default route to attach. + * Sets the default route to attach. The route can be either Restlet instances, {@link + * ServerResource} subclasses (as {@link Class} instances or as qualified class names). + * + * @param route The default route to attach. */ public void setDefaultAttachment(Object route) { setAttachment(this, "", route); } - } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java index f09ee75c8d..19d0da7b37 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java @@ -1,35 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; import java.util.Enumeration; import java.util.Properties; - import org.restlet.Context; import org.restlet.Restlet; import org.restlet.data.Protocol; /** * Server that is easily configurable with Spring. Here is a usage example: - * + * *

  * <bean id="server" class="org.restlet.ext.spring.SpringServer">
  *      <constructor-arg value="http" />
  *      <constructor-arg value="8111" />
  * </bean>
  * 
- * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @see Spring home page * @author Jerome Louvel */ @@ -37,9 +35,8 @@ public class SpringServer extends org.restlet.Server { /** * Constructor. - * - * @param protocol - * The server's protocol such as "HTTP" or "HTTPS". + * + * @param protocol The server's protocol such as "HTTP" or "HTTPS". */ public SpringServer(String protocol) { super(new Context(), Protocol.valueOf(protocol), (Restlet) null); @@ -47,11 +44,9 @@ public SpringServer(String protocol) { /** * Constructor. - * - * @param protocol - * The server's protocol such as "HTTP" or "HTTPS". - * @param port - * The port number. + * + * @param protocol The server's protocol such as "HTTP" or "HTTPS". + * @param port The port number. */ public SpringServer(String protocol, int port) { super(new Context(), Protocol.valueOf(protocol), port, (Restlet) null); @@ -59,13 +54,10 @@ public SpringServer(String protocol, int port) { /** * Constructor. - * - * @param protocol - * The server's protocol such as "HTTP" or "HTTPS". - * @param address - * The IP address. - * @param port - * The port number. + * + * @param protocol The server's protocol such as "HTTP" or "HTTPS". + * @param address The IP address. + * @param port The port number. */ public SpringServer(String protocol, String address, int port) { super(new Context(), Protocol.valueOf(protocol), address, port, null); @@ -73,17 +65,14 @@ public SpringServer(String protocol, String address, int port) { /** * Sets parameters on the server. - * - * @param parameters - * Parameters to set on the server. + * + * @param parameters Parameters to set on the server. */ public void setParameters(Properties parameters) { final Enumeration names = parameters.propertyNames(); while (names.hasMoreElements()) { final String name = (String) names.nextElement(); - getContext().getParameters() - .add(name, parameters.getProperty(name)); + getContext().getParameters().add(name, parameters.getProperty(name)); } } - } diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java index 7a590e2328..b34925811e 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java @@ -1,14 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -20,20 +24,14 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.support.StaticApplicationContext; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - /** * @author Rhett Sutphin */ public class SpringBeanFinderTestCase { - private static class AnotherResource extends ServerResource { - } + private static class AnotherResource extends ServerResource {} - private static class SomeResource extends ServerResource { - } + private static class SomeResource extends ServerResource {} private static class SomeServerResource extends ServerResource { private String src; @@ -61,10 +59,11 @@ public void setSrc(String src) { private SpringBeanFinder finder; private MutablePropertyValues createServerResourcePropertyValues() { - return new MutablePropertyValues(List.of(new PropertyValue("src","spring"))); + return new MutablePropertyValues(List.of(new PropertyValue("src", "spring"))); } - private void registerApplicationContextBean(String beanName, Class resourceClass) { + private void registerApplicationContextBean( + String beanName, Class resourceClass) { this.applicationContext.registerPrototype(beanName, resourceClass); this.applicationContext.refresh(); } @@ -73,8 +72,10 @@ private void registerBeanFactoryBean(String beanName, Class resourceClass) { registerBeanFactoryBean(beanName, resourceClass, null); } - private void registerBeanFactoryBean(String beanName, Class resourceClass, MutablePropertyValues values) { - this.beanFactory.registerBeanDefinition(beanName, + private void registerBeanFactoryBean( + String beanName, Class resourceClass, MutablePropertyValues values) { + this.beanFactory.registerBeanDefinition( + beanName, new RootBeanDefinition(resourceClass, new ConstructorArgumentValues(), values)); } @@ -95,22 +96,27 @@ protected void tearDownEach() { @Test public void testBeanResolutionFailsWithNeitherApplicationContextOrBeanFactory() { - IllegalStateException iae = assertThrows(IllegalStateException.class, () -> this.finder.create()); - assertEquals("Either a beanFactory or an applicationContext is required for SpringBeanFinder.", iae.getMessage()); + IllegalStateException iae = + assertThrows(IllegalStateException.class, () -> this.finder.create()); + assertEquals( + "Either a beanFactory or an applicationContext is required for SpringBeanFinder.", + iae.getMessage()); } @Test public void testBeanResolutionFailsWhenNoMatchingBeanButThereIsABeanFactory() { this.finder.setBeanFactory(beanFactory); - IllegalStateException iae = assertThrows(IllegalStateException.class, () -> this.finder.create()); + IllegalStateException iae = + assertThrows(IllegalStateException.class, () -> this.finder.create()); assertEquals("No bean named " + BEAN_NAME + " present.", iae.getMessage()); } @Test public void testBeanResolutionFailsWhenNoMatchingBeanButThereIsAnApplicationContext() { this.finder.setApplicationContext(applicationContext); - IllegalStateException iae = assertThrows(IllegalStateException.class, () -> this.finder.create()); + IllegalStateException iae = + assertThrows(IllegalStateException.class, () -> this.finder.create()); assertEquals("No bean named " + BEAN_NAME + " present.", iae.getMessage()); } @@ -120,7 +126,8 @@ public void testExceptionWhenResourceBeanIsWrongType() { this.finder.setBeanFactory(beanFactory); - ClassCastException classCastException = assertThrows(ClassCastException.class, () -> this.finder.create()); + ClassCastException classCastException = + assertThrows(ClassCastException.class, () -> this.finder.create()); assertEquals( "fish does not resolve to an instance of org.restlet.resource.ServerResource", classCastException.getMessage()); @@ -132,7 +139,8 @@ public void testExceptionWhenServerResourceBeanIsWrongType() { this.finder.setBeanFactory(beanFactory); - ClassCastException classCastException = assertThrows(ClassCastException.class, () -> this.finder.create()); + ClassCastException classCastException = + assertThrows(ClassCastException.class, () -> this.finder.create()); assertEquals( "fish does not resolve to an instance of org.restlet.resource.ServerResource", classCastException.getMessage()); @@ -147,7 +155,9 @@ public void testPrefersApplicationContextOverBeanFactoryIfTheBeanIsInBoth() { ServerResource actual = this.finder.create(); - assertTrue(actual instanceof SomeResource, "Resource not from application context: " + actual.getClass().getName()); + assertTrue( + actual instanceof SomeResource, + "Resource not from application context: " + actual.getClass().getName()); } @Test @@ -163,22 +173,24 @@ public void testReturnsResourceBeanWhenExists() { @Test public void testReturnsServerResourceBeanForLongFormOfCreate() { - registerBeanFactoryBean(BEAN_NAME, SomeServerResource.class, - createServerResourcePropertyValues()); + registerBeanFactoryBean( + BEAN_NAME, SomeServerResource.class, createServerResourcePropertyValues()); this.finder.setBeanFactory(beanFactory); - final ServerResource actual = this.finder.create( - SomeServerResource.class, null, null); + final ServerResource actual = this.finder.create(SomeServerResource.class, null, null); assertTrue(actual instanceof SomeServerResource, "Resource not the correct type"); - assertEquals("spring", ((SomeServerResource) actual).getSrc(), "Resource not from spring context"); + assertEquals( + "spring", + ((SomeServerResource) actual).getSrc(), + "Resource not from spring context"); } @Test public void testReturnsServerResourceBeanWhenExists() { - registerBeanFactoryBean(BEAN_NAME, SomeServerResource.class, - createServerResourcePropertyValues()); + registerBeanFactoryBean( + BEAN_NAME, SomeServerResource.class, createServerResourcePropertyValues()); this.finder.setBeanFactory(beanFactory); diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java index e8566469a4..882871f61a 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java @@ -1,14 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,12 +37,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.*; - /** * @author Rhett Sutphin */ @@ -46,14 +48,11 @@ private TestAuthenticator() throws IllegalArgumentException { } } - private static class TestFilter extends Filter { - } + private static class TestFilter extends Filter {} - private static class TestResource extends ServerResource { - } + private static class TestResource extends ServerResource {} - private static class TestRestlet extends Restlet { - } + private static class TestRestlet extends Restlet {} private static final String FISH_URI = "/renewable/fish/{fish_name}"; @@ -69,12 +68,19 @@ private RouteList actualRoutes() { } private void assertFinderForBean(String expectedBeanName, Restlet restlet) { - assertInstanceOf(SpringBeanFinder.class, restlet, "Restlet is not a bean finder restlet: " - + restlet.getClass().getName()); + assertInstanceOf( + SpringBeanFinder.class, + restlet, + "Restlet is not a bean finder restlet: " + restlet.getClass().getName()); final SpringBeanFinder actualFinder = (SpringBeanFinder) restlet; - assertEquals(expectedBeanName, actualFinder.getBeanName(), + assertEquals( + expectedBeanName, + actualFinder.getBeanName(), "Finder does not point to correct bean"); - assertEquals(this.factory, actualFinder.getBeanFactory(), "Finder does not point to correct bean factory"); + assertEquals( + this.factory, + actualFinder.getBeanFactory(), + "Finder does not point to correct bean factory"); } private void doPostProcess() { @@ -82,18 +88,21 @@ private void doPostProcess() { } private TemplateRoute matchRouteFor(String uri) { - Request req = new Request(Method.GET, - new Template(uri).format(new Resolver() { - @Override - public String resolve(String name) { - return name; - } - })); + Request req = + new Request( + Method.GET, + new Template(uri) + .format( + new Resolver() { + @Override + public String resolve(String name) { + return name; + } + })); return (TemplateRoute) router.getNext(req, new Response(req)); } - private void registerBeanDefinition(String id, String alias, - Class beanClass, String scope) { + private void registerBeanDefinition(String id, String alias, Class beanClass, String scope) { BeanDefinition bd = new RootBeanDefinition(beanClass); bd.setScope(scope == null ? BeanDefinition.SCOPE_SINGLETON : scope); this.factory.registerBeanDefinition(id, bd); @@ -104,8 +113,7 @@ private void registerBeanDefinition(String id, String alias, } private void registerServerResourceBeanDefinition(String id, String alias) { - registerBeanDefinition(id, alias, ServerResource.class, - BeanDefinition.SCOPE_PROTOTYPE); + registerBeanDefinition(id, alias, ServerResource.class, BeanDefinition.SCOPE_PROTOTYPE); } private Set routeUris(RouteList routes) { @@ -113,8 +121,7 @@ private Set routeUris(RouteList routes) { for (Route actualRoute : routes) { if (actualRoute instanceof TemplateRoute) { - uris.add(((TemplateRoute) actualRoute).getTemplate() - .getPattern()); + uris.add(((TemplateRoute) actualRoute).getTemplate().getPattern()); } } @@ -139,14 +146,14 @@ protected void tearDownEach() throws Exception { @Test public void testExplicitAttachmentsMayBeRestlets() { String expected = "/protected/timber"; - this.router - .setAttachments(Collections.singletonMap(expected, "timber")); + this.router.setAttachments(Collections.singletonMap(expected, "timber")); registerBeanDefinition("timber", null, TestAuthenticator.class, null); doPostProcess(); TemplateRoute timberRoute = matchRouteFor(expected); assertNotNull(timberRoute, "No route for " + expected); - assertInstanceOf(TestAuthenticator.class, timberRoute.getNext(), "Route is not for correct restlet"); + assertInstanceOf( + TestAuthenticator.class, timberRoute.getNext(), "Route is not for correct restlet"); } @Test @@ -204,13 +211,15 @@ public void testRoutesPointToFindersForBeans() { @Test public void testRoutingIncludesAuthenticators() { String expected = "/protected/timber"; - registerBeanDefinition("timber", expected, TestAuthenticator.class, - null); + registerBeanDefinition("timber", expected, TestAuthenticator.class, null); doPostProcess(); TemplateRoute authenticatorRoute = matchRouteFor(expected); assertNotNull(authenticatorRoute, "No route for authenticator"); - assertInstanceOf(TestAuthenticator.class, authenticatorRoute.getNext(), "Route is not for authenticator"); + assertInstanceOf( + TestAuthenticator.class, + authenticatorRoute.getNext(), + "Route is not for authenticator"); } @Test @@ -238,8 +247,8 @@ public void testRoutingIncludesOtherRestlets() { @Test public void testRoutingIncludesResourceSubclasses() { String expected = "/renewable/timber/{id}"; - registerBeanDefinition("timber", expected, TestResource.class, - BeanDefinition.SCOPE_PROTOTYPE); + registerBeanDefinition( + "timber", expected, TestResource.class, BeanDefinition.SCOPE_PROTOTYPE); doPostProcess(); TemplateRoute timberRoute = matchRouteFor("/renewable/timber/sycamore"); @@ -255,8 +264,7 @@ public void testRoutingIncludesSpringRouterStyleExplicitlyMappedBeans() { this.factory.registerAlias("timber", "no-slash"); String expectedTemplate = "/renewable/timber/{farm_type}"; - router.setAttachments(Collections.singletonMap(expectedTemplate, - "timber")); + router.setAttachments(Collections.singletonMap(expectedTemplate, "timber")); final RouteList actualRoutes = actualRoutes(); assertEquals(3, actualRoutes.size(), "Wrong number of routes"); diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java index 70e2f13972..6ec21ffdae 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java @@ -1,14 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,13 +20,9 @@ import org.restlet.engine.Engine; import org.springframework.context.support.ClassPathXmlApplicationContext; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - /** * Unit test case for the Spring extension. - * + * * @author Jerome Louvel */ public class SpringTestCase { @@ -44,10 +43,8 @@ public void testSpring() throws Exception { public void testSpringServerProperties() { Server server = (Server) ctx.getBean("server"); - assertEquals("value1", server.getContext().getParameters() - .getFirstValue("key1")); - assertEquals("value2", server.getContext().getParameters() - .getFirstValue("key2")); + assertEquals("value1", server.getContext().getParameters().getFirstValue("key1")); + assertEquals("value2", server.getContext().getParameters().getFirstValue("key2")); } private ClassPathXmlApplicationContext ctx; @@ -63,5 +60,4 @@ void cleanUp() { Engine.clearThreadLocalVariables(); ctx.close(); } - } diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java index f757a80641..79e0ae2b1d 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring.resources; import org.restlet.resource.Get; @@ -24,5 +23,4 @@ public void doInit() { public String toString() { return "Order \"" + this.orderId + "\" for user \"" + this.userName + "\""; } - } diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java index bac7783487..6eabc73585 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring.resources; import org.restlet.resource.Get; @@ -17,5 +16,4 @@ public class OrdersResource extends UserResource { public String toString() { return "Orders of user \"" + this.userName + "\""; } - } diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java index 5df583c50d..44c2dfdd36 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.spring.resources; import org.restlet.resource.Get; diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java index 672bc10baa..e7f1ed1f9e 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.thymeleaf; import java.util.Locale; import java.util.Map; - import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -21,22 +19,21 @@ import org.restlet.util.Resolver; /** - * Filters response's entity and wraps it with a Thymeleaf's template - * representation. By default, the template representation provides a data model - * based on the request and response objects. In order for the wrapping to - * happen, the representations must have the {@link #THYMELEAF} encoding + * Filters response's entity and wraps it with a Thymeleaf's template representation. By default, + * the template representation provides a data model based on the request and response objects. In + * order for the wrapping to happen, the representations must have the {@link #THYMELEAF} encoding * set.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. * * @author Grzegorz Godlewski */ public abstract class TemplateFilter extends Filter { - private static final Encoding THYMELEAF = new Encoding("thymeleaf", - "Thymeleaf templated representation"); + private static final Encoding THYMELEAF = + new Encoding("thymeleaf", "Thymeleaf templated representation"); /** The template's data model as a map. */ private volatile Map mapDataModel; @@ -44,9 +41,7 @@ public abstract class TemplateFilter extends Filter { /** The template's data model as a resolver. */ private volatile Resolver resolverDataModel; - /** - * Constructor. - */ + /** Constructor. */ public TemplateFilter() { super(); } @@ -54,8 +49,7 @@ public TemplateFilter() { /** * Constructor. * - * @param context - * The context. + * @param context The context. */ public TemplateFilter(Context context) { super(context); @@ -64,10 +58,8 @@ public TemplateFilter(Context context) { /** * Constructor. * - * @param context - * The context. - * @param next - * The next Restlet. + * @param context The context. + * @param next The next Restlet. */ public TemplateFilter(Context context, Restlet next) { super(context, next); @@ -78,15 +70,11 @@ public TemplateFilter(Context context, Restlet next) { /** * Constructor. * - * @param context - * The context. - * @param next - * The next Restlet. - * @param dataModel - * The filter's data model. + * @param context The context. + * @param next The next Restlet. + * @param dataModel The filter's data model. */ - public TemplateFilter(Context context, Restlet next, - Map dataModel) { + public TemplateFilter(Context context, Restlet next, Map dataModel) { super(context, next); this.mapDataModel = dataModel; this.resolverDataModel = null; @@ -95,15 +83,11 @@ public TemplateFilter(Context context, Restlet next, /** * Constructor. * - * @param context - * The context. - * @param next - * The next Restlet. - * @param dataModel - * The filter's data model. + * @param context The context. + * @param next The next Restlet. + * @param dataModel The filter's data model. */ - public TemplateFilter(Context context, Restlet next, - Resolver dataModel) { + public TemplateFilter(Context context, Restlet next, Resolver dataModel) { super(context, next); this.mapDataModel = null; this.resolverDataModel = dataModel; @@ -113,12 +97,13 @@ public TemplateFilter(Context context, Restlet next, protected void afterHandle(Request request, Response response) { if (response.isEntityAvailable() && response.getEntity().getEncodings().contains(THYMELEAF)) { - final TemplateRepresentation representation = new TemplateRepresentation( - (TemplateRepresentation) response.getEntity(), - getLocale(), response.getEntity().getMediaType()); + final TemplateRepresentation representation = + new TemplateRepresentation( + (TemplateRepresentation) response.getEntity(), + getLocale(), + response.getEntity().getMediaType()); - if ((this.mapDataModel == null) - && (this.resolverDataModel == null)) { + if ((this.mapDataModel == null) && (this.resolverDataModel == null)) { representation.setDataModel(request, response); } else { if (this.mapDataModel == null) { diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java index 17cc4b7467..fc7fdc2806 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.thymeleaf; import java.io.IOException; @@ -17,7 +16,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; - import org.restlet.Request; import org.restlet.Response; import org.restlet.data.Form; @@ -32,16 +30,15 @@ import org.thymeleaf.util.Validate; /** - * Thymeleaf template representation. Useful for dynamic string-based - * representations. - * + * Thymeleaf template representation. Useful for dynamic string-based representations. + * * @author Grzegorz Godlewski */ public class TemplateRepresentation extends WriterRepresentation { /** * Context that leverages an instance of {@link Resolver}. - * + * * @author Grzegorz Godlewski */ private static class ResolverContext implements IContext { @@ -53,11 +50,9 @@ private static class ResolverContext implements IContext { /** * Constructor. - * - * @param locale - * The Locale. - * @param resolver - * The Resolver instance. + * + * @param locale The Locale. + * @param resolver The Resolver instance. */ public ResolverContext(Locale locale, Resolver resolver) { this.locale = locale; @@ -65,8 +60,7 @@ public ResolverContext(Locale locale, Resolver resolver) { } public final void addContextExecutionInfo(final String templateName) { - Validate.notEmpty(templateName, - "Template name cannot be null or empty"); + Validate.notEmpty(templateName, "Template name cannot be null or empty"); } public Locale getLocale() { @@ -87,14 +81,12 @@ public Set getVariableNames() { public Object getVariable(final String key) { return resolver.resolve(key); } - } /** - * Returns a new instance of {@link TemplateEngine} based by default on a - * {@link ITemplateResolver} returned by calling - * {@link #createTemplateResolver()}. - * + * Returns a new instance of {@link TemplateEngine} based by default on a {@link + * ITemplateResolver} returned by calling {@link #createTemplateResolver()}. + * * @return A new instance of {@link TemplateEngine} */ public static TemplateEngine createTemplateEngine() { @@ -102,10 +94,9 @@ public static TemplateEngine createTemplateEngine() { } /** - * Returns a new instance of {@link TemplateEngine} based by default on a - * {@link ITemplateResolver} returned by calling - * {@link #createTemplateResolver()}. - * + * Returns a new instance of {@link TemplateEngine} based by default on a {@link + * ITemplateResolver} returned by calling {@link #createTemplateResolver()}. + * * @return A new instance of {@link TemplateEngine} */ public static TemplateEngine createTemplateEngine(ITemplateResolver resolver) { @@ -115,10 +106,9 @@ public static TemplateEngine createTemplateEngine(ITemplateResolver resolver) { } /** - * Returns a new instance of {@link ITemplateResolver} with default - * configuration (XHTML template model, templates located inside - * "/WEB-INF/templates/", suffixed by ".html". - * + * Returns a new instance of {@link ITemplateResolver} with default configuration (XHTML + * template model, templates located inside "/WEB-INF/templates/", suffixed by ".html". + * * @return A new instance of {@link ITemplateResolver}. */ public static ITemplateResolver createTemplateResolver() { @@ -150,56 +140,49 @@ public static ITemplateResolver createTemplateResolver() { /** * Constructor. - * - * @param templateName - * The Thymeleaf template's name. The actual template is - * retrieved using the Thymeleaf configuration. - * @param locale - * The locale of the template. - * @param dataModel - * The Thymeleaf template's data model. - * @param mediaType - * The representation's media type. + * + * @param templateName The Thymeleaf template's name. The actual template is retrieved using the + * Thymeleaf configuration. + * @param locale The locale of the template. + * @param dataModel The Thymeleaf template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(String templateName, Locale locale, - Map dataModel, MediaType mediaType) { + public TemplateRepresentation( + String templateName, + Locale locale, + Map dataModel, + MediaType mediaType) { this(templateName, createTemplateEngine(), locale, dataModel, mediaType); } /** * Constructor. - * - * @param templateName - * The Thymeleaf template's name. The full path is resolved by - * the configuration. - * @param locale - * The locale of the template. - * @param mediaType - * The representation's media type. + * + * @param templateName The Thymeleaf template's name. The full path is resolved by the + * configuration. + * @param locale The locale of the template. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(String templateName, Locale locale, - MediaType mediaType) { - this(templateName, locale, new ConcurrentHashMap(), - mediaType); + public TemplateRepresentation(String templateName, Locale locale, MediaType mediaType) { + this(templateName, locale, new ConcurrentHashMap(), mediaType); } /** * Constructor. - * - * @param templateName - * The Thymeleaf template's name. The actual template is - * retrieved using the Thymeleaf configuration. - * @param engine - * The template engine. - * @param locale - * The locale of the template. - * @param dataModel - * The Thymeleaf template's data model. - * @param mediaType - * The representation's media type. + * + * @param templateName The Thymeleaf template's name. The actual template is retrieved using the + * Thymeleaf configuration. + * @param engine The template engine. + * @param locale The locale of the template. + * @param dataModel The Thymeleaf template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(String templateName, TemplateEngine engine, - Locale locale, Map dataModel, MediaType mediaType) { + public TemplateRepresentation( + String templateName, + TemplateEngine engine, + Locale locale, + Map dataModel, + MediaType mediaType) { super(mediaType); this.locale = locale; this.engine = engine; @@ -209,52 +192,42 @@ public TemplateRepresentation(String templateName, TemplateEngine engine, /** * Constructor. - * - * @param templateName - * The Thymeleaf template's name. The full path is resolved by - * the configuration. - * @param locale - * The locale of the template - * @param mediaType - * The representation's media type. + * + * @param templateName The Thymeleaf template's name. The full path is resolved by the + * configuration. + * @param locale The locale of the template + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(String templateName, TemplateEngine engine, - Locale locale, MediaType mediaType) { - this(templateName, engine, locale, - new ConcurrentHashMap(), mediaType); + public TemplateRepresentation( + String templateName, TemplateEngine engine, Locale locale, MediaType mediaType) { + this(templateName, engine, locale, new ConcurrentHashMap(), mediaType); } /** * Constructor based on a Thymeleaf 'encoded' representation. - * - * @param templateRepresentation - * The representation to 'decode'. - * @param locale - * The locale of the template. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The representation to 'decode'. + * @param locale The locale of the template. + * @param mediaType The representation's media-type. */ public TemplateRepresentation( - TemplateRepresentation templateRepresentation, Locale locale, - MediaType mediaType) { + TemplateRepresentation templateRepresentation, Locale locale, MediaType mediaType) { this(templateRepresentation, createTemplateEngine(), locale, mediaType); } /** * Constructor based on a Thymeleaf 'encoded' representation. - * - * @param templateRepresentation - * The representation to 'decode'. - * @param engine - * The template engine. - * @param locale - * The locale of the template. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The representation to 'decode'. + * @param engine The template engine. + * @param locale The locale of the template. + * @param mediaType The representation's media-type. */ public TemplateRepresentation( TemplateRepresentation templateRepresentation, - TemplateEngine engine, Locale locale, MediaType mediaType) { + TemplateEngine engine, + Locale locale, + MediaType mediaType) { super(mediaType); this.locale = locale; this.engine = engine; @@ -263,7 +236,7 @@ public TemplateRepresentation( /** * Returns the representation's locale. - * + * * @return The representation's locale. */ public Locale getLocale() { @@ -272,7 +245,7 @@ public Locale getLocale() { /** * Returns the template's name. - * + * * @return The template's name. */ public String getTemplateName() { @@ -281,9 +254,8 @@ public String getTemplateName() { /** * Sets the Thymeleaf context. - * - * @param context - * The Thymeleaf context + * + * @param context The Thymeleaf context */ protected void setContext(IContext context) { this.context = context; @@ -291,9 +263,8 @@ protected void setContext(IContext context) { /** * Sets the template's data model. - * - * @param dataModel - * The template's data model. + * + * @param dataModel The template's data model. */ public void setDataModel(Map dataModel) { Context ctx = new Context(locale); @@ -302,16 +273,13 @@ public void setDataModel(Map dataModel) { } /** - * Sets the template's data model from a request/response pair. This default - * implementation uses a Resolver. - * + * Sets the template's data model from a request/response pair. This default implementation uses + * a Resolver. + * * @see Resolver * @see Resolver#createResolver(Request, Response) - * - * @param request - * The request where data are located. - * @param response - * The response where data are located. + * @param request The request where data are located. + * @param response The response where data are located. */ public void setDataModel(Request request, Response response) { Form form = new Form(request.getEntity()); @@ -322,9 +290,8 @@ public void setDataModel(Request request, Response response) { /** * Sets the template's data model from a resolver. - * - * @param resolver - * The resolver. + * + * @param resolver The resolver. */ public void setDataModel(Resolver resolver) { setContext(new ResolverContext(locale, resolver)); @@ -332,9 +299,8 @@ public void setDataModel(Resolver resolver) { /** * Sets the template's name. - * - * @param templateName - * The template's name. + * + * @param templateName The template's name. */ public void setTemplateName(String templateName) { this.templateName = templateName; @@ -342,9 +308,8 @@ public void setTemplateName(String templateName) { /** * Writes the datum as a stream of characters. - * - * @param writer - * The writer to use when writing. + * + * @param writer The writer to use when writing. */ @Override public void write(Writer writer) throws IOException { @@ -354,18 +319,15 @@ public void write(Writer writer) throws IOException { engine.process(templateName, context, writer); } catch (Exception e) { - final org.restlet.Context context = org.restlet.Context - .getCurrent(); + final org.restlet.Context context = org.restlet.Context.getCurrent(); if (context != null) { - context.getLogger().log(Level.WARNING, - "Unable to process the template", e); + context.getLogger().log(Level.WARNING, "Unable to process the template", e); } e.printStackTrace(); - throw new IOException("Template processing error. " - + e.getMessage()); + throw new IOException("Template processing error. " + e.getMessage()); } } } diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java index 96ffd6e803..7fd66003c3 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java @@ -1,18 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.thymeleaf; import java.io.IOException; import java.util.List; import java.util.Locale; - import org.restlet.data.MediaType; import org.restlet.data.Preference; import org.restlet.engine.converter.ConverterHelper; @@ -23,15 +21,14 @@ import org.thymeleaf.templateresource.ITemplateResource; /** - * Converter between the Thymeleaf Template objects and Representations. The - * adjoined data model is based on the request and response objects. - * + * Converter between the Thymeleaf Template objects and Representations. The adjoined data model is + * based on the request and response objects. + * * @author Grzegorz Godlewski */ public class ThymeleafConverter extends ConverterHelper { - private static final VariantInfo VARIANT_ALL = new VariantInfo( - MediaType.ALL); + private static final VariantInfo VARIANT_ALL = new VariantInfo(MediaType.ALL); private Locale getLocale(Resource resource) { return Locale.getDefault(); @@ -63,27 +60,28 @@ public float score(Object source, Variant target, Resource resource) { } @Override - public float score(Representation source, Class target, - Resource resource) { + public float score(Representation source, Class target, Resource resource) { return -1.0f; } @Override - public T toObject(Representation source, Class target, - Resource resource) throws IOException { + public T toObject(Representation source, Class target, Resource resource) + throws IOException { return null; } @Override - public Representation toRepresentation(Object source, Variant target, - Resource resource) throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { if (source instanceof ITemplateResource) { Locale locale = getLocale(resource); - TemplateRepresentation tr = new TemplateRepresentation( - ((ITemplateResource) source).getBaseName(), locale, - target.getMediaType()); + TemplateRepresentation tr = + new TemplateRepresentation( + ((ITemplateResource) source).getBaseName(), + locale, + target.getMediaType()); tr.setDataModel(resource.getRequest(), resource.getResponse()); return tr; } @@ -92,8 +90,7 @@ public Representation toRepresentation(Object source, Variant target, } @Override - public void updatePreferences(List> preferences, - Class entity) { + public void updatePreferences(List> preferences, Class entity) { if (ITemplateResource.class.isAssignableFrom(entity)) { updatePreferences(preferences, MediaType.ALL, 1.0F); } diff --git a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java index e1ab55a270..2848050f21 100644 --- a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java +++ b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java @@ -1,26 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.thymeleaf; -import org.junit.jupiter.api.Test; -import org.restlet.data.MediaType; -import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Locale; import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; /** * Unit test for the Thymeleaf extension. - * + * * @author Thierry Boileau */ public class ThymeleafTestCase { @@ -34,10 +32,14 @@ public void testTemplate() throws Exception { final Map map = Map.of("welcome", "Hello, world"); - final String result = new TemplateRepresentation("test", - TemplateRepresentation.createTemplateEngine(templateResolver), - Locale.getDefault(), map, MediaType.TEXT_PLAIN).getText(); + final String result = + new TemplateRepresentation( + "test", + TemplateRepresentation.createTemplateEngine(templateResolver), + Locale.getDefault(), + map, + MediaType.TEXT_PLAIN) + .getText(); assertTrue(result.contains("Hello, world")); } - } diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java index 2b5a57f35b..556c2c16ed 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java @@ -1,19 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.velocity; import java.io.IOException; import java.io.Reader; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.resource.Resource; import org.apache.velocity.runtime.resource.loader.ResourceLoader; @@ -21,19 +19,19 @@ import org.restlet.representation.Representation; /** - * Velocity resource loader based on a static map of representations or on a - * default representation. - * + * Velocity resource loader based on a static map of representations or on a default representation. + * * @author Jerome Louvel */ public class RepresentationResourceLoader extends ResourceLoader { /** The cache of template representations. */ - private static final Map store = new ConcurrentHashMap(); + private static final Map store = + new ConcurrentHashMap(); /** * Returns the cache of template representations. - * + * * @return The cache of template representations. */ public static Map getStore() { @@ -45,9 +43,8 @@ public static Map getStore() { /** * Constructor. - * - * @param defaultRepresentation - * The default representation to use. + * + * @param defaultRepresentation The default representation to use. */ public RepresentationResourceLoader(Representation defaultRepresentation) { this.defaultRepresentation = defaultRepresentation; @@ -65,7 +62,8 @@ public boolean isSourceModified(Resource resource) { } @Override - public Reader getResourceReader(String source, String encoding) throws ResourceNotFoundException { + public Reader getResourceReader(String source, String encoding) + throws ResourceNotFoundException { try { Representation resultRepresentation = getStore().get(source); @@ -84,7 +82,5 @@ public Reader getResourceReader(String source, String encoding) throws ResourceN } @Override - public void init(ExtProperties configuration) { - } - + public void init(ExtProperties configuration) {} } diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java index 5285715179..421d3ed829 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.velocity; import java.io.IOException; import java.util.Map; - import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; import org.restlet.Context; @@ -24,16 +22,15 @@ import org.restlet.util.Resolver; /** - * Filter response's entity and wrap it with a Velocity's template - * representation. By default, the template representation provides a data model - * based on the request and response objects. In order for the wrapping to - * happen, the representations must have the {@link Encoding#VELOCITY} encoding + * Filter response's entity and wrap it with a Velocity's template representation. By default, the + * template representation provides a data model based on the request and response objects. In order + * for the wrapping to happen, the representations must have the {@link Encoding#VELOCITY} encoding * set.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Thierry Boileau */ public class TemplateFilter extends Filter { @@ -44,18 +41,15 @@ public class TemplateFilter extends Filter { /** The template's data model as a resolver. */ private volatile Resolver resolverDataModel; - /** - * Constructor. - */ + /** Constructor. */ public TemplateFilter() { super(); } /** * Constructor. - * - * @param context - * The context. + * + * @param context The context. */ public TemplateFilter(Context context) { super(context); @@ -63,11 +57,9 @@ public TemplateFilter(Context context) { /** * Constructor. - * - * @param context - * The context. - * @param next - * The next Restlet. + * + * @param context The context. + * @param next The next Restlet. */ public TemplateFilter(Context context, Restlet next) { super(context, next); @@ -77,16 +69,12 @@ public TemplateFilter(Context context, Restlet next) { /** * Constructor. - * - * @param context - * The context. - * @param next - * The next Restlet. - * @param dataModel - * The filter's data model. + * + * @param context The context. + * @param next The next Restlet. + * @param dataModel The filter's data model. */ - public TemplateFilter(Context context, Restlet next, - Map dataModel) { + public TemplateFilter(Context context, Restlet next, Map dataModel) { super(context, next); this.mapDataModel = dataModel; this.resolverDataModel = null; @@ -94,16 +82,12 @@ public TemplateFilter(Context context, Restlet next, /** * Constructor. - * - * @param context - * The context. - * @param next - * The next Restlet. - * @param dataModel - * The filter's data model. + * + * @param context The context. + * @param next The next Restlet. + * @param dataModel The filter's data model. */ - public TemplateFilter(Context context, Restlet next, - Resolver dataModel) { + public TemplateFilter(Context context, Restlet next, Resolver dataModel) { super(context, next); this.mapDataModel = null; this.resolverDataModel = dataModel; @@ -112,15 +96,13 @@ public TemplateFilter(Context context, Restlet next, @Override protected void afterHandle(Request request, Response response) { if (response.isEntityAvailable() - && response.getEntity().getEncodings() - .contains(Encoding.VELOCITY)) { + && response.getEntity().getEncodings().contains(Encoding.VELOCITY)) { try { - final TemplateRepresentation representation = new TemplateRepresentation( - response.getEntity(), response.getEntity() - .getMediaType()); + final TemplateRepresentation representation = + new TemplateRepresentation( + response.getEntity(), response.getEntity().getMediaType()); - if ((this.mapDataModel == null) - && (this.resolverDataModel == null)) { + if ((this.mapDataModel == null) && (this.resolverDataModel == null)) { representation.setDataModel(request, response); } else { if (this.mapDataModel == null) { diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java index 08993733b3..e9dc44e4d0 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.velocity; import java.io.IOException; @@ -15,7 +14,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; - import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; @@ -32,15 +30,14 @@ import org.restlet.util.Resolver; /** - * Velocity template representation. Useful for dynamic string-based - * representations. - * + * Velocity template representation. Useful for dynamic string-based representations. + * * @author Jerome Louvel */ public class TemplateRepresentation extends WriterRepresentation { /** * Velocity context based on a Resolver. - * + * * @see Resolver */ private static class ResolverContext implements org.apache.velocity.context.Context { @@ -49,9 +46,8 @@ private static class ResolverContext implements org.apache.velocity.context.Cont /** * Constructor. - * - * @param resolver - * The resolver. + * + * @param resolver The resolver. */ public ResolverContext(Resolver resolver) { super(); @@ -64,20 +60,15 @@ public boolean containsKey(String key) { } /** - * Gets the value corresponding to the provided key from the context. - * - * @Param key The name of the desired value. - * @Return The value corresponding to the provided key. + * Gets the value corresponding to the provided key from the context. @Param key The name of + * the desired value. @Return The value corresponding to the provided key. */ public Object get(String key) { return this.resolver.resolve(key); } /** - * Returns null since a resolver does not know by advance the whole - * values. - * - * @Return null. + * Returns null since a resolver does not know by advance the whole values. @Return null. */ @Override public String[] getKeys() { @@ -86,11 +77,9 @@ public String[] getKeys() { /** * Returns null since a resolver as a data model cannot be updated. - * - * @param key - * The name to key the provided value with. - * @param value - * The corresponding value. + * + * @param key The name to key the provided value with. + * @param value The corresponding value. * @return null. */ public Object put(String key, Object value) { @@ -99,16 +88,14 @@ public Object put(String key, Object value) { /** * Does nothing since resolver as a data model cannot be updated. - * - * @param value - * The name of the value to remove. + * + * @param value The name of the value to remove. * @return null. */ @Override public Object remove(String key) { return null; } - } /** The template's data model. */ @@ -125,87 +112,82 @@ public Object remove(String key) { /** * Constructor based on a Velocity 'encoded' representation. - * - * @param templateRepresentation - * The representation to 'decode'. - * @param dataModel - * The Velocity template's data model. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The representation to 'decode'. + * @param dataModel The Velocity template's data model. + * @param mediaType The representation's media-type. * @throws IOException * @throws ParseErrorException * @throws ResourceNotFoundException */ - public TemplateRepresentation(Representation templateRepresentation, - Map dataModel, MediaType mediaType) + public TemplateRepresentation( + Representation templateRepresentation, + Map dataModel, + MediaType mediaType) throws ResourceNotFoundException, ParseErrorException, IOException { super(mediaType); setDataModel(dataModel); this.engine = null; this.template = new Template(); - CharacterSet charSet = (templateRepresentation.getCharacterSet() != null) ? templateRepresentation - .getCharacterSet() : CharacterSet.DEFAULT; + CharacterSet charSet = + (templateRepresentation.getCharacterSet() != null) + ? templateRepresentation.getCharacterSet() + : CharacterSet.DEFAULT; this.template.setEncoding(charSet.getName()); if (templateRepresentation.getModificationDate() != null) { - this.template.setLastModified(templateRepresentation - .getModificationDate().getTime()); + this.template.setLastModified(templateRepresentation.getModificationDate().getTime()); } this.template.setName("org.restlet.resource.representation"); this.template.setRuntimeServices(RuntimeSingleton.getRuntimeServices()); - this.template.setResourceLoader(new RepresentationResourceLoader( - templateRepresentation)); + this.template.setResourceLoader(new RepresentationResourceLoader(templateRepresentation)); this.template.process(); this.templateName = null; } /** * Constructor based on a Velocity 'encoded' representation. - * - * @param templateRepresentation - * The representation to 'decode'. - * @param mediaType - * The representation's media type. + * + * @param templateRepresentation The representation to 'decode'. + * @param mediaType The representation's media-type. * @throws IOException * @throws ParseErrorException * @throws ResourceNotFoundException */ - public TemplateRepresentation(Representation templateRepresentation, - MediaType mediaType) throws ResourceNotFoundException, - ParseErrorException, IOException { + public TemplateRepresentation(Representation templateRepresentation, MediaType mediaType) + throws ResourceNotFoundException, ParseErrorException, IOException { super(mediaType); this.engine = null; this.template = new Template(); - CharacterSet charSet = (templateRepresentation.getCharacterSet() != null) ? templateRepresentation - .getCharacterSet() : CharacterSet.DEFAULT; + CharacterSet charSet = + (templateRepresentation.getCharacterSet() != null) + ? templateRepresentation.getCharacterSet() + : CharacterSet.DEFAULT; this.template.setEncoding(charSet.getName()); - this.template.setLastModified((templateRepresentation - .getModificationDate() == null) ? new Date().getTime() - : templateRepresentation.getModificationDate().getTime()); + this.template.setLastModified( + (templateRepresentation.getModificationDate() == null) + ? new Date().getTime() + : templateRepresentation.getModificationDate().getTime()); this.template.setName("org.restlet.resource.representation"); this.template.setRuntimeServices(RuntimeSingleton.getRuntimeServices()); - this.template.setResourceLoader(new RepresentationResourceLoader( - templateRepresentation)); + this.template.setResourceLoader(new RepresentationResourceLoader(templateRepresentation)); this.template.process(); this.templateName = null; } /** * Constructor. - * - * @param templateName - * The Velocity template's name. The actual template is retrieved - * using the Velocity configuration. - * @param dataModel - * The Velocity template's data model. - * @param mediaType - * The representation's media type. + * + * @param templateName The Velocity template's name. The actual template is retrieved using the + * Velocity configuration. + * @param dataModel The Velocity template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(String templateName, - Map dataModel, MediaType mediaType) { + public TemplateRepresentation( + String templateName, Map dataModel, MediaType mediaType) { super(mediaType); try { @@ -220,12 +202,10 @@ public TemplateRepresentation(String templateName, /** * Constructor. - * - * @param templateName - * The Velocity template's name. The full path is resolved by the - * configuration. - * @param mediaType - * The representation's media type. + * + * @param templateName The Velocity template's name. The full path is resolved by the + * configuration. + * @param mediaType The representation's media-type. */ public TemplateRepresentation(String templateName, MediaType mediaType) { this(templateName, new ConcurrentHashMap(), mediaType); @@ -233,16 +213,13 @@ public TemplateRepresentation(String templateName, MediaType mediaType) { /** * Constructor. - * - * @param template - * The Velocity template. - * @param dataModel - * The Velocity template's data model. - * @param mediaType - * The representation's media type. + * + * @param template The Velocity template. + * @param dataModel The Velocity template's data model. + * @param mediaType The representation's media-type. */ - public TemplateRepresentation(Template template, - Map dataModel, MediaType mediaType) { + public TemplateRepresentation( + Template template, Map dataModel, MediaType mediaType) { super(mediaType); setDataModel(dataModel); this.engine = null; @@ -252,11 +229,9 @@ public TemplateRepresentation(Template template, /** * Constructor. - * - * @param template - * The Velocity template. - * @param mediaType - * The representation's media type. + * + * @param template The Velocity template. + * @param mediaType The representation's media-type. */ public TemplateRepresentation(Template template, MediaType mediaType) { super(mediaType); @@ -267,7 +242,7 @@ public TemplateRepresentation(Template template, MediaType mediaType) { /** * Returns the Velocity context. - * + * * @return The Velocity context. */ private org.apache.velocity.context.Context getContext() { @@ -276,7 +251,7 @@ private org.apache.velocity.context.Context getContext() { /** * Returns the Velocity engine. - * + * * @return The Velocity engine. */ public VelocityEngine getEngine() { @@ -285,7 +260,7 @@ public VelocityEngine getEngine() { /** * Returns the Velocity template. - * + * * @return The Velocity template. */ public Template getTemplate() { @@ -298,8 +273,7 @@ public Template getTemplate() { final Context context = Context.getCurrent(); if (context != null) { - context.getLogger().log(Level.WARNING, - "Unable to get template", e); + context.getLogger().log(Level.WARNING, "Unable to get template", e); } } } @@ -310,9 +284,8 @@ public Template getTemplate() { /** * Sets the Velocity context. - * - * @param context - * The Velocity context + * + * @param context The Velocity context */ private void setContext(org.apache.velocity.context.Context context) { this.context = context; @@ -320,36 +293,30 @@ private void setContext(org.apache.velocity.context.Context context) { /** * Sets the template's data model. - * - * @param dataModel - * The template's data model. + * + * @param dataModel The template's data model. */ public void setDataModel(Map dataModel) { setContext(new VelocityContext(dataModel)); } /** - * Sets the template's data model from a request/response pair. This default - * implementation uses a Resolver. - * + * Sets the template's data model from a request/response pair. This default implementation uses + * a Resolver. + * * @see Resolver * @see Resolver#createResolver(Request, Response) - * - * @param request - * The request where data are located. - * @param response - * The response where data are located. + * @param request The request where data are located. + * @param response The response where data are located. */ public void setDataModel(Request request, Response response) { - setContext(new ResolverContext(Resolver.createResolver(request, - response))); + setContext(new ResolverContext(Resolver.createResolver(request, response))); } /** * Sets the template's data model from a resolver. - * - * @param resolver - * The resolver. + * + * @param resolver The resolver. */ public void setDataModel(Resolver resolver) { setContext(new ResolverContext(resolver)); @@ -357,9 +324,8 @@ public void setDataModel(Resolver resolver) { /** * Writes the datum as a stream of characters. - * - * @param writer - * The writer to use when writing. + * + * @param writer The writer to use when writing. */ @Override public void write(Writer writer) throws IOException { @@ -371,15 +337,12 @@ public void write(Writer writer) throws IOException { final Context context = Context.getCurrent(); if (context != null) { - context.getLogger().log(Level.WARNING, - "Unable to process the template", e); + context.getLogger().log(Level.WARNING, "Unable to process the template", e); } e.printStackTrace(); - throw new IOException("Template processing error. " - + e.getMessage()); + throw new IOException("Template processing error. " + e.getMessage()); } } - } diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java index 906e29d4d2..938831a52c 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.velocity; import java.io.IOException; import java.util.List; - import org.apache.velocity.Template; import org.restlet.data.MediaType; import org.restlet.data.Preference; @@ -22,9 +20,9 @@ import org.restlet.resource.Resource; /** - * Converter between the Velocity Template objects and Representations. The - * adjoined data model is based on the request and response objects. - * + * Converter between the Velocity Template objects and Representations. The adjoined data model is + * based on the request and response objects. + * * @author Thierry Boileau. */ public class VelocityConverter extends ConverterHelper { @@ -62,16 +60,18 @@ public float score(Representation source, Class target, Resource resource } @Override - public T toObject(Representation source, Class target, - Resource resource) throws IOException { + public T toObject(Representation source, Class target, Resource resource) + throws IOException { return null; } @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { if (source instanceof Template) { - TemplateRepresentation tr = new TemplateRepresentation((Template) source, target.getMediaType()); + TemplateRepresentation tr = + new TemplateRepresentation((Template) source, target.getMediaType()); tr.setDataModel(resource.getRequest(), resource.getResponse()); return tr; } diff --git a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java index 5e3ceaf047..10c753b9c8 100644 --- a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java +++ b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java @@ -1,25 +1,29 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.velocity; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.restlet.*; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.LocalReference; import org.restlet.data.Method; import org.restlet.data.Protocol; import org.restlet.engine.Engine; import org.restlet.resource.Directory; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test case for template filters. * @@ -29,7 +33,7 @@ public class TemplateFilterTestCase { @Test public void representationShouldBeUsedAsTemplate() throws Exception { - Request request = new Request(Method.GET,"/template.txt.vm"); + Request request = new Request(Method.GET, "/template.txt.vm"); Response response = testApplication.handle(request); assertEquals("Method=GET/Path=/template.txt.vm", response.getEntity().getText()); } @@ -56,7 +60,11 @@ private static class MyVelocityApplication extends Application { @Override public Restlet createInboundRoot() { - final Directory directory = new Directory(getContext(), LocalReference.createClapReference(TemplateFilterTestCase.class.getPackage())); + final Directory directory = + new Directory( + getContext(), + LocalReference.createClapReference( + TemplateFilterTestCase.class.getPackage())); // Create a Directory that manages a local directory return new TemplateFilter(getContext(), directory); diff --git a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java index 135321e816..b82aa15af9 100644 --- a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java +++ b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java @@ -1,14 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.velocity; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.io.FileWriter; +import java.util.Map; +import java.util.TreeMap; import org.junit.jupiter.api.Test; import org.restlet.data.LocalReference; import org.restlet.data.MediaType; @@ -17,16 +22,9 @@ import org.restlet.representation.Representation; import org.restlet.resource.ClientResource; -import java.io.File; -import java.io.FileWriter; -import java.util.Map; -import java.util.TreeMap; - -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test case for the Velocity extension. - * + * * @author Jerome Louvel */ public class VelocityTestCase { @@ -34,8 +32,7 @@ public class VelocityTestCase { @Test public void testRepresentationTemplate() throws Exception { // Create a temporary directory for the tests - File testDir = new File(System.getProperty("java.io.tmpdir"), - "VelocityTestCase"); + File testDir = new File(System.getProperty("java.io.tmpdir"), "VelocityTestCase"); testDir.mkdir(); // Create a temporary template file @@ -51,8 +48,8 @@ public void testRepresentationTemplate() throws Exception { Reference ref = LocalReference.createFileReference(testFile); ClientResource r = new ClientResource(ref); Representation templateFile = r.get(); - TemplateRepresentation tr = new TemplateRepresentation(templateFile, - map, MediaType.TEXT_PLAIN); + TemplateRepresentation tr = + new TemplateRepresentation(templateFile, map, MediaType.TEXT_PLAIN); final String result = tr.getText(); assertEquals("Value=myValue", result); @@ -64,8 +61,7 @@ public void testRepresentationTemplate() throws Exception { @Test public void testStandardTemplate() throws Exception { // Create a temporary directory for the tests - final File testDir = new File(System.getProperty("java.io.tmpdir"), - "VelocityTestCase"); + final File testDir = new File(System.getProperty("java.io.tmpdir"), "VelocityTestCase"); testDir.mkdir(); // Create a temporary template file @@ -78,10 +74,9 @@ public void testStandardTemplate() throws Exception { map.put("value", "myValue"); // Standard approach - final TemplateRepresentation tr = new TemplateRepresentation( - testFile.getName(), map, MediaType.TEXT_PLAIN); - tr.getEngine().setProperty("file.resource.loader.path", - testDir.getAbsolutePath()); + final TemplateRepresentation tr = + new TemplateRepresentation(testFile.getName(), map, MediaType.TEXT_PLAIN); + tr.getEngine().setProperty("file.resource.loader.path", testDir.getAbsolutePath()); final String result = tr.getText(); assertEquals("Value=myValue", result); diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java index 33633c2243..50f00d76de 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java @@ -1,18 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.io.IOException; import java.io.InputStream; import java.io.Writer; - import org.restlet.data.CharacterSet; import org.restlet.data.MediaType; import org.restlet.representation.Representation; @@ -22,9 +20,9 @@ import org.xml.sax.SAXException; /** - * XML representation based on a DOM document. DOM is a standard XML object - * model defined by the W3C. - * + * XML representation based on a DOM document. DOM is a standard XML object model defined by the + * W3C. + * * @author Jerome Louvel */ public class DomRepresentation extends XmlRepresentation { @@ -37,18 +35,15 @@ public class DomRepresentation extends XmlRepresentation { /** The source XML representation. */ private volatile Representation xmlRepresentation; - /** - * Default constructor. Uses the {@link MediaType#TEXT_XML} media type. - */ + /** Default constructor. Uses the {@link MediaType#TEXT_XML} media type. */ public DomRepresentation() throws IOException { this(MediaType.TEXT_XML); } /** * Constructor for an empty document. - * - * @param mediaType - * The representation's media type. + * + * @param mediaType The representation's media-type. */ public DomRepresentation(MediaType mediaType) throws IOException { super(mediaType); @@ -57,11 +52,9 @@ public DomRepresentation(MediaType mediaType) throws IOException { /** * Constructor from an existing DOM document. - * - * @param mediaType - * The representation's media type. - * @param xmlDocument - * The source DOM document. + * + * @param mediaType The representation's media-type. + * @param xmlDocument The source DOM document. */ public DomRepresentation(MediaType mediaType, Document xmlDocument) { super(mediaType); @@ -70,9 +63,8 @@ public DomRepresentation(MediaType mediaType, Document xmlDocument) { /** * Constructor. - * - * @param xmlRepresentation - * A source XML representation to parse. + * + * @param xmlRepresentation A source XML representation to parse. */ public DomRepresentation(Representation xmlRepresentation) { super((xmlRepresentation == null) ? null : xmlRepresentation.getMediaType()); @@ -81,31 +73,25 @@ public DomRepresentation(Representation xmlRepresentation) { } /** - * Creates a new JAXP Transformer object that will be used to serialize this - * DOM. This method may be overridden to set custom properties on - * the Transformer. - * + * Creates a new JAXP Transformer object that will be used to serialize this DOM. This method + * may be overridden to set custom properties on the Transformer. + * * @return The transformer to be used for serialization. */ - protected javax.xml.transform.Transformer createTransformer() - throws IOException { + protected javax.xml.transform.Transformer createTransformer() throws IOException { try { - javax.xml.transform.Transformer transformer = javax.xml.transform.TransformerFactory - .newInstance().newTransformer(); - transformer.setOutputProperty( - javax.xml.transform.OutputKeys.METHOD, "xml"); + javax.xml.transform.Transformer transformer = + javax.xml.transform.TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, "xml"); transformer.setOutputProperty( - javax.xml.transform.OutputKeys.INDENT, - isIndenting() ? "yes" : "no"); + javax.xml.transform.OutputKeys.INDENT, isIndenting() ? "yes" : "no"); if (getCharacterSet() != null) { transformer.setOutputProperty( - javax.xml.transform.OutputKeys.ENCODING, - getCharacterSet().getName()); + javax.xml.transform.OutputKeys.ENCODING, getCharacterSet().getName()); } else { transformer.setOutputProperty( - javax.xml.transform.OutputKeys.ENCODING, - CharacterSet.ISO_8859_1.getName()); + javax.xml.transform.OutputKeys.ENCODING, CharacterSet.ISO_8859_1.getName()); } DocumentType docType = getDocument().getDoctype(); @@ -126,16 +112,15 @@ protected javax.xml.transform.Transformer createTransformer() return transformer; } catch (javax.xml.transform.TransformerConfigurationException tce) { - throw new IOException("Couldn't write the XML representation: " - + tce.getMessage()); + throw new IOException("Couldn't write the XML representation: " + tce.getMessage()); } } /** - * Returns the wrapped DOM document. If no document is defined yet, it - * attempts to parse the XML representation eventually given at construction - * time. Otherwise, it just creates a new document. - * + * Returns the wrapped DOM document. If no document is defined yet, it attempts to parse the XML + * representation eventually given at construction time. Otherwise, it just creates a new + * document. + * * @return The wrapped DOM document. */ @Override @@ -145,7 +130,8 @@ public Document getDocument() throws IOException { try { this.document = getDocumentBuilder().parse(getInputSource()); } catch (SAXException se) { - throw new IOException("Couldn't read the XML representation. " + se.getMessage()); + throw new IOException( + "Couldn't read the XML representation. " + se.getMessage()); } } else { this.document = getDocumentBuilder().newDocument(); @@ -157,7 +143,7 @@ public Document getDocument() throws IOException { /** * Returns a DOM source. - * + * * @return A DOM source. */ @Override @@ -175,7 +161,7 @@ public InputSource getInputSource() throws IOException { /** * Indicates if the XML serialization should be indented. False by default. - * + * * @return True if the XML serialization should be indented. */ public boolean isIndenting() { @@ -183,8 +169,8 @@ public boolean isIndenting() { } /** - * Releases the wrapped DOM document and the source XML representation if - * they have been defined. + * Releases the wrapped DOM document and the source XML representation if they have been + * defined. */ @Override public void release() { @@ -199,9 +185,8 @@ public void release() { /** * Sets the wrapped DOM document. - * - * @param dom - * The wrapped DOM document. + * + * @param dom The wrapped DOM document. */ public void setDocument(Document dom) { this.document = dom; @@ -209,9 +194,8 @@ public void setDocument(Document dom) { /** * Indicates if the XML serialization should be indented. - * - * @param indenting - * True if the XML serialization should be indented. + * + * @param indenting True if the XML serialization should be indented. */ public void setIndenting(boolean indenting) { this.indenting = indenting; @@ -222,19 +206,16 @@ public void write(Writer writer) throws IOException { try { if (getDocument() != null) { final javax.xml.transform.Transformer transformer = createTransformer(); - transformer.transform(new javax.xml.transform.dom.DOMSource( - getDocument()), + transformer.transform( + new javax.xml.transform.dom.DOMSource(getDocument()), new javax.xml.transform.stream.StreamResult(writer)); } } catch (javax.xml.transform.TransformerConfigurationException tce) { - throw new IOException("Couldn't write the XML representation: " - + tce.getMessage()); + throw new IOException("Couldn't write the XML representation: " + tce.getMessage()); } catch (javax.xml.transform.TransformerException te) { - throw new IOException("Couldn't write the XML representation: " - + te.getMessage()); + throw new IOException("Couldn't write the XML representation: " + te.getMessage()); } catch (javax.xml.transform.TransformerFactoryConfigurationError tfce) { - throw new IOException("Couldn't write the XML representation: " - + tfce.getMessage()); + throw new IOException("Couldn't write the XML representation: " + tfce.getMessage()); } } } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java index 1549d7a77a..62cac6381c 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java @@ -1,35 +1,30 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.util.AbstractList; - import org.w3c.dom.Node; /** - * DOM nodes set that implements the standard List interface for easier - * iteration. - * + * DOM nodes set that implements the standard List interface for easier iteration. + * * @author Jerome Louvel */ -public class NodeList extends AbstractList implements - org.w3c.dom.NodeList { +public class NodeList extends AbstractList implements org.w3c.dom.NodeList { /** The wrapped node list. */ private volatile org.w3c.dom.NodeList nodes; /** * Constructor. - * - * @param nodes - * The node list to wrap. + * + * @param nodes The node list to wrap. */ public NodeList(org.w3c.dom.NodeList nodes) { this.nodes = nodes; @@ -40,16 +35,12 @@ public Node get(int index) { return this.nodes.item(index); } - /** - * {@inheritDoc org.w3c.dom.NodeList#getLength()} - */ + /** {@inheritDoc org.w3c.dom.NodeList#getLength()} */ public int getLength() { return this.nodes.getLength(); } - /** - * {@inheritDoc org.w3c.dom.NodeList#item(int)} - */ + /** {@inheritDoc org.w3c.dom.NodeList#item(int)} */ public Node item(int index) { return this.nodes.item(index); } @@ -58,5 +49,4 @@ public Node item(int index) { public int size() { return this.nodes.getLength(); } - } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java index 1d8d08d68c..eafcc02705 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.io.IOException; import java.io.Writer; - import javax.xml.XMLConstants; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Result; @@ -22,7 +20,6 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.sax.SAXSource; - import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.w3c.dom.Document; @@ -31,32 +28,29 @@ import org.xml.sax.XMLReader; /** - * XML representation for SAX events processing. The purpose is to create a - * streamable content based on a custom Java object model instead of a neutral - * DOM tree. This domain object can then be directly modified and efficiently - * serialized at a later time.
+ * XML representation for SAX events processing. The purpose is to create a streamable content based + * on a custom Java object model instead of a neutral DOM tree. This domain object can then be + * directly modified and efficiently serialized at a later time.
*
- * Subclasses only need to override the ContentHandler methods required for the - * reading and also the write(XmlWriter writer) method when serialization is - * requested.
+ * Subclasses only need to override the ContentHandler methods required for the reading and also the + * write(XmlWriter writer) method when serialization is requested.
*
- * SECURITY WARNING: Using XML parsers configured to not prevent nor limit - * document type definition (DTD) entity resolution can expose the parser to an - * XML Entity Expansion injection attack, see + * SECURITY WARNING: Using XML parsers configured to not prevent nor limit document type definition + * (DTD) entity resolution can expose the parser to an XML Entity Expansion injection attack, see * https://github.com/restlet/restlet-framework-java/wiki/XEE-security-enhancements. - * + * * @author Jerome Louvel */ public class SaxRepresentation extends XmlRepresentation { /** - * True for turning on secure parsing XML representations; default value - * provided by system property "org.restlet.ext.xml.secureProcessing", true - * by default. + * True for turning on secure parsing XML representations; default value provided by system + * property "org.restlet.ext.xml.secureProcessing", true by default. */ - public static final boolean XML_SECURE_PROCESSING = (System - .getProperty("org.restlet.ext.xml.secureProcessing") == null) ? true - : Boolean.getBoolean("org.restlet.ext.xml.secureProcessing"); + public static final boolean XML_SECURE_PROCESSING = + (System.getProperty("org.restlet.ext.xml.secureProcessing") == null) + ? true + : Boolean.getBoolean("org.restlet.ext.xml.secureProcessing"); /** Limits potential XML overflow attacks. */ private boolean secureProcessing; @@ -67,18 +61,15 @@ public class SaxRepresentation extends XmlRepresentation { /** The source XML representation. */ private volatile Representation xmlRepresentation; - /** - * Default constructor. Uses the {@link MediaType#TEXT_XML} media type. - */ + /** Default constructor. Uses the {@link MediaType#TEXT_XML} media type. */ public SaxRepresentation() { this(MediaType.TEXT_XML); } /** * Constructor. - * - * @param mediaType - * The representation media type. + * + * @param mediaType The representation media type. */ public SaxRepresentation(MediaType mediaType) { super(mediaType); @@ -87,26 +78,21 @@ public SaxRepresentation(MediaType mediaType) { /** * Constructor. - * - * @param mediaType - * The representation's media type. - * @param xmlDocument - * A DOM document to parse. + * + * @param mediaType The representation's media-type. + * @param xmlDocument A DOM document to parse. */ public SaxRepresentation(MediaType mediaType, Document xmlDocument) { super(mediaType); this.secureProcessing = XML_SECURE_PROCESSING; - this.source = new SAXSource( - SAXSource.sourceToInputSource(new DOMSource(xmlDocument))); + this.source = new SAXSource(SAXSource.sourceToInputSource(new DOMSource(xmlDocument))); } /** * Constructor. - * - * @param mediaType - * The representation's media type. - * @param xmlSource - * A SAX input source to parse. + * + * @param mediaType The representation's media-type. + * @param xmlSource A SAX input source to parse. */ public SaxRepresentation(MediaType mediaType, InputSource xmlSource) { super(mediaType); @@ -116,11 +102,9 @@ public SaxRepresentation(MediaType mediaType, InputSource xmlSource) { /** * Constructor. - * - * @param mediaType - * The representation's media type. - * @param xmlSource - * A JAXP source to parse. + * + * @param mediaType The representation's media-type. + * @param xmlSource A JAXP source to parse. */ public SaxRepresentation(MediaType mediaType, SAXSource xmlSource) { super(mediaType); @@ -130,33 +114,29 @@ public SaxRepresentation(MediaType mediaType, SAXSource xmlSource) { /** * Constructor. - * - * @param xmlRepresentation - * A source XML representation to parse. + * + * @param xmlRepresentation A source XML representation to parse. */ public SaxRepresentation(Representation xmlRepresentation) { - super((xmlRepresentation == null) ? null : xmlRepresentation - .getMediaType()); + super((xmlRepresentation == null) ? null : xmlRepresentation.getMediaType()); this.secureProcessing = XML_SECURE_PROCESSING; this.xmlRepresentation = xmlRepresentation; } @Override public InputSource getInputSource() throws IOException { - return (getSaxSource() == null) ? null : getSaxSource() - .getInputSource(); + return (getSaxSource() == null) ? null : getSaxSource().getInputSource(); } /** - * Returns the SAX source that can be parsed by the - * {@link #parse(ContentHandler)} method or used for an XSLT transformation. + * Returns the SAX source that can be parsed by the {@link #parse(ContentHandler)} method or + * used for an XSLT transformation. */ @Override public SAXSource getSaxSource() throws IOException { if (this.source == null && this.xmlRepresentation != null) { if (xmlRepresentation instanceof XmlRepresentation) { - this.source = ((XmlRepresentation) xmlRepresentation) - .getSaxSource(); + this.source = ((XmlRepresentation) xmlRepresentation).getSaxSource(); } else { try { SAXParserFactory spf = SAXParserFactory.newInstance(); @@ -172,8 +152,7 @@ public SAXSource getSaxSource() throws IOException { } spf.setXIncludeAware(isXIncludeAware()); - spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, - isSecureProcessing()); + spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isSecureProcessing()); spf.setFeature( "http://xml.org/sax/features/external-general-entities", isExpandingEntityRefs()); @@ -181,17 +160,17 @@ public SAXSource getSaxSource() throws IOException { "http://xml.org/sax/features/external-parameter-entities", isExpandingEntityRefs()); XMLReader xmlReader = spf.newSAXParser().getXMLReader(); - this.source = new SAXSource(xmlReader, new InputSource( - xmlRepresentation.getReader())); + this.source = + new SAXSource( + xmlReader, new InputSource(xmlRepresentation.getReader())); } catch (Exception e) { - throw new IOException( - "Unable to create customized SAX source", e); + throw new IOException("Unable to create customized SAX source", e); } } if (xmlRepresentation.getLocationRef() != null) { - this.source.setSystemId(xmlRepresentation.getLocationRef() - .getTargetRef().toString()); + this.source.setSystemId( + xmlRepresentation.getLocationRef().getTargetRef().toString()); } } @@ -200,7 +179,7 @@ public SAXSource getSaxSource() throws IOException { /** * Indicates if it limits potential XML overflow attacks. - * + * * @return True if it limits potential XML overflow attacks. */ public boolean isSecureProcessing() { @@ -209,28 +188,23 @@ public boolean isSecureProcessing() { /** * Parses the source and sends SAX events to a content handler. - * - * @param contentHandler - * The SAX content handler to use for parsing. + * + * @param contentHandler The SAX content handler to use for parsing. */ public void parse(ContentHandler contentHandler) throws IOException { if (contentHandler != null) { try { Result result = new SAXResult(contentHandler); - TransformerFactory.newInstance().newTransformer() - .transform(getSaxSource(), result); + TransformerFactory.newInstance().newTransformer().transform(getSaxSource(), result); } catch (TransformerConfigurationException tce) { throw new IOException( - "Couldn't parse the source representation: " - + tce.getMessage(), tce); + "Couldn't parse the source representation: " + tce.getMessage(), tce); } catch (TransformerException te) { throw new IOException( - "Couldn't parse the source representation: " - + te.getMessage(), te); + "Couldn't parse the source representation: " + te.getMessage(), te); } catch (TransformerFactoryConfigurationError tfce) { throw new IOException( - "Couldn't parse the source representation: " - + tfce.getMessage(), tfce); + "Couldn't parse the source representation: " + tfce.getMessage(), tfce); } } else { throw new IOException( @@ -238,9 +212,7 @@ public void parse(ContentHandler contentHandler) throws IOException { } } - /** - * Releases the namespaces map. - */ + /** Releases the namespaces map. */ @Override public void release() { if (this.source != null) { @@ -254,11 +226,9 @@ public void release() { } /** - * Sets a SAX source that can be parsed by the - * {@link #parse(ContentHandler)} method. - * - * @param source - * A SAX source. + * Sets a SAX source that can be parsed by the {@link #parse(ContentHandler)} method. + * + * @param source A SAX source. */ public void setSaxSource(SAXSource source) { this.source = source; @@ -266,9 +236,8 @@ public void setSaxSource(SAXSource source) { /** * Indicates if it limits potential XML overflow attacks. - * - * @param secureProcessing - * True if it limits potential XML overflow attacks. + * + * @param secureProcessing True if it limits potential XML overflow attacks. */ public void setSecureProcessing(boolean secureProcessing) { this.secureProcessing = secureProcessing; @@ -281,13 +250,11 @@ public void write(Writer writer) throws IOException { } /** - * Writes the representation to a XML writer. The default implementation - * calls {@link #parse(ContentHandler)} using the {@link XmlWriter} - * parameter as the content handler. This behavior is intended to be - * overridden. - * - * @param writer - * The XML writer to write to. + * Writes the representation to a XML writer. The default implementation calls {@link + * #parse(ContentHandler)} using the {@link XmlWriter} parameter as the content handler. This + * behavior is intended to be overridden. + * + * @param writer The XML writer to write to. * @throws IOException */ public void write(XmlWriter writer) throws IOException { diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java index fbe9ad58b2..ab7b3bf4e9 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java @@ -1,19 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.io.IOException; import java.io.Writer; import java.util.HashMap; import java.util.Map; - import javax.xml.transform.ErrorListener; import javax.xml.transform.Result; import javax.xml.transform.Source; @@ -30,7 +28,6 @@ import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; - import org.restlet.Context; import org.restlet.ext.xml.internal.AbstractXmlReader; import org.restlet.ext.xml.internal.ContextResolver; @@ -42,69 +39,60 @@ import org.xml.sax.XMLReader; /** - * Representation able to apply an XSLT transformation. The internal JAXP - * transformer is created when the getTransformer() method is first called. So, - * if you need to specify a custom URI resolver, do it before actually using the - * representation for a transformation.
+ * Representation able to apply an XSLT transformation. The internal JAXP transformer is created + * when the getTransformer() method is first called. So, if you need to specify a custom URI + * resolver, do it before actually using the representation for a transformation.
*
- * This representation should be viewed as a wrapper representation that applies - * a transform sheet on a source representation when it is read or written out. - * Therefore, it isn't intended to be reused on different sources. For this use - * case, you should instead use the {@link org.restlet.routing.Transformer} - * filter. - * + * This representation should be viewed as a wrapper representation that applies a transform sheet + * on a source representation when it is read or written out. Therefore, it isn't intended to be + * reused on different sources. For this use case, you should instead use the {@link + * org.restlet.routing.Transformer} filter. + * * @author Jerome Louvel */ public class TransformRepresentation extends WriterRepresentation { /** - * Wraps a source representation into a {@link SAXSource}. This method can - * detect other {@link XmlRepresentation} instances to use their - * {@link XmlRepresentation#getSaxSource()} method as well as other - * {@link TransformRepresentation} instances to support transformation - * chaining. - * - * @param representation - * The source representation. + * Wraps a source representation into a {@link SAXSource}. This method can detect other {@link + * XmlRepresentation} instances to use their {@link XmlRepresentation#getSaxSource()} method as + * well as other {@link TransformRepresentation} instances to support transformation chaining. + * + * @param representation The source representation. * @return The SAX source. * @throws IOException */ - public static SAXSource toSaxSource(Representation representation) - throws IOException { + public static SAXSource toSaxSource(Representation representation) throws IOException { SAXSource result = null; if (representation instanceof XmlRepresentation) { result = ((XmlRepresentation) representation).getSaxSource(); } else if (representation instanceof TransformRepresentation) { final TransformRepresentation source = (TransformRepresentation) representation; - XMLReader reader = new AbstractXmlReader() { - - /** - * Parses the input source by sending the result event to the - * XML reader's content handler. - * - * @param input - * The input source. - */ - public void parse(InputSource input) throws IOException, - SAXException { - try { - source.getTransformer().transform( - source.getSaxSource(), - new SAXResult(getContentHandler())); - } catch (TransformerException te) { - throw new IOException("Transformer exception. " - + te.getMessage()); - } - } - - public void parse(String systemId) throws IOException, - SAXException { - throw new IllegalStateException("Not implemented"); - } - }; - - result = new SAXSource(reader, new InputSource( - representation.getReader())); + XMLReader reader = + new AbstractXmlReader() { + + /** + * Parses the input source by sending the result event to the XML reader's + * content handler. + * + * @param input The input source. + */ + public void parse(InputSource input) throws IOException, SAXException { + try { + source.getTransformer() + .transform( + source.getSaxSource(), + new SAXResult(getContentHandler())); + } catch (TransformerException te) { + throw new IOException("Transformer exception. " + te.getMessage()); + } + } + + public void parse(String systemId) throws IOException, SAXException { + throw new IllegalStateException("Not implemented"); + } + }; + + result = new SAXSource(reader, new InputSource(representation.getReader())); } else { // Prepare the source and result documents result = new SAXSource(new InputSource(representation.getReader())); @@ -112,8 +100,7 @@ public void parse(String systemId) throws IOException, // Copy the representation's URI as an XML system ID. if (representation.getLocationRef() != null) { - result.setSystemId(representation.getLocationRef().getTargetRef() - .toString()); + result.setSystemId(representation.getLocationRef().getTargetRef().toString()); } return result; @@ -141,63 +128,50 @@ public void parse(String systemId) throws IOException, private volatile URIResolver uriResolver; /** - * Constructor. Note that a default URI resolver will be created based on - * the given context. - * - * @param context - * The parent context. - * @param source - * The source representation to transform. - * @param transformSheet - * The XSLT transform sheet to apply. + * Constructor. Note that a default URI resolver will be created based on the given context. + * + * @param context The parent context. + * @param source The source representation to transform. + * @param transformSheet The XSLT transform sheet to apply. */ - public TransformRepresentation(Context context, Representation source, - Representation transformSheet) { - this((context == null) ? null : new ContextResolver(context), source, - transformSheet); + public TransformRepresentation( + Context context, Representation source, Representation transformSheet) { + this((context == null) ? null : new ContextResolver(context), source, transformSheet); } /** * Default constructor. - * - * @param source - * The source representation to transform. - * @param transformSheet - * The XSLT transform sheet to apply. + * + * @param source The source representation to transform. + * @param transformSheet The XSLT transform sheet to apply. */ - public TransformRepresentation(Representation source, - Representation transformSheet) { + public TransformRepresentation(Representation source, Representation transformSheet) { this((URIResolver) null, source, transformSheet); } /** - * Constructor. Note that a default URI resolver will be created based on - * the given context. - * - * @param uriResolver - * The JAXP URI resolver. - * @param source - * The source representation to transform. - * @param transformSheet - * The XSLT transform sheet to apply. + * Constructor. Note that a default URI resolver will be created based on the given context. + * + * @param uriResolver The JAXP URI resolver. + * @param source The source representation to transform. + * @param transformSheet The XSLT transform sheet to apply. */ - public TransformRepresentation(URIResolver uriResolver, - Representation source, Representation transformSheet) { + public TransformRepresentation( + URIResolver uriResolver, Representation source, Representation transformSheet) { this(uriResolver, source, transformSheet, null); } /** * Constructor. - * - * @param uriResolver - * The optional JAXP URI resolver. - * @param source - * The source representation to transform. - * @param templates - * The precompiled JAXP template. + * + * @param uriResolver The optional JAXP URI resolver. + * @param source The source representation to transform. + * @param templates The precompiled JAXP template. */ - private TransformRepresentation(URIResolver uriResolver, - Representation source, Representation transformSheet, + private TransformRepresentation( + URIResolver uriResolver, + Representation source, + Representation transformSheet, Templates templates) { super(null); this.sourceRepresentation = source; @@ -211,23 +185,20 @@ private TransformRepresentation(URIResolver uriResolver, /** * Constructor. - * - * @param uriResolver - * The optional JAXP URI resolver. - * @param source - * The source representation to transform. - * @param templates - * The precompiled JAXP template. + * + * @param uriResolver The optional JAXP URI resolver. + * @param source The source representation to transform. + * @param templates The precompiled JAXP template. */ - public TransformRepresentation(URIResolver uriResolver, - Representation source, Templates templates) { + public TransformRepresentation( + URIResolver uriResolver, Representation source, Templates templates) { this(uriResolver, source, null, templates); } /** - * Returns the transformer's error listener. Default value is null, leaving - * the original listener intact. - * + * Returns the transformer's error listener. Default value is null, leaving the original + * listener intact. + * * @return The transformer's error listener. */ public ErrorListener getErrorListener() { @@ -236,7 +207,7 @@ public ErrorListener getErrorListener() { /** * Returns the modifiable map of JAXP transformer output properties. - * + * * @return The JAXP transformer output properties. */ public Map getOutputProperties() { @@ -245,7 +216,7 @@ public Map getOutputProperties() { /** * Returns the modifiable map of JAXP transformer parameters. - * + * * @return The JAXP transformer parameters. */ public Map getParameters() { @@ -253,9 +224,9 @@ public Map getParameters() { } /** - * Returns the SAX source associated to the source representation. - * - * @return The SAX source associated to the source representation. + * Returns the SAX source associated with the source representation. + * + * @return The SAX source associated with the source representation. * @throws IOException */ public SAXSource getSaxSource() throws IOException { @@ -264,18 +235,17 @@ public SAXSource getSaxSource() throws IOException { /** * Returns the default SAX transformer factory. - * + * * @return The default SAX transformer factory. */ private SAXTransformerFactory getSaxTransformerFactory() { - SAXTransformerFactory result = (SAXTransformerFactory) TransformerFactory - .newInstance(); + SAXTransformerFactory result = (SAXTransformerFactory) TransformerFactory.newInstance(); return result; } /** * Returns the source representation to transform. - * + * * @return The source representation to transform. */ public Representation getSourceRepresentation() { @@ -283,10 +253,9 @@ public Representation getSourceRepresentation() { } /** - * Returns the templates to be used and reused. If no one exists, it creates - * a new one based on the transformSheet representation and on the URI - * resolver. - * + * Returns the templates to be used and reused. If no one exists, it creates a new one based on + * the transformSheet representation and on the URI resolver. + * * @return The templates to be used and reused. */ public Templates getTemplates() throws IOException { @@ -294,17 +263,16 @@ public Templates getTemplates() throws IOException { if (getTransformSheet() != null) { try { // Prepare the XSLT transformer documents - final StreamSource transformSource = new StreamSource( - getTransformSheet().getStream()); + final StreamSource transformSource = + new StreamSource(getTransformSheet().getStream()); if (getTransformSheet().getLocationRef() != null) { - transformSource.setSystemId(getTransformSheet() - .getLocationRef().getTargetRef().toString()); + transformSource.setSystemId( + getTransformSheet().getLocationRef().getTargetRef().toString()); } // Create the transformer factory - final TransformerFactory transformerFactory = TransformerFactory - .newInstance(); + final TransformerFactory transformerFactory = TransformerFactory.newInstance(); // Set the URI resolver if (getUriResolver() != null) { @@ -312,12 +280,10 @@ public Templates getTemplates() throws IOException { } // Create a new transformer - this.templates = transformerFactory - .newTemplates(transformSource); + this.templates = transformerFactory.newTemplates(transformSource); } catch (TransformerConfigurationException tce) { throw new IOException( - "Transformer configuration exception. " - + tce.getMessage()); + "Transformer configuration exception. " + tce.getMessage()); } } } @@ -326,9 +292,9 @@ public Templates getTemplates() throws IOException { } /** - * Returns a new transformer to be used. Creation is based on the - * {@link #getTemplates()}.newTransformer() method. - * + * Returns a new transformer to be used. Creation is based on the {@link + * #getTemplates()}.newTransformer() method. + * * @return The new transformer to be used. */ public Transformer getTransformer() throws IOException { @@ -353,25 +319,22 @@ public Transformer getTransformer() throws IOException { } for (String name : getOutputProperties().keySet()) { - result.setOutputProperty(name, - getOutputProperties().get(name)); + result.setOutputProperty(name, getOutputProperties().get(name)); } } } catch (TransformerConfigurationException tce) { - throw new IOException("Transformer configuration exception. " - + tce.getMessage()); + throw new IOException("Transformer configuration exception. " + tce.getMessage()); } catch (TransformerFactoryConfigurationError tfce) { throw new IOException( - "Transformer factory configuration exception. " - + tfce.getMessage()); + "Transformer factory configuration exception. " + tfce.getMessage()); } return result; } /** - * Returns the SAX transformer handler associated to the transform sheet. - * + * Returns the SAX transformer handler associated with the transform sheet. + * * @return The SAX transformer handler. * @throws IOException */ @@ -381,11 +344,9 @@ public TransformerHandler getTransformerHandler() throws IOException { if (templates != null) { try { - result = getSaxTransformerFactory().newTransformerHandler( - templates); + result = getSaxTransformerFactory().newTransformerHandler(templates); } catch (TransformerConfigurationException tce) { - throw new IOException("Transformer configuration exception. " - + tce.getMessage()); + throw new IOException("Transformer configuration exception. " + tce.getMessage()); } } @@ -394,7 +355,7 @@ public TransformerHandler getTransformerHandler() throws IOException { /** * Returns the XSLT transform sheet to apply to the source representation. - * + * * @return The XSLT transform sheet to apply. */ public Representation getTransformSheet() { @@ -403,7 +364,7 @@ public Representation getTransformSheet() { /** * Returns the URI resolver. - * + * * @return The URI resolver. */ public URIResolver getUriResolver() { @@ -412,7 +373,7 @@ public URIResolver getUriResolver() { /** * Returns the SAX XML filter applying the transform sheet to its input. - * + * * @return The SAX XML filter. * @throws IOException */ @@ -424,8 +385,7 @@ public XMLFilter getXmlFilter() throws IOException { try { result = getSaxTransformerFactory().newXMLFilter(templates); } catch (TransformerConfigurationException tce) { - throw new IOException("Transformer configuration exception. " - + tce.getMessage()); + throw new IOException("Transformer configuration exception. " + tce.getMessage()); } } @@ -433,8 +393,8 @@ public XMLFilter getXmlFilter() throws IOException { } /** - * Releases the source and transform sheet representations, the transformer - * and the URI resolver. + * Releases the source and transform sheet representations, the transformer and the URI + * resolver. */ @Override public void release() { @@ -461,9 +421,8 @@ public void release() { /** * Sets the transformer's error listener. - * - * @param errorListener - * The transformer's error listener. + * + * @param errorListener The transformer's error listener. */ public void setErrorListener(ErrorListener errorListener) { this.errorListener = errorListener; @@ -471,9 +430,8 @@ public void setErrorListener(ErrorListener errorListener) { /** * Sets the modifiable map of JAXP transformer output properties. - * - * @param outputProperties - * The JAXP transformer output properties. + * + * @param outputProperties The JAXP transformer output properties. */ public void setOutputProperties(Map outputProperties) { this.outputProperties = outputProperties; @@ -481,9 +439,8 @@ public void setOutputProperties(Map outputProperties) { /** * Sets the JAXP transformer parameters. - * - * @param parameters - * The JAXP transformer parameters. + * + * @param parameters The JAXP transformer parameters. */ public void setParameters(Map parameters) { this.parameters = parameters; @@ -491,9 +448,8 @@ public void setParameters(Map parameters) { /** * Sets the source representation to transform. - * - * @param source - * The source representation to transform. + * + * @param source The source representation to transform. */ public void setSourceRepresentation(Representation source) { this.sourceRepresentation = source; @@ -501,9 +457,8 @@ public void setSourceRepresentation(Representation source) { /** * Sets the templates to be used and reused. - * - * @param templates - * The templates to be used and reused. + * + * @param templates The templates to be used and reused. */ public void setTemplates(Templates templates) { this.templates = templates; @@ -511,9 +466,8 @@ public void setTemplates(Templates templates) { /** * Sets the XSLT transform sheet to apply to message entities. - * - * @param transformSheet - * The XSLT transform sheet to apply to message entities. + * + * @param transformSheet The XSLT transform sheet to apply to message entities. */ public void setTransformSheet(Representation transformSheet) { this.transformSheet = transformSheet; @@ -521,9 +475,8 @@ public void setTransformSheet(Representation transformSheet) { /** * Sets the URI resolver. - * - * @param uriResolver - * The URI resolver. + * + * @param uriResolver The URI resolver. */ public void setUriResolver(URIResolver uriResolver) { this.uriResolver = uriResolver; @@ -531,35 +484,30 @@ public void setUriResolver(URIResolver uriResolver) { /** * Transforms the given JAXP source into the given result. - * - * @param source - * The JAXP source object. - * @param result - * The JAXP result object. + * + * @param source The JAXP source object. + * @param result The JAXP result object. * @throws IOException */ public void transform(Source source, Result result) throws IOException { if (getTransformer() == null) { Context.getCurrentLogger() - .warning( - "Unable to apply the transformation. No transformer found!"); + .warning("Unable to apply the transformation. No transformer found!"); } else { try { // Generates the result of the transformation getTransformer().transform(source, result); } catch (TransformerException te) { - throw new IOException("Transformer exception. " - + te.getMessage()); + throw new IOException("Transformer exception. " + te.getMessage()); } } } /** - * Writes the transformed source into the given JAXP result. The source is - * retrieved using the {@link #getSaxSource()} method. - * - * @param result - * The JAXP result object. + * Writes the transformed source into the given JAXP result. The source is retrieved using the + * {@link #getSaxSource()} method. + * + * @param result The JAXP result object. * @throws IOException */ public void write(Result result) throws IOException { @@ -567,9 +515,8 @@ public void write(Result result) throws IOException { } /** - * Writes the transformed source into the given output stream. By default, - * it leverages the {@link #write(Result)} method using a - * {@link StreamResult} object. + * Writes the transformed source into the given output stream. By default, it leverages the + * {@link #write(Result)} method using a {@link StreamResult} object. */ @Override public void write(Writer writer) throws IOException { diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java index fb66faeb92..cb35eab1d6 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; - import org.restlet.Request; import org.restlet.Response; import org.restlet.data.CharacterSet; @@ -22,50 +20,36 @@ import org.restlet.routing.Filter; /** - * Filter that can transform XML representations by applying an XSLT transform - * sheet. It uses the {@link org.restlet.representation.TransformRepresentation} - * to actually transform the XML entities.
+ * Filter that can transform XML representations by applying an XSLT transform sheet. It uses the + * {@link org.restlet.representation.TransformRepresentation} to actually transform the XML + * entities.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class Transformer extends Filter { - /** - * Mode that transforms request entities before their handling by the - * attached Restlet. - */ + /** Mode that transforms request entities before their handling by the attached Restlet. */ public static final int MODE_REQUEST = 1; - /** - * Mode that transforms response entities after their handling by the - * attached Restlet. - */ + /** Mode that transforms response entities after their handling by the attached Restlet. */ public static final int MODE_RESPONSE = 2; /** The transformation mode. */ private volatile int mode; - /** - * The character set of the result representation. The default value is - * null. - */ + /** The character set of the result representation. The default value is null. */ private volatile CharacterSet resultCharacterSet; - /** - * The encodings of the result representation. - */ + /** The encodings of the result representation. */ private volatile List resultEncodings; /** The languages of the result representation. */ private volatile List resultLanguages; - /** - * The media type of the result representation. MediaType.APPLICATION_XML by - * default. - */ + /** The media type of the result representation. MediaType.APPLICATION_XML by default. */ private volatile MediaType resultMediaType; /** The XSLT transform sheet to apply to message entities. */ @@ -73,11 +57,9 @@ public class Transformer extends Filter { /** * Constructor. - * - * @param mode - * The transformation mode. - * @param transformSheet - * The XSLT transform sheet to apply to message entities. + * + * @param mode The transformation mode. + * @param transformSheet The XSLT transform sheet to apply to message entities. */ public Transformer(int mode, Representation transformSheet) { this.mode = mode; @@ -103,11 +85,10 @@ protected int beforeHandle(Request request, Response response) { } /** - * Indicates if the filter can transform the given message entity. By - * default, it always returns true. - * - * @param representation - * The entity representation to test. + * Indicates if the filter can transform the given message entity. By default, it always returns + * true. + * + * @param representation The entity representation to test. * @return True if the transformation can be applied. */ protected boolean canTransform(Representation representation) { @@ -116,7 +97,7 @@ protected boolean canTransform(Representation representation) { /** * Returns the transformation mode. See MODE_* constants. - * + * * @return The transformation mode. */ public int getMode() { @@ -124,9 +105,8 @@ public int getMode() { } /** - * Returns the character set of the result representation. The default value - * is null. - * + * Returns the character set of the result representation. The default value is null. + * * @return The character set of the result representation. */ public CharacterSet getResultCharacterSet() { @@ -135,7 +115,7 @@ public CharacterSet getResultCharacterSet() { /** * Returns the modifiable list of encodings of the result representation. - * + * * @return The encoding of the result representation. */ public List getResultEncodings() { @@ -154,7 +134,7 @@ public List getResultEncodings() { /** * Returns the modifiable list of languages of the result representation. - * + * * @return The language of the result representation. */ public List getResultLanguages() { @@ -174,7 +154,7 @@ public List getResultLanguages() { /** * Returns the media type of the result representation. The default value is * MediaType.APPLICATION_XML. - * + * * @return The media type of the result representation. */ public MediaType getResultMediaType() { @@ -183,7 +163,7 @@ public MediaType getResultMediaType() { /** * Returns the XSLT transform sheet to apply to message entities. - * + * * @return The XSLT transform sheet to apply to message entities. */ public Representation getTransformSheet() { @@ -192,9 +172,8 @@ public Representation getTransformSheet() { /** * Sets the transformation mode. See MODE_* constants. - * - * @param mode - * The transformation mode. + * + * @param mode The transformation mode. */ public void setMode(int mode) { this.mode = mode; @@ -202,9 +181,8 @@ public void setMode(int mode) { /** * Sets the character set of the result representation. - * - * @param resultCharacterSet - * The character set of the result representation. + * + * @param resultCharacterSet The character set of the result representation. */ public void setResultCharacterSet(CharacterSet resultCharacterSet) { this.resultCharacterSet = resultCharacterSet; @@ -212,9 +190,8 @@ public void setResultCharacterSet(CharacterSet resultCharacterSet) { /** * Sets the encodings of the result representation. - * - * @param resultEncodings - * The encodings of the result representation. + * + * @param resultEncodings The encodings of the result representation. */ public void setResultEncodings(List resultEncodings) { this.resultEncodings = resultEncodings; @@ -222,9 +199,8 @@ public void setResultEncodings(List resultEncodings) { /** * Sets the languages of the result representation. - * - * @param resultLanguages - * The languages of the result representation. + * + * @param resultLanguages The languages of the result representation. */ public void setResultLanguages(List resultLanguages) { this.resultLanguages = resultLanguages; @@ -232,9 +208,8 @@ public void setResultLanguages(List resultLanguages) { /** * Sets the media type of the result representation. - * - * @param resultMediaType - * The media type of the result representation. + * + * @param resultMediaType The media type of the result representation. */ public void setResultMediaType(MediaType resultMediaType) { this.resultMediaType = resultMediaType; @@ -242,25 +217,22 @@ public void setResultMediaType(MediaType resultMediaType) { /** * Sets the XSLT transform sheet to apply to message entities. - * - * @param transformSheet - * The XSLT transform sheet to apply to message entities. + * + * @param transformSheet The XSLT transform sheet to apply to message entities. */ public void setTransformSheet(Representation transformSheet) { this.transformSheet = transformSheet; } /** - * Transforms a source XML representation by applying an XSLT transform - * sheet to it. - * - * @param source - * The source XML representation. + * Transforms a source XML representation by applying an XSLT transform sheet to it. + * + * @param source The source XML representation. * @return The generated result representation. */ public Representation transform(Representation source) { - final Representation result = new TransformRepresentation(getContext(), - source, getTransformSheet()); + final Representation result = + new TransformRepresentation(getContext(), source, getTransformSheet()); if (this.resultLanguages != null) { result.getLanguages().addAll(getResultLanguages()); @@ -274,5 +246,4 @@ public Representation transform(Representation source) { result.setMediaType(getResultMediaType()); return result; } - } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java index e0cfda1b94..98b39c6149 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.io.IOException; import java.util.List; - import org.restlet.data.MediaType; import org.restlet.data.Preference; import org.restlet.engine.converter.ConverterHelper; @@ -23,19 +21,18 @@ /** * Converter between the XML APIs and XML Representation classes. - * + * * @author Jerome Louvel */ public class XmlConverter extends ConverterHelper { - private static final VariantInfo VARIANT_APPLICATION_ALL_XML = new VariantInfo( - MediaType.APPLICATION_ALL_XML); + private static final VariantInfo VARIANT_APPLICATION_ALL_XML = + new VariantInfo(MediaType.APPLICATION_ALL_XML); - private static final VariantInfo VARIANT_APPLICATION_XML = new VariantInfo( - MediaType.APPLICATION_XML); + private static final VariantInfo VARIANT_APPLICATION_XML = + new VariantInfo(MediaType.APPLICATION_XML); - private static final VariantInfo VARIANT_TEXT_XML = new VariantInfo( - MediaType.TEXT_XML); + private static final VariantInfo VARIANT_TEXT_XML = new VariantInfo(MediaType.TEXT_XML); @Override public List> getObjectClasses(Variant source) { @@ -74,11 +71,9 @@ public float score(Object source, Variant target, Resource resource) { if (source instanceof Document) { if (target == null) { result = 0.5F; - } else if (MediaType.APPLICATION_ALL_XML.isCompatible(target - .getMediaType())) { + } else if (MediaType.APPLICATION_ALL_XML.isCompatible(target.getMediaType())) { result = 0.8F; - } else if (MediaType.APPLICATION_XML.isCompatible(target - .getMediaType())) { + } else if (MediaType.APPLICATION_XML.isCompatible(target.getMediaType())) { result = 0.9F; } else if (MediaType.TEXT_XML.isCompatible(target.getMediaType())) { result = 0.9F; @@ -91,19 +86,16 @@ public float score(Object source, Variant target, Resource resource) { } @Override - public float score(Representation source, Class target, - Resource resource) { + public float score(Representation source, Class target, Resource resource) { float result = -1.0F; if ((target != null) && (Document.class.isAssignableFrom(target) - || DomRepresentation.class.isAssignableFrom(target) || SaxRepresentation.class - .isAssignableFrom(target))) { - if (MediaType.APPLICATION_ALL_XML.isCompatible(source - .getMediaType())) { + || DomRepresentation.class.isAssignableFrom(target) + || SaxRepresentation.class.isAssignableFrom(target))) { + if (MediaType.APPLICATION_ALL_XML.isCompatible(source.getMediaType())) { result = 0.8F; - } else if (MediaType.APPLICATION_XML.isCompatible(source - .getMediaType())) { + } else if (MediaType.APPLICATION_XML.isCompatible(source.getMediaType())) { result = 0.9F; } else if (MediaType.TEXT_XML.isCompatible(source.getMediaType())) { result = 0.9F; @@ -117,8 +109,8 @@ public float score(Representation source, Class target, @SuppressWarnings("unchecked") @Override - public T toObject(Representation source, Class target, - Resource resource) throws IOException { + public T toObject(Representation source, Class target, Resource resource) + throws IOException { Object result = null; if (target != null) { @@ -147,13 +139,12 @@ public T toObject(Representation source, Class target, } @Override - public Representation toRepresentation(Object source, Variant target, - Resource resource) throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { Representation result = null; if (source instanceof Document) { - result = new DomRepresentation(target.getMediaType(), - (Document) source); + result = new DomRepresentation(target.getMediaType(), (Document) source); } else if (source instanceof Representation) { result = (Representation) source; } @@ -162,8 +153,7 @@ public Representation toRepresentation(Object source, Variant target, } @Override - public void updatePreferences(List> preferences, - Class entity) { + public void updatePreferences(List> preferences, Class entity) { if (Document.class.isAssignableFrom(entity) || DomRepresentation.class.isAssignableFrom(entity) || SaxRepresentation.class.isAssignableFrom(entity)) { diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java index e4489f0263..d96d86bdcf 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.io.IOException; @@ -17,14 +16,12 @@ import java.util.List; import java.util.Map; import java.util.logging.Level; - import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; - import org.restlet.Context; import org.restlet.data.MediaType; import org.restlet.representation.Representation; @@ -37,91 +34,85 @@ import org.xml.sax.SAXException; /** - * Representation based on an XML document. It knows how to evaluate XPath - * expressions and how to manage a namespace context. This class also offers - * convenient methods to validate the document against a specified XML scheme.
+ * Representation based on an XML document. It knows how to evaluate XPath expressions and how to + * manage a namespace context. This class also offers convenient methods to validate the document + * against a specified XML scheme.
*
- * SECURITY WARNING: Using XML parsers configured to not prevent nor limit - * document type definition (DTD) entity resolution can expose the parser to an - * XML Entity Expansion injection attack. - * + * SECURITY WARNING: Using XML parsers configured to not prevent nor limit document type definition + * (DTD) entity resolution can expose the parser to an XML Entity Expansion injection attack. + * * @see XML - * Entity Expansion injection attack + * href="https://github.com/restlet/restlet-framework-java/wiki/XEE-security-enhancements">XML + * Entity Expansion injection attack * @author Jerome Louvel */ -public abstract class XmlRepresentation extends WriterRepresentation implements javax.xml.namespace.NamespaceContext { +public abstract class XmlRepresentation extends WriterRepresentation + implements javax.xml.namespace.NamespaceContext { /** - * True for expanding entity references when parsing XML representations; - * default value provided by system property - * "org.restlet.ext.xml.expandingEntityRefs", false by default. + * True for expanding entity references when parsing XML representations; default value provided + * by system property "org.restlet.ext.xml.expandingEntityRefs", false by default. */ - public final static boolean XML_EXPANDING_ENTITY_REFS = Boolean - .getBoolean("org.restlet.ext.xml.expandingEntityRefs"); + public static final boolean XML_EXPANDING_ENTITY_REFS = + Boolean.getBoolean("org.restlet.ext.xml.expandingEntityRefs"); /** - * True for validating DTD documents when parsing XML representations; - * default value provided by system property - * "org.restlet.ext.xml.validatingDtd", false by default. + * True for validating DTD documents when parsing XML representations; default value provided by + * system property "org.restlet.ext.xml.validatingDtd", false by default. */ - public final static boolean XML_VALIDATING_DTD = Boolean - .getBoolean("org.restlet.ext.xml.validatingDtd"); + public static final boolean XML_VALIDATING_DTD = + Boolean.getBoolean("org.restlet.ext.xml.validatingDtd"); /** - * Appends the text content of a given node and its descendants to the given - * buffer. - * - * @param node - * The node. - * @param sb - * The buffer. + * Appends the text content of a given node and its descendants to the given buffer. + * + * @param node The node. + * @param sb The buffer. */ private static void appendTextContent(Node node, StringBuilder sb) { switch (node.getNodeType()) { - case Node.TEXT_NODE: - case Node.CDATA_SECTION_NODE: - case Node.COMMENT_NODE: - case Node.PROCESSING_INSTRUCTION_NODE: - sb.append(node.getNodeValue()); - break; - case Node.ENTITY_REFERENCE_NODE: - if (node.getNodeName().startsWith("#")) { - int ch = Integer.parseInt(node.getNodeName().substring(1)); - sb.append((char) ch); - } - break; - case Node.ELEMENT_NODE: - case Node.ATTRIBUTE_NODE: - case Node.ENTITY_NODE: - case Node.DOCUMENT_FRAGMENT_NODE: - for (int i = 0; i < node.getChildNodes().getLength(); i++) { - appendTextContent(node.getChildNodes().item(i), sb); - } - break; - default: - break; + case Node.TEXT_NODE: + case Node.CDATA_SECTION_NODE: + case Node.COMMENT_NODE: + case Node.PROCESSING_INSTRUCTION_NODE: + sb.append(node.getNodeValue()); + break; + case Node.ENTITY_REFERENCE_NODE: + if (node.getNodeName().startsWith("#")) { + int ch = Integer.parseInt(node.getNodeName().substring(1)); + sb.append((char) ch); + } + break; + case Node.ELEMENT_NODE: + case Node.ATTRIBUTE_NODE: + case Node.ENTITY_NODE: + case Node.DOCUMENT_FRAGMENT_NODE: + for (int i = 0; i < node.getChildNodes().getLength(); i++) { + appendTextContent(node.getChildNodes().item(i), sb); + } + break; + default: + break; } } /** * Returns a SAX source. - * - * @param xmlRepresentation - * The XML representation to wrap. + * + * @param xmlRepresentation The XML representation to wrap. * @return A SAX source. * @throws IOException */ - public static javax.xml.transform.sax.SAXSource getSaxSource( - Representation xmlRepresentation) throws IOException { + public static javax.xml.transform.sax.SAXSource getSaxSource(Representation xmlRepresentation) + throws IOException { javax.xml.transform.sax.SAXSource result = null; if (xmlRepresentation != null) { - result = new javax.xml.transform.sax.SAXSource(new InputSource( - xmlRepresentation.getStream())); + result = + new javax.xml.transform.sax.SAXSource( + new InputSource(xmlRepresentation.getStream())); if (xmlRepresentation.getLocationRef() != null) { - result.setSystemId(xmlRepresentation.getLocationRef() - .getTargetRef().toString()); + result.setSystemId(xmlRepresentation.getLocationRef().getTargetRef().toString()); } } @@ -130,20 +121,21 @@ public static javax.xml.transform.sax.SAXSource getSaxSource( /** * Returns the wrapped schema. - * + * * @return The wrapped schema. * @throws IOException */ - private static javax.xml.validation.Schema getSchema( - Representation schemaRepresentation) throws Exception { + private static javax.xml.validation.Schema getSchema(Representation schemaRepresentation) + throws Exception { javax.xml.validation.Schema result = null; if (schemaRepresentation != null) { - final javax.xml.transform.stream.StreamSource streamSource = new javax.xml.transform.stream.StreamSource( - schemaRepresentation.getStream()); - result = javax.xml.validation.SchemaFactory.newInstance( - getSchemaLanguageUri(schemaRepresentation)).newSchema( - streamSource); + final javax.xml.transform.stream.StreamSource streamSource = + new javax.xml.transform.stream.StreamSource(schemaRepresentation.getStream()); + result = + javax.xml.validation.SchemaFactory.newInstance( + getSchemaLanguageUri(schemaRepresentation)) + .newSchema(streamSource); } return result; @@ -151,22 +143,20 @@ private static javax.xml.validation.Schema getSchema( /** * Returns the schema URI for the current schema media type. - * + * * @return The schema URI. */ - private static String getSchemaLanguageUri( - Representation schemaRepresentation) { + private static String getSchemaLanguageUri(Representation schemaRepresentation) { String result = null; if (schemaRepresentation != null) { - if (MediaType.APPLICATION_W3C_SCHEMA.equals(schemaRepresentation - .getMediaType())) { + if (MediaType.APPLICATION_W3C_SCHEMA.equals(schemaRepresentation.getMediaType())) { result = XMLConstants.W3C_XML_SCHEMA_NS_URI; - } else if (MediaType.APPLICATION_RELAXNG_COMPACT - .equals(schemaRepresentation.getMediaType())) { + } else if (MediaType.APPLICATION_RELAXNG_COMPACT.equals( + schemaRepresentation.getMediaType())) { result = XMLConstants.RELAXNG_NS_URI; - } else if (MediaType.APPLICATION_RELAXNG_XML - .equals(schemaRepresentation.getMediaType())) { + } else if (MediaType.APPLICATION_RELAXNG_XML.equals( + schemaRepresentation.getMediaType())) { result = XMLConstants.RELAXNG_NS_URI; } } @@ -176,9 +166,8 @@ private static String getSchemaLanguageUri( /** * Returns the text content of a given node and its descendants. - * - * @param node - * The node. + * + * @param node The node. * @return The text content of a given node. */ public static String getTextContent(Node node) { @@ -188,43 +177,41 @@ public static String getTextContent(Node node) { } /** - * Specifies that the parser will convert CDATA nodes to text nodes and - * append it to the adjacent (if any) text node. By default, the value of - * this is set to false. + * Specifies that the parser will convert CDATA nodes to text nodes and append it to the + * adjacent (if any) text node. By default, the value of this is set to false. */ private volatile boolean coalescing; /** - * A SAX {@link EntityResolver} to use when resolving external entity - * references while parsing this type of XML representations. - * + * A SAX {@link EntityResolver} to use when resolving external entity references while parsing + * this type of XML representations. + * * @see DocumentBuilder#setEntityResolver(EntityResolver) */ private volatile EntityResolver entityResolver; /** - * A SAX {@link ErrorHandler} to use for signaling SAX exceptions while - * parsing this type of XML representations. - * + * A SAX {@link ErrorHandler} to use for signaling SAX exceptions while parsing this type of XML + * representations. + * * @see DocumentBuilder#setErrorHandler(ErrorHandler) */ private volatile ErrorHandler errorHandler; /** - * Specifies that the parser will expand entity reference nodes. By default, - * the value of this is set to true. + * Specifies that the parser will expand entity reference nodes. By default, the value of this + * is set to true. */ private volatile boolean expandingEntityRefs; /** - * Indicates if the parser will ignore comments. By default, the value of - * this is set to false. + * Indicates if the parser will ignore comments. By default, the value of this is set to false. */ private volatile boolean ignoringComments; /** - * Indicates if the parser will ignore extra white spaces in element - * content. By default, the value of this is set to false. + * Indicates if the parser will ignore extra white spaces in element content. By default, the + * value of this is set to false. */ private volatile boolean ignoringExtraWhitespaces; @@ -235,36 +222,33 @@ public static String getTextContent(Node node) { private volatile Map namespaces; /** - * A (compiled) {@link javax.xml.validation.Schema} to use when validating - * this type of XML representations. - * + * A (compiled) {@link javax.xml.validation.Schema} to use when validating this type of XML + * representations. + * * @see DocumentBuilderFactory#setSchema(javax.xml.validation.Schema) */ private volatile javax.xml.validation.Schema schema; /** - * Indicates the desire for validating this type of XML representations - * against a DTD. Note that for XML schema or Relax NG validation, use the - * "schema" property instead. - * + * Indicates the desire for validating this type of XML representations against a DTD. Note that + * for XML schema or Relax NG validation, use the "schema" property instead. + * * @see DocumentBuilderFactory#setValidating(boolean) */ private volatile boolean validatingDtd; /** - * Indicates the desire for processing XInclude if found in this - * type of XML representations. By default, the value of this is set to - * false. - * + * Indicates the desire for processing XInclude if found in this type of XML + * representations. By default, the value of this is set to false. + * * @see DocumentBuilderFactory#setXIncludeAware(boolean) */ private volatile boolean xIncludeAware; /** * Constructor. - * - * @param mediaType - * The representation's mediaType. + * + * @param mediaType The representation's mediaType. */ public XmlRepresentation(MediaType mediaType) { this(mediaType, UNKNOWN_SIZE); @@ -272,11 +256,9 @@ public XmlRepresentation(MediaType mediaType) { /** * Constructor. - * - * @param mediaType - * The representation's mediaType. - * @param expectedSize - * The expected input stream size. + * + * @param mediaType The representation's mediaType. + * @param expectedSize The expected input stream size. */ public XmlRepresentation(MediaType mediaType, long expectedSize) { super(mediaType, expectedSize); @@ -294,19 +276,17 @@ public XmlRepresentation(MediaType mediaType, long expectedSize) { } /** - * Evaluates an XPath expression as a boolean. If the evaluation fails, null - * will be returned. - * + * Evaluates an XPath expression as a boolean. If the evaluation fails, null will be returned. + * * @return The evaluation result. */ public Boolean getBoolean(String expression) { - return (Boolean) internalEval(expression, - javax.xml.xpath.XPathConstants.BOOLEAN); + return (Boolean) internalEval(expression, javax.xml.xpath.XPathConstants.BOOLEAN); } /** * Returns the XML representation as a DOM document. - * + * * @return The DOM document. */ protected Document getDocument() throws Exception { @@ -315,7 +295,7 @@ protected Document getDocument() throws Exception { /** * Returns a document builder properly configured. - * + * * @return A document builder properly configured. */ protected DocumentBuilder getDocumentBuilder() throws IOException { @@ -327,17 +307,21 @@ protected DocumentBuilder getDocumentBuilder() throws IOException { dbf.setValidating(isValidatingDtd()); dbf.setCoalescing(isCoalescing()); dbf.setExpandEntityReferences(false); - dbf.setFeature("http://xml.org/sax/features/external-parameter-entities",isExpandingEntityRefs()); - dbf.setFeature("http://xml.org/sax/features/external-general-entities",isExpandingEntityRefs()); - + dbf.setFeature( + "http://xml.org/sax/features/external-parameter-entities", + isExpandingEntityRefs()); + dbf.setFeature( + "http://xml.org/sax/features/external-general-entities", + isExpandingEntityRefs()); + dbf.setIgnoringComments(isIgnoringComments()); dbf.setIgnoringElementContentWhitespace(isIgnoringExtraWhitespaces()); try { dbf.setXIncludeAware(isXIncludeAware()); } catch (UnsupportedOperationException uoe) { - Context.getCurrentLogger().log(Level.FINE, - "The JAXP parser doesn't support XInclude.", uoe); + Context.getCurrentLogger() + .log(Level.FINE, "The JAXP parser doesn't support XInclude.", uoe); } javax.xml.validation.Schema xsd = getSchema(); @@ -345,13 +329,11 @@ protected DocumentBuilder getDocumentBuilder() throws IOException { dbf.setSchema(xsd); } - result = dbf.newDocumentBuilder(); result.setEntityResolver(getEntityResolver()); result.setErrorHandler(getErrorHandler()); } catch (ParserConfigurationException pce) { - throw new IOException("Couldn't create the empty document: " - + pce.getMessage()); + throw new IOException("Couldn't create the empty document: " + pce.getMessage()); } return result; @@ -359,7 +341,7 @@ protected DocumentBuilder getDocumentBuilder() throws IOException { /** * Returns a DOM source. - * + * * @return A DOM source. * @throws IOException */ @@ -370,8 +352,7 @@ public javax.xml.transform.dom.DOMSource getDomSource() throws IOException { try { document = getDocumentBuilder().parse(getInputSource()); } catch (SAXException se) { - throw new IOException("Couldn't read the XML representation. " - + se.getMessage()); + throw new IOException("Couldn't read the XML representation. " + se.getMessage()); } if (document != null) { @@ -387,7 +368,7 @@ public javax.xml.transform.dom.DOMSource getDomSource() throws IOException { /** * Return the possibly null current SAX {@link EntityResolver}. - * + * * @return The possibly null current SAX {@link EntityResolver}. */ public EntityResolver getEntityResolver() { @@ -396,7 +377,7 @@ public EntityResolver getEntityResolver() { /** * Return the possibly null current SAX {@link ErrorHandler}. - * + * * @return The possibly null current SAX {@link ErrorHandler}. */ public ErrorHandler getErrorHandler() { @@ -405,15 +386,14 @@ public ErrorHandler getErrorHandler() { /** * Returns the XML representation as a SAX input source. - * + * * @return The SAX input source. */ public abstract InputSource getInputSource() throws IOException; /** - * Returns the map of namespaces. Namespace prefixes are keys and URI - * references are values. - * + * Returns the map of namespaces. Namespace prefixes are keys and URI references are values. + * * @return The map of namespaces. */ public Map getNamespaces() { @@ -424,58 +404,49 @@ public Map getNamespaces() { return this.namespaces; } - /** - * {@inheritDoc - * javax.xml.namespace.NamespaceContext#getNamespaceURI(java.lang.String} - */ + /** {@inheritDoc javax.xml.namespace.NamespaceContext#getNamespaceURI(java.lang.String} */ public String getNamespaceURI(String prefix) { return (this.namespaces == null) ? null : this.namespaces.get(prefix); } /** - * Evaluates an XPath expression as a DOM Node. If the evaluation fails, - * null will be returned. - * + * Evaluates an XPath expression as a DOM Node. If the evaluation fails, null will be returned. + * * @return The evaluation result. */ public Node getNode(String expression) { - return (Node) internalEval(expression, - javax.xml.xpath.XPathConstants.NODE); + return (Node) internalEval(expression, javax.xml.xpath.XPathConstants.NODE); } /** - * Evaluates an XPath expression as a DOM NodeList. If the evaluation fails, - * null will be returned. - * + * Evaluates an XPath expression as a DOM NodeList. If the evaluation fails, null will be + * returned. + * * @return The evaluation result. */ public NodeList getNodes(String expression) { - final org.w3c.dom.NodeList nodes = (org.w3c.dom.NodeList) internalEval( - expression, javax.xml.xpath.XPathConstants.NODESET); + final org.w3c.dom.NodeList nodes = + (org.w3c.dom.NodeList) + internalEval(expression, javax.xml.xpath.XPathConstants.NODESET); return (nodes == null) ? null : new NodeList(nodes); } /** - * Evaluates an XPath expression as a number. If the evaluation fails, null - * will be returned. - * + * Evaluates an XPath expression as a number. If the evaluation fails, null will be returned. + * * @return The evaluation result. */ public Double getNumber(String expression) { - return (Double) internalEval(expression, - javax.xml.xpath.XPathConstants.NUMBER); + return (Double) internalEval(expression, javax.xml.xpath.XPathConstants.NUMBER); } - /** - * {@inheritDoc - * javax.xml.namespace.NamespaceContext#getPrefix(java.lang.String} - */ + /** {@inheritDoc javax.xml.namespace.NamespaceContext#getPrefix(java.lang.String} */ public String getPrefix(String namespaceURI) { String result = null; boolean found = false; - for (Iterator iterator = getNamespaces().keySet().iterator(); iterator - .hasNext() && !found;) { + for (Iterator iterator = getNamespaces().keySet().iterator(); + iterator.hasNext() && !found; ) { String key = iterator.next(); if (getNamespaces().get(key).equals(namespaceURI)) { found = true; @@ -486,15 +457,12 @@ public String getPrefix(String namespaceURI) { return result; } - /** - * {@inheritDoc - * javax.xml.namespace.NamespaceContext#getPrefixes(java.lang.String} - */ + /** {@inheritDoc javax.xml.namespace.NamespaceContext#getPrefixes(java.lang.String} */ public Iterator getPrefixes(String namespaceURI) { final List result = new ArrayList(); - for (Iterator iterator = getNamespaces().keySet().iterator(); iterator - .hasNext();) { + for (Iterator iterator = getNamespaces().keySet().iterator(); + iterator.hasNext(); ) { String key = iterator.next(); if (getNamespaces().get(key).equals(namespaceURI)) { result.add(key); @@ -506,7 +474,7 @@ public Iterator getPrefixes(String namespaceURI) { /** * Returns a SAX source. - * + * * @return A SAX source. * @throws IOException */ @@ -515,11 +483,10 @@ public javax.xml.transform.sax.SAXSource getSaxSource() throws IOException { } /** - * Return the possibly null {@link javax.xml.validation.Schema} to use for - * this type of XML representations. - * - * @return the {@link javax.xml.validation.Schema} object of this type of - * XML representations. + * Return the possibly null {@link javax.xml.validation.Schema} to use for this type of XML + * representations. + * + * @return the {@link javax.xml.validation.Schema} object of this type of XML representations. */ public javax.xml.validation.Schema getSchema() { return schema; @@ -527,14 +494,13 @@ public javax.xml.validation.Schema getSchema() { /** * Returns a stream of XML markup. - * + * * @return A stream of XML markup. * @throws IOException */ - public javax.xml.transform.stream.StreamSource getStreamSource() - throws IOException { - final javax.xml.transform.stream.StreamSource result = new javax.xml.transform.stream.StreamSource( - getStream()); + public javax.xml.transform.stream.StreamSource getStreamSource() throws IOException { + final javax.xml.transform.stream.StreamSource result = + new javax.xml.transform.stream.StreamSource(getStream()); if (getLocationRef() != null) { result.setSystemId(getLocationRef().getTargetRef().toString()); @@ -545,24 +511,20 @@ public javax.xml.transform.stream.StreamSource getStreamSource() /** * Evaluates an XPath expression as a string. - * + * * @return The evaluation result. */ public String getText(String expression) { - return (String) internalEval(expression, - javax.xml.xpath.XPathConstants.STRING); + return (String) internalEval(expression, javax.xml.xpath.XPathConstants.STRING); } /** - * Evaluates an XPath expression and returns the result as in the given - * return type. - * - * @param returnType - * The qualified name of the return type. + * Evaluates an XPath expression and returns the result as in the given return type. + * + * @param returnType The qualified name of the return type. * @return The evaluation result. */ - private Object internalEval(String expression, - javax.xml.namespace.QName returnType) { + private Object internalEval(String expression, javax.xml.namespace.QName returnType) { try { Object result = null; @@ -587,10 +549,10 @@ private Object internalEval(String expression, } /** - * Indicates if the parser should be coalescing text. If true the parser - * will convert CDATA nodes to text nodes and append it to the adjacent (if - * any) text node. By default, the value of this is set to false. - * + * Indicates if the parser should be coalescing text. If true the parser will convert CDATA + * nodes to text nodes and append it to the adjacent (if any) text node. By default, the value + * of this is set to false. + * * @return True if parser should be coalescing text. */ public boolean isCoalescing() { @@ -598,9 +560,9 @@ public boolean isCoalescing() { } /** - * Indicates if the parser will expand entity reference nodes. By default, - * the value of this is set to true. - * + * Indicates if the parser will expand entity reference nodes. By default, the value of this is + * set to true. + * * @return True if the parser will expand entity reference nodes. */ public boolean isExpandingEntityRefs() { @@ -608,9 +570,8 @@ public boolean isExpandingEntityRefs() { } /** - * Indicates if the parser will ignore comments. By default, the value of - * this is set to false. - * + * Indicates if the parser will ignore comments. By default, the value of this is set to false. + * * @return True if the parser will ignore comments. */ public boolean isIgnoringComments() { @@ -618,11 +579,10 @@ public boolean isIgnoringComments() { } /** - * Indicates if the parser will ignore extra white spaces in element - * content. Note that the {@link #isValidatingDtd()} must be true when this - * property is 'true' as validation is needed for it to work. By default, the - * value of this is set to false. - * + * Indicates if the parser will ignore extra white spaces in element content. Note that the + * {@link #isValidatingDtd()} must be true when this property is 'true' as validation is needed + * for it to work. By default, the value of this is set to false. + * * @return True if the parser will ignore extra white spaces. */ public boolean isIgnoringExtraWhitespaces() { @@ -631,7 +591,7 @@ public boolean isIgnoringExtraWhitespaces() { /** * Indicates if processing is namespace aware. - * + * * @return True if processing is namespace aware. */ public boolean isNamespaceAware() { @@ -639,9 +599,9 @@ public boolean isNamespaceAware() { } /** - * Indicates the desire for validating this type of XML representations - * against an XML schema if one is referenced within the contents. - * + * Indicates the desire for validating this type of XML representations against an XML schema if + * one is referenced within the contents. + * * @return True if the schema-based validation is enabled. */ public boolean isValidatingDtd() { @@ -649,19 +609,16 @@ public boolean isValidatingDtd() { } /** - * Indicates the desire for processing XInclude if found in this - * type of XML representations. By default, the value of this is set to - * false. - * + * Indicates the desire for processing XInclude if found in this type of XML + * representations. By default, the value of this is set to false. + * * @return The current value of the xIncludeAware flag. */ public boolean isXIncludeAware() { return xIncludeAware; } - /** - * Releases the namespaces map. - */ + /** Releases the namespaces map. */ @Override public void release() { if (this.namespaces != null) { @@ -672,23 +629,21 @@ public void release() { } /** - * Indicates if the parser should be coalescing text. If true the parser - * will convert CDATA nodes to text nodes and append it to the adjacent (if - * any) text node. By default, the value of this is set to false. - * - * @param coalescing - * True if parser should be coalescing text. + * Indicates if the parser should be coalescing text. If true the parser will convert CDATA + * nodes to text nodes and append it to the adjacent (if any) text node. By default, the value + * of this is set to false. + * + * @param coalescing True if parser should be coalescing text. */ public void setCoalescing(boolean coalescing) { this.coalescing = coalescing; } /** - * Set the {@link EntityResolver} to use when resolving external entity - * references encountered in this type of XML representations. - * - * @param entityResolver - * the {@link EntityResolver} to set. + * Set the {@link EntityResolver} to use when resolving external entity references encountered + * in this type of XML representations. + * + * @param entityResolver the {@link EntityResolver} to set. */ public void setEntityResolver(EntityResolver entityResolver) { this.entityResolver = entityResolver; @@ -696,45 +651,39 @@ public void setEntityResolver(EntityResolver entityResolver) { /** * Set the {@link ErrorHandler} to use when signaling SAX event exceptions. - * - * @param errorHandler - * the {@link ErrorHandler} to set. + * + * @param errorHandler the {@link ErrorHandler} to set. */ public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } /** - * Indicates if the parser will expand entity reference nodes. By default, - * the value of this is set to true. - * - * @param expandEntityRefs - * True if the parser will expand entity reference nodes. + * Indicates if the parser will expand entity reference nodes. By default, the value of this is + * set to true. + * + * @param expandEntityRefs True if the parser will expand entity reference nodes. */ public void setExpandingEntityRefs(boolean expandEntityRefs) { this.expandingEntityRefs = expandEntityRefs; } /** - * Indicates if the parser will ignore comments. By default, the value of - * this is set to false. - * - * @param ignoringComments - * True if the parser will ignore comments. + * Indicates if the parser will ignore comments. By default, the value of this is set to false. + * + * @param ignoringComments True if the parser will ignore comments. */ public void setIgnoringComments(boolean ignoringComments) { this.ignoringComments = ignoringComments; } /** - * Indicates if the parser will ignore extra white spaces in element - * content. Note that the {@link #setValidatingDtd(boolean)} will be invoked - * with 'true' if setting this property to 'true' as validation is needed - * for it to work. - * - * @param ignoringExtraWhitespaces - * True if the parser will ignore extra white spaces in element - * content. + * Indicates if the parser will ignore extra white spaces in element content. Note that the + * {@link #setValidatingDtd(boolean)} will be invoked with 'true' if setting this property to + * 'true' as validation is needed for it to work. + * + * @param ignoringExtraWhitespaces True if the parser will ignore extra white spaces in element + * content. */ public void setIgnoringExtraWhitespaces(boolean ignoringExtraWhitespaces) { if (this.ignoringExtraWhitespaces != ignoringExtraWhitespaces) { @@ -748,9 +697,8 @@ public void setIgnoringExtraWhitespaces(boolean ignoringExtraWhitespaces) { /** * Indicates if processing is namespace aware. - * - * @param namespaceAware - * Indicates if processing is namespace aware. + * + * @param namespaceAware Indicates if processing is namespace aware. */ public void setNamespaceAware(boolean namespaceAware) { this.namespaceAware = namespaceAware; @@ -758,60 +706,53 @@ public void setNamespaceAware(boolean namespaceAware) { /** * Sets the map of namespaces. - * - * @param namespaces - * The map of namespaces. + * + * @param namespaces The map of namespaces. */ public void setNamespaces(Map namespaces) { this.namespaces = namespaces; } /** - * Set a (compiled) {@link javax.xml.validation.Schema} to use when parsing - * and validating this type of XML representations. - * - * @param schema - * The (compiled) {@link javax.xml.validation.Schema} object to - * set. + * Set a (compiled) {@link javax.xml.validation.Schema} to use when parsing and validating this + * type of XML representations. + * + * @param schema The (compiled) {@link javax.xml.validation.Schema} object to set. */ public void setSchema(javax.xml.validation.Schema schema) { this.schema = schema; } /** - * Set a schema representation to be compiled and used when parsing and - * validating this type of XML representations. - * - * @param schemaRepresentation - * The schema representation to set. + * Set a schema representation to be compiled and used when parsing and validating this type of + * XML representations. + * + * @param schemaRepresentation The schema representation to set. */ public void setSchema(Representation schemaRepresentation) { try { this.schema = getSchema(schemaRepresentation); } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, - "Unable to compile the schema representation", e); + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to compile the schema representation", e); } } /** - * Indicates the desire for validating this type of XML representations - * against an XML schema if one is referenced within the contents. - * - * @param validating - * The new validation flag to set. + * Indicates the desire for validating this type of XML representations against an XML schema if + * one is referenced within the contents. + * + * @param validating The new validation flag to set. */ public void setValidatingDtd(boolean validating) { this.validatingDtd = validating; } /** - * Indicates the desire for processing XInclude if found in this - * type of XML representations. By default, the value of this is set to - * false. - * - * @param includeAware - * The new value of the xIncludeAware flag. + * Indicates the desire for processing XInclude if found in this type of XML + * representations. By default, the value of this is set to false. + * + * @param includeAware The new value of the xIncludeAware flag. */ public void setXIncludeAware(boolean includeAware) { xIncludeAware = includeAware; @@ -819,9 +760,8 @@ public void setXIncludeAware(boolean includeAware) { /** * Validates the XML representation against a given schema. - * - * @param schema - * The XML schema to use. + * + * @param schema The XML schema to use. */ public void validate(javax.xml.validation.Schema schema) throws Exception { validate(schema, null); @@ -829,22 +769,19 @@ public void validate(javax.xml.validation.Schema schema) throws Exception { /** * Validates the XML representation against a given schema. - * - * @param schema - * The XML schema to use. - * @param result - * The Result object that receives (possibly augmented) XML. - */ - public void validate(javax.xml.validation.Schema schema, - javax.xml.transform.Result result) throws Exception { + * + * @param schema The XML schema to use. + * @param result The Result object that receives (possibly augmented) XML. + */ + public void validate(javax.xml.validation.Schema schema, javax.xml.transform.Result result) + throws Exception { schema.newValidator().validate(getSaxSource(), result); } /** * Validates the XML representation against a given schema. - * - * @param schemaRepresentation - * The XML schema representation to use. + * + * @param schemaRepresentation The XML schema representation to use. */ public void validate(Representation schemaRepresentation) throws Exception { validate(schemaRepresentation, null); @@ -852,15 +789,12 @@ public void validate(Representation schemaRepresentation) throws Exception { /** * Validates the XML representation against a given schema. - * - * @param schemaRepresentation - * The XML schema representation to use. - * @param result - * The Result object that receives (possibly augmented) XML. - */ - public void validate(Representation schemaRepresentation, - javax.xml.transform.Result result) throws Exception { + * + * @param schemaRepresentation The XML schema representation to use. + * @param result The Result object that receives (possibly augmented) XML. + */ + public void validate(Representation schemaRepresentation, javax.xml.transform.Result result) + throws Exception { validate(getSchema(schemaRepresentation), result); } - } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java index de5a767128..c9d1311c97 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; import java.io.IOException; @@ -21,7 +20,6 @@ import java.util.Map; import java.util.Stack; import java.util.concurrent.ConcurrentHashMap; - import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -30,207 +28,161 @@ import org.xml.sax.helpers.XMLFilterImpl; /** - * XML writer doing the opposite work of a SAX-based XML reader. The - * implementation is based on the work of David Megginson, the creator of SAX - * who placed the original code in the public domain. - * - *

- * This class can be used by itself or as part of a SAX event stream: it takes - * as input a series of SAX2 ContentHandler events and uses the information in - * those events to write an XML document. Since this class is a filter, it can - * also pass the events on down a filter chain for further processing (you can - * use the XmlWriter to take a snapshot of the current state at any point in a - * filter chain), and it can be used directly as a ContentHandler for a SAX2 - * XMLReader. - *

- * - *

- * The client creates a document by invoking the methods for standard SAX2 - * events, always beginning with the {@link #startDocument startDocument} method - * and ending with the {@link #endDocument endDocument} method. There are - * convenience methods provided so that clients to not have to create empty - * attribute lists or provide empty strings as parameters; for example, the + * XML writer doing the opposite work of a SAX-based XML reader. The implementation is based on the + * work of David Megginson, the creator of SAX who placed the original code in the public domain. + * + *

This class can be used by itself or as part of a SAX event stream: it takes as input a series + * of SAX2 ContentHandler events and uses the information in those events to write an XML document. + * Since this class is a filter, it can also pass the events on down a filter chain for further + * processing (you can use the XmlWriter to take a snapshot of the current state at any point in a + * filter chain), and it can be used directly as a ContentHandler for a SAX2 XMLReader. + * + *

The client creates a document by invoking the methods for standard SAX2 events, always + * beginning with the {@link #startDocument startDocument} method and ending with the {@link + * #endDocument endDocument} method. There are convenience methods provided so that clients to not + * have to create empty attribute lists or provide empty strings as parameters; for example, the * method invocation - *

- * + * *
  * w.startElement("foo");
  * 
- * - *

- * is equivalent to the regular SAX2 ContentHandler method - *

- * + * + *

is equivalent to the regular SAX2 ContentHandler method + * *

  * w.startElement("", "foo", "", new AttributesImpl());
  * 
- * - *

- * Except that it is more efficient because it does not allocate a new empty - * attribute list each time. The following code will send a simple XML document - * to standard output: - *

- * + * + *

Except that it is more efficient because it does not allocate a new empty attribute list each + * time. The following code will send a simple XML document to standard output: + * *

  * XmlWriter w = new XmlWriter();
- * 
+ *
  * w.startDocument();
  * w.startElement("greeting");
  * w.characters("Hello, world!");
  * w.endElement("greeting");
  * w.endDocument();
  * 
- * - *

- * The resulting document will look like this: - *

- * + * + *

The resulting document will look like this: + * *

  *           <?xml version="1.0" standalone='yes'?>
- *          
+ *
  *           <greeting>Hello, world!</greeting>
  * 
- * - *

- * In fact, there is an even simpler convenience method, dataElement, - * designed for writing elements that contain only character data, so the code - * to generate the document could be shortened to - *

- * + * + *

In fact, there is an even simpler convenience method, dataElement, designed for + * writing elements that contain only character data, so the code to generate the document could be + * shortened to + * *

  * XmlWriter w = new XmlWriter();
- * 
+ *
  * w.startDocument();
  * w.dataElement("greeting", "Hello, world!");
  * w.endDocument();
  * 
- * + * *

Whitespace

- * - *

- * According to the XML Recommendation, all whitespace in an XML - * document is potentially significant to an application, so this class never - * adds newlines or indentation. If you insert three elements in a row, as in - *

- * + * + *

According to the XML Recommendation, all whitespace in an XML document is potentially + * significant to an application, so this class never adds newlines or indentation. If you insert + * three elements in a row, as in + * *

  * w.dataElement("item", "1");
  * w.dataElement("item", "2");
  * w.dataElement("item", "3");
  * 
- * - *

- * you will end up with - *

- * + * + *

you will end up with + * *

  *           <item>1</item><item>3</item><item>3</item>
  * 
- * - *

- * You need to invoke one of the characters methods explicitly to add - * newlines or indentation. Alternatively, you can use the data format mode (set - * the "dataFormat" property) which is optimized for writing purely - * data-oriented (or field-oriented) XML, and does automatic linebreaks and - * indentation (but does not support mixed content properly). See details below. - *

- * + * + *

You need to invoke one of the characters methods explicitly to add newlines or + * indentation. Alternatively, you can use the data format mode (set the "dataFormat" property) + * which is optimized for writing purely data-oriented (or field-oriented) XML, and does automatic + * linebreaks and indentation (but does not support mixed content properly). See details below. + * *

Namespace Support

- * - *

- * The writer contains extensive support for XML Namespaces, so that a client - * application does not have to keep track of prefixes and supply - * xmlns attributes. By default, the XML writer will generate - * Namespace declarations in the form _NS1, _NS2, etc., wherever they are + * + *

The writer contains extensive support for XML Namespaces, so that a client application does + * not have to keep track of prefixes and supply xmlns attributes. By default, the XML + * writer will generate Namespace declarations in the form _NS1, _NS2, etc., wherever they are * needed, as in the following example: - *

- * + * *
  * w.startDocument();
  * w.emptyElement("http://www.foo.com/ns/", "foo");
  * w.endDocument();
  * 
- * - *

- * The resulting document will look like this: - *

- * + * + *

The resulting document will look like this: + * *

  *           <?xml version="1.0" standalone='yes'?>
- *          
+ *
  *           <_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/>
  * 
- * - *

- * In many cases, document authors will prefer to choose their own prefixes - * rather than using the (ugly) default names. The XML writer allows two methods - * for selecting prefixes: - *

- * + * + *

In many cases, document authors will prefer to choose their own prefixes rather than using the + * (ugly) default names. The XML writer allows two methods for selecting prefixes: + * *

    - *
  1. the qualified name
  2. - *
  3. the {@link #setPrefix setPrefix} method.
  4. + *
  5. the qualified name + *
  6. the {@link #setPrefix setPrefix} method. *
- * - *

- * Whenever the XML writer finds a new Namespace URI, it checks to see if a - * qualified (prefixed) name is also available; if so it attempts to use the - * name's prefix (as long as the prefix is not already in use for another - * Namespace URI). - *

- * - *

- * Before writing a document, the client can also pre-map a prefix to a - * Namespace URI with the setPrefix method: - *

- * + * + *

Whenever the XML writer finds a new Namespace URI, it checks to see if a qualified (prefixed) + * name is also available; if so it attempts to use the name's prefix (as long as the prefix is not + * already in use for another Namespace URI). + * + *

Before writing a document, the client can also pre-map a prefix to a Namespace URI with the + * setPrefix method: + * *

  * w.setPrefix("http://www.foo.com/ns/", "foo");
  * w.startDocument();
  * w.emptyElement("http://www.foo.com/ns/", "foo");
  * w.endDocument();
  * 
- * - *

- * The resulting document will look like this: - *

- * + * + *

The resulting document will look like this: + * *

  *           <?xml version="1.0" standalone='yes'?>
- *          
+ *
  *           <foo:foo xmlns:foo="http://www.foo.com/ns/"/>
  * 
- * - *

- * The default Namespace simply uses an empty string as the prefix: - *

- * + * + *

The default Namespace simply uses an empty string as the prefix: + * *

  * w.setPrefix("http://www.foo.com/ns/", "");
  * w.startDocument();
  * w.emptyElement("http://www.foo.com/ns/", "foo");
  * w.endDocument();
  * 
- * - *

- * The resulting document will look like this: - *

- * + * + *

The resulting document will look like this: + * *

  *           <?xml version="1.0" standalone='yes'?>
- *          
+ *
  *           <foo xmlns="http://www.foo.com/ns/"/>
  * 
- * - *

- * By default, the XML writer will not declare a Namespace until it is actually - * used. Sometimes, this approach will create a large number of Namespace - * declarations, as in the following example: - *

- * + * + *

By default, the XML writer will not declare a Namespace until it is actually used. Sometimes, + * this approach will create a large number of Namespace declarations, as in the following example: + * *

  *           <xml version="1.0" standalone='yes'?>
- *          
+ *
  *           <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  *            <rdf:Description about="http://www.foo.com/ids/books/12345">
  *             <dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night</dc:title>
@@ -239,27 +191,23 @@
  *            </rdf:Description>
  *           </rdf:RDF>
  * 
- * - *

- * The "rdf" prefix is declared only once, because the RDF Namespace is used by - * the root element and can be inherited by all of its descendants; the "dc" - * prefix, on the other hand, is declared three times, because no higher element - * uses the Namespace. To solve this problem, you can instruct the XML writer to - * predeclare Namespaces on the root element even if they are not used there: - *

- * + * + *

The "rdf" prefix is declared only once, because the RDF Namespace is used by the root element + * and can be inherited by all of its descendants; the "dc" prefix, on the other hand, is declared + * three times, because no higher element uses the Namespace. To solve this problem, you can + * instruct the XML writer to predeclare Namespaces on the root element even if they are not used + * there: + * *

  * w.forceNSDecl("http://www.purl.org/dc/");
  * 
- * - *

- * Now, the "dc" prefix will be declared on the root element even though it's - * not needed there, and can be inherited by its descendants: - *

- * + * + *

Now, the "dc" prefix will be declared on the root element even though it's not needed there, + * and can be inherited by its descendants: + * *

  *           <xml version="1.0" standalone='yes'?>
- *          
+ *
  *           <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  *                       xmlns:dc="http://www.purl.org/dc/">
  *            <rdf:Description about="http://www.foo.com/ids/books/12345">
@@ -269,43 +217,30 @@
  *            </rdf:Description>
  *           </rdf:RDF>
  * 
- * - *

- * This approach is also useful for declaring Namespace prefixes that be used by - * qualified names appearing in attribute values or character data. - *

- * + * + *

This approach is also useful for declaring Namespace prefixes that be used by qualified names + * appearing in attribute values or character data. + * *

Data Format

- * - *

- * This mode, enabled by the "dataFormat" property, pretty-prints field-oriented - * XML without mixed content. All added indentation and newlines will be passed - * on down the filter chain (if any). - *

- * - *

- * In general, all whitespace in an XML document is potentially significant, so - * a general-purpose XML writing tool cannot add newlines or indentation. - *

- * - *

- * There is, however, a large class of XML documents where information is - * strictly fielded: each element contains either character data or other - * elements, but not both. For this special case, it is possible for a writing - * tool to provide automatic indentation and newlines without requiring extra - * work from the user. Note that this class will likely not yield appropriate - * results for document-oriented XML like XHTML pages, which mix character data - * and elements together. - *

- * - *

- * This writer mode will automatically place each start tag on a new line, - * optionally indented if an indent step is provided (by default, there is no - * indentation). If an element contains other elements, the end tag will also - * appear on a new line with leading indentation. Consider, for example, the - * following code: - *

- * + * + *

This mode, enabled by the "dataFormat" property, pretty-prints field-oriented XML without + * mixed content. All added indentation and newlines will be passed on down the filter chain (if + * any). + * + *

In general, all whitespace in an XML document is potentially significant, so a general-purpose + * XML writing tool cannot add newlines or indentation. + * + *

There is, however, a large class of XML documents where information is strictly fielded: each + * element contains either character data or other elements, but not both. For this special case, it + * is possible for a writing tool to provide automatic indentation and newlines without requiring + * extra work from the user. Note that this class will likely not yield appropriate results for + * document-oriented XML like XHTML pages, which mix character data and elements together. + * + *

This writer mode will automatically place each start tag on a new line, optionally indented if + * an indent step is provided (by default, there is no indentation). If an element contains other + * elements, the end tag will also appear on a new line with leading indentation. Consider, for + * example, the following code: + * *

  * XmlWriter w = new XmlWriter();
  * w.setDataFormat(true);
@@ -318,21 +253,19 @@
  * w.endElement("Person");
  * w.endDocument();
  * 
- * - *

- * This code will produce the following document: - *

- * + * + *

This code will produce the following document: + * *

  *           <?xml version="1.0" standalone='yes'?>
- *          
+ *
  *           <Person>
  *             <name>Jane Smith</name>
  *             <date-of-birth>1965-05-23</date-of-birth>
  *             <citizenship>US</citizenship>
  *           </Person>
  * 
- * + * * @see org.xml.sax.XMLFilter * @see org.xml.sax.ContentHandler * @author David Megginson, Jerome Louvel (contact@restlet.com) @@ -348,46 +281,30 @@ public final class XmlWriter extends XMLFilterImpl { private volatile int depth = 0; - /** - * The document declarations table. - */ + /** The document declarations table. */ private volatile Map doneDeclTable; - /** - * The element level. - */ + /** The element level. */ private volatile int elementLevel = 0; - /** - * Constant representing empty attributes. - */ + /** Constant representing empty attributes. */ private final Attributes EMPTY_ATTS = new AttributesImpl(); - /** - * The forced declarations table. - */ + /** The forced declarations table. */ private volatile Map forcedDeclTable; private volatile int indentStep = 0; - /** - * The namespace support. - */ + /** The namespace support. */ private volatile NamespaceSupport nsSupport; - /** - * The underlying writer. - */ + /** The underlying writer. */ private volatile Writer output; - /** - * The prefix counter. - */ + /** The prefix counter. */ private volatile int prefixCounter = 0; - /** - * The prefixes table. - */ + /** The prefixes table. */ private volatile Map prefixTable; private volatile Object state = SEEN_NOTHING; @@ -396,9 +313,8 @@ public final class XmlWriter extends XMLFilterImpl { /** * Create a new XML writer. - *

- * Write to standard output. - *

+ * + *

Write to standard output. */ public XmlWriter() { init(null); @@ -406,9 +322,8 @@ public XmlWriter() { /** * Constructor. - * - * @param out - * The underlying output stream. + * + * @param out The underlying output stream. */ public XmlWriter(OutputStream out) { this(new OutputStreamWriter(out)); @@ -416,9 +331,8 @@ public XmlWriter(OutputStream out) { /** * Constructor. - * - * @param out - * The underlying output stream. + * + * @param out The underlying output stream. */ public XmlWriter(OutputStream out, Charset cs) { this(new OutputStreamWriter(out, cs)); @@ -426,9 +340,8 @@ public XmlWriter(OutputStream out, Charset cs) { /** * Constructor. - * - * @param out - * The underlying output stream. + * + * @param out The underlying output stream. */ public XmlWriter(OutputStream out, CharsetEncoder enc) { this(new OutputStreamWriter(out, enc)); @@ -436,23 +349,19 @@ public XmlWriter(OutputStream out, CharsetEncoder enc) { /** * Constructor. - * - * @param out - * The underlying output stream. + * + * @param out The underlying output stream. */ - public XmlWriter(OutputStream out, String charsetName) - throws UnsupportedEncodingException { + public XmlWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException { this(new OutputStreamWriter(out, charsetName)); } /** * Create a new XML writer. - *

- * Write to the writer provided. - *

- * - * @param writer - * The output destination, or null to use standard output. + * + *

Write to the writer provided. + * + * @param writer The output destination, or null to use standard output. */ public XmlWriter(Writer writer) { init(writer); @@ -460,12 +369,10 @@ public XmlWriter(Writer writer) { /** * Create a new XML writer. - *

- * Use the specified XML reader as the parent. - *

- * - * @param xmlreader - * The parent in the filter chain, or null for no parent. + * + *

Use the specified XML reader as the parent. + * + * @param xmlreader The parent in the filter chain, or null for no parent. */ public XmlWriter(XMLReader xmlreader) { super(xmlreader); @@ -474,15 +381,11 @@ public XmlWriter(XMLReader xmlreader) { /** * Create a new XML writer. - *

- * Use the specified XML reader as the parent, and write to the specified - * writer. - *

- * - * @param xmlreader - * The parent in the filter chain, or null for no parent. - * @param writer - * The output destination, or null to use standard output. + * + *

Use the specified XML reader as the parent, and write to the specified writer. + * + * @param xmlreader The parent in the filter chain, or null for no parent. + * @param writer The output destination, or null to use standard output. */ public XmlWriter(XMLReader xmlreader, Writer writer) { super(xmlreader); @@ -490,22 +393,16 @@ public XmlWriter(XMLReader xmlreader, Writer writer) { } /** - * Write character data. Pass the event on down the filter chain for further - * processing. - * - * @param ch - * The array of characters to write. - * @param start - * The starting position in the array. - * @param len - * The number of characters to write. - * @exception org.xml.sax.SAXException - * If there is an error writing the characters, or if a - * restlet further down the filter chain raises an exception. + * Write character data. Pass the event on down the filter chain for further processing. + * + * @param ch The array of characters to write. + * @param start The starting position in the array. + * @param len The number of characters to write. + * @exception org.xml.sax.SAXException If there is an error writing the characters, or if a + * restlet further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#characters */ - private void characters(boolean dataFormat, char ch[], int start, int len) - throws SAXException { + private void characters(boolean dataFormat, char ch[], int start, int len) throws SAXException { if (dataFormat) { this.state = SEEN_DATA; } @@ -520,37 +417,28 @@ private void characters(boolean dataFormat, char ch[], int start, int len) /** * Write a string of character data, with XML escaping. - *

- * This is a convenience method that takes an XML String, converts it to a - * character array, then invokes {@link #characters(char[], int, int)}. - *

- * - * @param data - * The character data. - * @exception org.xml.sax.SAXException - * If there is an error writing the string, or if a restlet - * further down the filter chain raises an exception. + * + *

This is a convenience method that takes an XML String, converts it to a character array, + * then invokes {@link #characters(char[], int, int)}. + * + * @param data The character data. + * @exception org.xml.sax.SAXException If there is an error writing the string, or if a restlet + * further down the filter chain raises an exception. * @see #characters(char[], int, int) */ - private void characters(boolean dataFormat, String data) - throws SAXException { + private void characters(boolean dataFormat, String data) throws SAXException { final char ch[] = data.toCharArray(); characters(dataFormat, ch, 0, ch.length); } /** - * Write character data. Pass the event on down the filter chain for further - * processing. - * - * @param ch - * The array of characters to write. - * @param start - * The starting position in the array. - * @param len - * The number of characters to write. - * @exception org.xml.sax.SAXException - * If there is an error writing the characters, or if a - * restlet further down the filter chain raises an exception. + * Write character data. Pass the event on down the filter chain for further processing. + * + * @param ch The array of characters to write. + * @param start The starting position in the array. + * @param len The number of characters to write. + * @exception org.xml.sax.SAXException If there is an error writing the characters, or if a + * restlet further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#characters */ @Override @@ -560,16 +448,13 @@ public void characters(char ch[], int start, int len) throws SAXException { /** * Write a string of character data, with XML escaping. - *

- * This is a convenience method that takes an XML String, converts it to a - * character array, then invokes {@link #characters(char[], int, int)}. - *

- * - * @param data - * The character data. - * @exception org.xml.sax.SAXException - * If there is an error writing the string, or if a restlet - * further down the filter chain raises an exception. + * + *

This is a convenience method that takes an XML String, converts it to a character array, + * then invokes {@link #characters(char[], int, int)}. + * + * @param data The character data. + * @exception org.xml.sax.SAXException If there is an error writing the string, or if a restlet + * further down the filter chain raises an exception. * @see #characters(char[], int, int) */ public void characters(String data) throws SAXException { @@ -577,107 +462,73 @@ public void characters(String data) throws SAXException { } /** - * Write an element with character data content but no attributes or - * Namespace URI. - * - *

- * This is a convenience method to write a complete element with character - * data content, including the start tag and end tag. The method provides an - * empty string for the Namespace URI, and empty string for the qualified - * name, and an empty attribute list. - *

- * - *

- * This method invokes - * {@link #startElement(String, String, String, Attributes)}, followed by - * {@link #characters(String)}, followed by - * {@link #endElement(String, String, String)}. - *

- * - * @param localName - * The element's local name. - * @param content - * The character data content. - * @exception org.xml.sax.SAXException - * If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * Write an element with character data content but no attributes or Namespace URI. + * + *

This is a convenience method to write a complete element with character data content, + * including the start tag and end tag. The method provides an empty string for the Namespace + * URI, and empty string for the qualified name, and an empty attribute list. + * + *

This method invokes {@link #startElement(String, String, String, Attributes)}, followed by + * {@link #characters(String)}, followed by {@link #endElement(String, String, String)}. + * + * @param localName The element's local name. + * @param content The character data content. + * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a + * restlet further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) * @see #characters(String) * @see #endElement(String, String, String) */ - public void dataElement(String localName, String content) - throws SAXException { + public void dataElement(String localName, String content) throws SAXException { dataElement("", localName, "", this.EMPTY_ATTS, content); } /** * Write an element with character data content but no attributes. - * - *

- * This is a convenience method to write a complete element with character - * data content, including the start tag and end tag. This method provides - * an empty string for the qname and an empty attribute list. - *

- * - *

- * This method invokes - * {@link #startElement(String, String, String, Attributes)}, followed by - * {@link #characters(String)}, followed by - * {@link #endElement(String, String, String)}. - *

- * - * @param uri - * The element's Namespace URI. - * @param localName - * The element's local name. - * @param content - * The character data content. - * @exception org.xml.sax.SAXException - * If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * + *

This is a convenience method to write a complete element with character data content, + * including the start tag and end tag. This method provides an empty string for the qname and + * an empty attribute list. + * + *

This method invokes {@link #startElement(String, String, String, Attributes)}, followed by + * {@link #characters(String)}, followed by {@link #endElement(String, String, String)}. + * + * @param uri The element's Namespace URI. + * @param localName The element's local name. + * @param content The character data content. + * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a + * restlet further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) * @see #characters(String) * @see #endElement(String, String, String) */ - public void dataElement(String uri, String localName, String content) - throws SAXException { + public void dataElement(String uri, String localName, String content) throws SAXException { dataElement(uri, localName, "", this.EMPTY_ATTS, content); } /** * Write an element with character data content. - * - *

- * This is a convenience method to write a complete element with character - * data content, including the start tag and end tag. - *

- * - *

- * This method invokes - * {@link #startElement(String, String, String, Attributes)}, followed by - * {@link #characters(String)}, followed by - * {@link #endElement(String, String, String)}. - *

- * - * @param uri - * The element's Namespace URI. - * @param localName - * The element's local name. - * @param qName - * The element's default qualified name. - * @param atts - * The element's attributes. - * @param content - * The character data content. - * @exception org.xml.sax.SAXException - * If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * + *

This is a convenience method to write a complete element with character data content, + * including the start tag and end tag. + * + *

This method invokes {@link #startElement(String, String, String, Attributes)}, followed by + * {@link #characters(String)}, followed by {@link #endElement(String, String, String)}. + * + * @param uri The element's Namespace URI. + * @param localName The element's local name. + * @param qName The element's default qualified name. + * @param atts The element's attributes. + * @param content The character data content. + * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a + * restlet further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) * @see #characters(String) * @see #endElement(String, String, String) */ - public void dataElement(String uri, String localName, String qName, - Attributes atts, String content) throws SAXException { + public void dataElement( + String uri, String localName, String qName, Attributes atts, String content) + throws SAXException { startElement(uri, localName, qName, atts); characters(content); endElement(uri, localName, qName); @@ -685,10 +536,9 @@ public void dataElement(String uri, String localName, String qName, /** * Print indentation for the current level. - * - * @exception org.xml.sax.SAXException - * If there is an error writing the indentation characters, - * or if a filter further down the chain raises an exception. + * + * @exception org.xml.sax.SAXException If there is an error writing the indentation characters, + * or if a filter further down the chain raises an exception. */ private void doIndent() throws SAXException { if ((this.indentStep > 0) && (this.depth > 0)) { @@ -700,17 +550,14 @@ private void doIndent() throws SAXException { } /** - * Determine the prefix for an element or attribute name. TODO: this method - * probably needs some cleanup. - * - * @param uri - * The Namespace URI. - * @param qName - * The qualified name (optional); this will be used to indicate - * the preferred prefix if none is currently bound. - * @param isElement - * true if this is an element name, false if it is an attribute - * name (which cannot use the default Namespace). + * Determine the prefix for an element or attribute name. TODO: this method probably needs some + * cleanup. + * + * @param uri The Namespace URI. + * @param qName The qualified name (optional); this will be used to indicate the preferred + * prefix if none is currently bound. + * @param isElement true if this is an element name, false if it is an attribute name (which + * cannot use the default Namespace). */ private String doPrefix(String uri, String qName, boolean isElement) { final String defaultNS = this.nsSupport.getURI(""); @@ -731,14 +578,15 @@ private String doPrefix(String uri, String qName, boolean isElement) { } prefix = this.doneDeclTable.get(uri); if ((prefix != null) - && (((!isElement || (defaultNS != null)) && prefix.isEmpty()) || (this.nsSupport - .getURI(prefix) != null))) { + && (((!isElement || (defaultNS != null)) && prefix.isEmpty()) + || (this.nsSupport.getURI(prefix) != null))) { prefix = null; } if (prefix == null) { prefix = this.prefixTable.get(uri); if ((prefix != null) - && (((!isElement || (defaultNS != null)) && prefix.isEmpty()) || (this.nsSupport.getURI(prefix) != null))) { + && (((!isElement || (defaultNS != null)) && prefix.isEmpty()) + || (this.nsSupport.getURI(prefix) != null))) { prefix = null; } } @@ -752,8 +600,9 @@ private String doPrefix(String uri, String qName, boolean isElement) { prefix = qName.substring(0, i); } } - for (; (prefix == null) || (this.nsSupport.getURI(prefix) != null); prefix = "__NS" - + ++this.prefixCounter) { + for (; + (prefix == null) || (this.nsSupport.getURI(prefix) != null); + prefix = "__NS" + ++this.prefixCounter) { // Do nothing } @@ -768,18 +617,14 @@ private String doPrefix(String uri, String qName, boolean isElement) { /** * Add an empty element without a Namespace URI, qname or attributes. - * - *

- * This method will supply an empty string for the qname, and empty string - * for the Namespace URI, and an empty attribute list. It invokes - * {@link #emptyElement(String, String, String, Attributes)} directly. - *

- * - * @param localName - * The element's local name. - * @exception org.xml.sax.SAXException - * If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * + *

This method will supply an empty string for the qname, and empty string for the Namespace + * URI, and an empty attribute list. It invokes {@link #emptyElement(String, String, String, + * Attributes)} directly. + * + * @param localName The element's local name. + * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a + * restlet further down the filter chain raises an exception. * @see #emptyElement(String, String, String, Attributes) */ public void emptyElement(String localName) throws SAXException { @@ -788,20 +633,14 @@ public void emptyElement(String localName) throws SAXException { /** * Add an empty element without a qname or attributes. - * - *

- * This method will supply an empty string for the qname and an empty - * attribute list. It invokes - * {@link #emptyElement(String, String, String, Attributes)} directly. - *

- * - * @param uri - * The element's Namespace URI. - * @param localName - * The element's local name. - * @exception org.xml.sax.SAXException - * If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * + *

This method will supply an empty string for the qname and an empty attribute list. It + * invokes {@link #emptyElement(String, String, String, Attributes)} directly. + * + * @param uri The element's Namespace URI. + * @param localName The element's local name. + * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a + * restlet further down the filter chain raises an exception. * @see #emptyElement(String, String, String, Attributes) */ public void emptyElement(String uri, String localName) throws SAXException { @@ -809,32 +648,24 @@ public void emptyElement(String uri, String localName) throws SAXException { } /** - * Write an empty element. This method writes an empty element tag rather - * than a start tag followed by an end tag. Both a {@link #startElement - * startElement} and an {@link #endElement endElement} event will be passed - * on down the filter chain. - * - * @param uri - * The element's Namespace URI, or the empty string if the - * element has no Namespace or if Namespace processing is not - * being performed. - * @param localName - * The element's local name (without prefix). This parameter must - * be provided. - * @param qName - * The element's qualified name (with prefix), or the empty - * string if none is available. This parameter is strictly - * advisory: the writer may or may not use the prefix attached. - * @param atts - * The element's attribute list. - * @exception org.xml.sax.SAXException - * If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * Write an empty element. This method writes an empty element tag rather than a start tag + * followed by an end tag. Both a {@link #startElement startElement} and an {@link #endElement + * endElement} event will be passed on down the filter chain. + * + * @param uri The element's Namespace URI, or the empty string if the element has no Namespace + * or if Namespace processing is not being performed. + * @param localName The element's local name (without prefix). This parameter must be provided. + * @param qName The element's qualified name (with prefix), or the empty string if none is + * available. This parameter is strictly advisory: the writer may or may not use the prefix + * attached. + * @param atts The element's attribute list. + * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a + * restlet further down the filter chain raises an exception. * @see #startElement * @see #endElement */ - public void emptyElement(String uri, String localName, String qName, - Attributes atts) throws SAXException { + public void emptyElement(String uri, String localName, String qName, Attributes atts) + throws SAXException { if (isDataFormat()) { this.state = SEEN_ELEMENT; if (this.depth > 0) { @@ -857,12 +688,11 @@ public void emptyElement(String uri, String localName, String qName, } /** - * Write a newline at the end of the document. Pass the event on down the - * filter chain for further processing. - * - * @exception org.xml.sax.SAXException - * If there is an error writing the newline, or if a restlet - * further down the filter chain raises an exception. + * Write a newline at the end of the document. Pass the event on down the filter chain for + * further processing. + * + * @exception org.xml.sax.SAXException If there is an error writing the newline, or if a restlet + * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#endDocument */ @Override @@ -878,18 +708,13 @@ public void endDocument() throws SAXException { /** * End an element without a Namespace URI or qname. - * - *

- * This method will supply an empty string for the qName and an empty string - * for the Namespace URI. It invokes - * {@link #endElement(String, String, String)} directly. - *

- * - * @param localName - * The element's local name. - * @exception org.xml.sax.SAXException - * If there is an error writing the end tag, or if a restlet - * further down the filter chain raises an exception. + * + *

This method will supply an empty string for the qName and an empty string for the + * Namespace URI. It invokes {@link #endElement(String, String, String)} directly. + * + * @param localName The element's local name. + * @exception org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet + * further down the filter chain raises an exception. * @see #endElement(String, String, String) */ public void endElement(String localName) throws SAXException { @@ -898,19 +723,14 @@ public void endElement(String localName) throws SAXException { /** * End an element without a qname. - * - *

- * This method will supply an empty string for the qName. It invokes - * {@link #endElement(String, String, String)} directly. - *

- * - * @param uri - * The element's Namespace URI. - * @param localName - * The element's local name. - * @exception org.xml.sax.SAXException - * If there is an error writing the end tag, or if a restlet - * further down the filter chain raises an exception. + * + *

This method will supply an empty string for the qName. It invokes {@link + * #endElement(String, String, String)} directly. + * + * @param uri The element's Namespace URI. + * @param localName The element's local name. + * @exception org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet + * further down the filter chain raises an exception. * @see #endElement(String, String, String) */ public void endElement(String uri, String localName) throws SAXException { @@ -918,26 +738,19 @@ public void endElement(String uri, String localName) throws SAXException { } /** - * Write an end tag. Pass the event on down the filter chain for further - * processing. - * - * @param uri - * The Namespace URI, or the empty string if none is available. - * @param localName - * The element's local (unprefixed) name (required). - * @param qName - * The element's qualified (prefixed) name, or the empty string - * is none is available. This method will use the qName as a - * template for generating a prefix if necessary, but it is not - * guaranteed to use the same qName. - * @exception org.xml.sax.SAXException - * If there is an error writing the end tag, or if a restlet - * further down the filter chain raises an exception. + * Write an end tag. Pass the event on down the filter chain for further processing. + * + * @param uri The Namespace URI, or the empty string if none is available. + * @param localName The element's local (unprefixed) name (required). + * @param qName The element's qualified (prefixed) name, or the empty string is none is + * available. This method will use the qName as a template for generating a prefix if + * necessary, but it is not guaranteed to use the same qName. + * @exception org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet + * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#endElement */ @Override - public void endElement(String uri, String localName, String qName) - throws SAXException { + public void endElement(String uri, String localName, String qName) throws SAXException { if (isDataFormat()) { this.depth--; if (this.state == SEEN_ELEMENT) { @@ -963,16 +776,14 @@ public void endElement(String uri, String localName, String qName) /** * Flush the output. - *

- * This method flushes the output stream. It is especially useful when you - * need to make certain that the entire document has been written to output - * but do not want to close the output stream. - *

- *

- * This method is invoked automatically by the {@link #endDocument - * endDocument} method after writing a document. - *

- * + * + *

This method flushes the output stream. It is especially useful when you need to make + * certain that the entire document has been written to output but do not want to close the + * output stream. + * + *

This method is invoked automatically by the {@link #endDocument endDocument} method after + * writing a document. + * * @see #reset */ public void flush() throws IOException { @@ -985,19 +796,15 @@ public void flush() throws IOException { /** * Force a Namespace to be declared on the root element. - *

- * By default, the XMLWriter will declare only the Namespaces needed for an - * element; as a result, a Namespace may be declared many places in a - * document if it is not used on the root element. - *

- *

- * This method forces a Namespace to be declared on the root element even if - * it is not used there, and reduces the number of xmlns attributes in the - * document. - *

- * - * @param uri - * The Namespace URI to declare. + * + *

By default, the XMLWriter will declare only the Namespaces needed for an element; as a + * result, a Namespace may be declared many places in a document if it is not used on the root + * element. + * + *

This method forces a Namespace to be declared on the root element even if it is not used + * there, and reduces the number of xmlns attributes in the document. + * + * @param uri The Namespace URI to declare. * @see #forceNSDecl(java.lang.String,java.lang.String) * @see #setPrefix */ @@ -1011,16 +818,12 @@ public void forceNSDecl(String uri) { /** * Force a Namespace declaration with a preferred prefix. - *

- * This is a convenience method that invokes {@link #setPrefix setPrefix} - * then {@link #forceNSDecl(java.lang.String) forceNSDecl}. - *

- * - * @param uri - * The Namespace URI to declare on the root element. - * @param prefix - * The preferred prefix for the Namespace, or "" for the default - * Namespace. + * + *

This is a convenience method that invokes {@link #setPrefix setPrefix} then {@link + * #forceNSDecl(java.lang.String) forceNSDecl}. + * + * @param uri The Namespace URI to declare on the root element. + * @param prefix The preferred prefix for the Namespace, or "" for the default Namespace. * @see #setPrefix * @see #forceNSDecl(java.lang.String) */ @@ -1030,8 +833,8 @@ public void forceNSDecl(String uri, String prefix) { } /** - * Force all Namespaces to be declared. This method is used on the root - * element to ensure that the predeclared Namespaces all appear. + * Force all Namespaces to be declared. This method is used on the root element to ensure that + * the predeclared Namespaces all appear. */ private void forceNSDecls() { for (final String prefix : this.forcedDeclTable.keySet()) { @@ -1041,13 +844,11 @@ private void forceNSDecls() { /** * Return the current indent step. - *

- * Return the current indent step: each start tag will be indented by this - * number of spaces times the number of ancestors that the element has. - *

- * - * @return The number of spaces in each indentation step, or 0 or less for - * no indentation. + * + *

Return the current indent step: each start tag will be indented by this number of spaces + * times the number of ancestors that the element has. + * + * @return The number of spaces in each indentation step, or 0 or less for no indentation. */ public int getIndentStep() { return this.indentStep; @@ -1055,9 +856,8 @@ public int getIndentStep() { /** * Get the current or preferred prefix for a Namespace URI. - * - * @param uri - * The Namespace URI. + * + * @param uri The Namespace URI. * @return The preferred prefix, or "" for the default Namespace. * @see #setPrefix */ @@ -1067,7 +867,7 @@ public String getPrefix(String uri) { /** * Returns the underlying writer. - * + * * @return The underlying writer. */ public Writer getWriter() { @@ -1075,35 +875,27 @@ public Writer getWriter() { } /** - * Write ignorable whitespace. Pass the event on down the filter chain for - * further processing. - * - * @param ch - * The array of characters to write. - * @param start - * The starting position in the array. - * @param length - * The number of characters to write. - * @exception org.xml.sax.SAXException - * If there is an error writing the whitespace, or if a - * restlet further down the filter chain raises an exception. + * Write ignorable whitespace. Pass the event on down the filter chain for further processing. + * + * @param ch The array of characters to write. + * @param start The starting position in the array. + * @param length The number of characters to write. + * @exception org.xml.sax.SAXException If there is an error writing the whitespace, or if a + * restlet further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#ignorableWhitespace */ @Override - public void ignorableWhitespace(char ch[], int start, int length) - throws SAXException { + public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { writeEsc(ch, start, length, false); super.ignorableWhitespace(ch, start, length); } /** * Internal initialization method. - * - *

- * All of the public constructors invoke this method. - * - * @param writer - * The output destination, or null to use standard output. + * + *

All of the public constructors invoke this method. + * + * @param writer The output destination, or null to use standard output. */ private void init(Writer writer) { setOutput(writer); @@ -1118,21 +910,17 @@ public boolean isDataFormat() { } /** - * Write a processing instruction. Pass the event on down the filter chain - * for further processing. - * - * @param target - * The PI target. - * @param data - * The PI data. - * @exception org.xml.sax.SAXException - * If there is an error writing the PI, or if a restlet - * further down the filter chain raises an exception. + * Write a processing instruction. Pass the event on down the filter chain for further + * processing. + * + * @param target The PI target. + * @param data The PI data. + * @exception org.xml.sax.SAXException If there is an error writing the PI, or if a restlet + * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#processingInstruction */ @Override - public void processingInstruction(String target, String data) - throws SAXException { + public void processingInstruction(String target, String data) throws SAXException { write(" - * This method is especially useful if the writer throws an exception before - * it is finished, and you want to reuse the writer for a new document. It - * is usually a good idea to invoke {@link #flush flush} before resetting - * the writer, to make sure that no output is lost. - *

- * - *

- * This method is invoked automatically by the {@link #startDocument - * startDocument} method before writing a new document. - *

- * - *

- * Note: this method will not clear the prefix or - * URI information in the writer or the selected output writer. - *

- * + * + *

This method is especially useful if the writer throws an exception before it is finished, + * and you want to reuse the writer for a new document. It is usually a good idea to invoke + * {@link #flush flush} before resetting the writer, to make sure that no output is lost. + * + *

This method is invoked automatically by the {@link #startDocument startDocument} method + * before writing a new document. + * + *

Note: this method will not clear the prefix or URI information + * in the writer or the selected output writer. + * * @see #flush */ public void reset() { @@ -1188,9 +969,8 @@ public void setDataFormat(boolean dataFormat) { /** * Set the current indent step. - * - * @param indentStep - * The new indent step (0 or less for no indentation). + * + * @param indentStep The new indent step (0 or less for no indentation). */ public void setIndentStep(int indentStep) { this.indentStep = indentStep; @@ -1198,9 +978,8 @@ public void setIndentStep(int indentStep) { /** * Set a new output destination for the document. - * - * @param writer - * The output destination, or null to use standard output. + * + * @param writer The output destination, or null to use standard output. * @see #flush */ public void setOutput(Writer writer) { @@ -1213,16 +992,12 @@ public void setOutput(Writer writer) { /** * Specify a preferred prefix for a Namespace URI. - *

- * Note that this method does not actually force the Namespace to be - * declared; to do that, use the {@link #forceNSDecl(java.lang.String) - * forceNSDecl} method as well. - *

- * - * @param uri - * The Namespace URI. - * @param prefix - * The preferred prefix, or "" to select the default Namespace. + * + *

Note that this method does not actually force the Namespace to be declared; to do that, + * use the {@link #forceNSDecl(java.lang.String) forceNSDecl} method as well. + * + * @param uri The Namespace URI. + * @param prefix The preferred prefix, or "" to select the default Namespace. * @see #getPrefix * @see #forceNSDecl(java.lang.String) * @see #forceNSDecl(java.lang.String,java.lang.String) @@ -1232,12 +1007,11 @@ public void setPrefix(String uri, String prefix) { } /** - * Write the XML declaration at the beginning of the document. Pass the - * event on down the filter chain for further processing. - * - * @exception org.xml.sax.SAXException - * If there is an error writing the XML declaration, or if a - * restlet further down the filter chain raises an exception. + * Write the XML declaration at the beginning of the document. Pass the event on down the filter + * chain for further processing. + * + * @exception org.xml.sax.SAXException If there is an error writing the XML declaration, or if a + * restlet further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#startDocument */ @Override @@ -1249,18 +1023,14 @@ public void startDocument() throws SAXException { /** * Start a new element without a qname, attributes or a Namespace URI. - * - *

- * This method will provide an empty string for the Namespace URI, and empty - * string for the qualified name, and a default empty attribute list. It - * invokes #startElement(String, String, String, Attributes)} directly. - *

- * - * @param localName - * The element's local name. - * @exception org.xml.sax.SAXException - * If there is an error writing the start tag, or if a - * restlet further down the filter chain raises an exception. + * + *

This method will provide an empty string for the Namespace URI, and empty string for the + * qualified name, and a default empty attribute list. It invokes #startElement(String, String, + * String, Attributes)} directly. + * + * @param localName The element's local name. + * @exception org.xml.sax.SAXException If there is an error writing the start tag, or if a + * restlet further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) */ public void startElement(String localName) throws SAXException { @@ -1269,20 +1039,15 @@ public void startElement(String localName) throws SAXException { /** * Start a new element without a qname or attributes. - * - *

- * This method will provide a default empty attribute list and an empty - * string for the qualified name. It invokes - * {@link #startElement(String, String, String, Attributes)} directly. - *

- * - * @param uri - * The element's Namespace URI. - * @param localName - * The element's local name. - * @exception org.xml.sax.SAXException - * If there is an error writing the start tag, or if a - * restlet further down the filter chain raises an exception. + * + *

This method will provide a default empty attribute list and an empty string for the + * qualified name. It invokes {@link #startElement(String, String, String, Attributes)} + * directly. + * + * @param uri The element's Namespace URI. + * @param localName The element's local name. + * @exception org.xml.sax.SAXException If there is an error writing the start tag, or if a + * restlet further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) */ public void startElement(String uri, String localName) throws SAXException { @@ -1290,28 +1055,21 @@ public void startElement(String uri, String localName) throws SAXException { } /** - * Write a start tag. Pass the event on down the filter chain for further - * processing. - * - * @param uri - * The Namespace URI, or the empty string if none is available. - * @param localName - * The element's local (unprefixed) name (required). - * @param qName - * The element's qualified (prefixed) name, or the empty string - * is none is available. This method will use the qName as a - * template for generating a prefix if necessary, but it is not - * guaranteed to use the same qName. - * @param atts - * The element's attribute list (must not be null). - * @exception org.xml.sax.SAXException - * If there is an error writing the start tag, or if a - * restlet further down the filter chain raises an exception. + * Write a start tag. Pass the event on down the filter chain for further processing. + * + * @param uri The Namespace URI, or the empty string if none is available. + * @param localName The element's local (unprefixed) name (required). + * @param qName The element's qualified (prefixed) name, or the empty string is none is + * available. This method will use the qName as a template for generating a prefix if + * necessary, but it is not guaranteed to use the same qName. + * @param atts The element's attribute list (must not be null). + * @exception org.xml.sax.SAXException If there is an error writing the start tag, or if a + * restlet further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#startElement */ @Override - public void startElement(String uri, String localName, String qName, - Attributes atts) throws SAXException { + public void startElement(String uri, String localName, String qName, Attributes atts) + throws SAXException { if (isDataFormat()) { this.stateStack.push(SEEN_ELEMENT); this.state = SEEN_NOTHING; @@ -1340,12 +1098,10 @@ public void startElement(String uri, String localName, String qName, /** * Write a raw character. - * - * @param c - * The character to write. - * @exception org.xml.sax.SAXException - * If there is an error writing the character, this method - * will throw an IOException wrapped in a SAXException. + * + * @param c The character to write. + * @exception org.xml.sax.SAXException If there is an error writing the character, this method + * will throw an IOException wrapped in a SAXException. */ private void write(char c) throws SAXException { try { @@ -1357,11 +1113,10 @@ private void write(char c) throws SAXException { /** * Write a raw string. - * + * * @param s - * @exception org.xml.sax.SAXException - * If there is an error writing the string, this method will - * throw an IOException wrapped in a SAXException + * @exception org.xml.sax.SAXException If there is an error writing the string, this method will + * throw an IOException wrapped in a SAXException */ private void write(String s) throws SAXException { try { @@ -1372,15 +1127,11 @@ private void write(String s) throws SAXException { } /** - * Write out an attribute list, escaping values. The names will have - * prefixes added to them. - * - * @param atts - * The attribute list to write. - * @exception org.xml.SAXException - * If there is an error writing the attribute list, this - * method will throw an IOException wrapped in a - * SAXException. + * Write out an attribute list, escaping values. The names will have prefixes added to them. + * + * @param atts The attribute list to write. + * @exception org.xml.SAXException If there is an error writing the attribute list, this method + * will throw an IOException wrapped in a SAXException. */ private void writeAttributes(Attributes atts) throws SAXException { final int len = atts.getLength(); @@ -1388,15 +1139,13 @@ private void writeAttributes(Attributes atts) throws SAXException { if ("xmlns".equals(atts.getQName(i))) { // Redefines the default namespace. forceNSDecl(atts.getValue(i)); - } else if (atts.getQName(i) != null - && atts.getQName(i).startsWith("xmlns")) { + } else if (atts.getQName(i) != null && atts.getQName(i).startsWith("xmlns")) { // Defines the namespace using its prefix. forceNSDecl(atts.getValue(i), atts.getLocalName(i)); } else { final char ch[] = atts.getValue(i).toCharArray(); write(' '); - writeName(atts.getURI(i), atts.getLocalName(i), - atts.getQName(i), false); + writeName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i), false); write("=\""); writeEsc(ch, 0, ch.length, true); write('"'); @@ -1406,69 +1155,57 @@ private void writeAttributes(Attributes atts) throws SAXException { /** * Write an array of data characters with escaping. - * - * @param ch - * The array of characters. - * @param start - * The starting position. - * @param length - * The number of characters to use. - * @param isAttVal - * true if this is an attribute value literal. - * @exception org.xml.SAXException - * If there is an error writing the characters, this method - * will throw an IOException wrapped in a SAXException. + * + * @param ch The array of characters. + * @param start The starting position. + * @param length The number of characters to use. + * @param isAttVal true if this is an attribute value literal. + * @exception org.xml.SAXException If there is an error writing the characters, this method will + * throw an IOException wrapped in a SAXException. */ - private void writeEsc(char ch[], int start, int length, boolean isAttVal) - throws SAXException { + private void writeEsc(char ch[], int start, int length, boolean isAttVal) throws SAXException { for (int i = start; i < start + length; i++) { switch (ch[i]) { - case '&': - write("&"); - break; - case '<': - write("<"); - break; - case '>': - write(">"); - break; - case '\"': - if (isAttVal) { - write("""); - } else { - write('\"'); - } - break; - default: - if (ch[i] > '\u007f') { - write("&#"); - write(Integer.toString(ch[i])); - write(';'); - } else { - write(ch[i]); - } + case '&': + write("&"); + break; + case '<': + write("<"); + break; + case '>': + write(">"); + break; + case '\"': + if (isAttVal) { + write("""); + } else { + write('\"'); + } + break; + default: + if (ch[i] > '\u007f') { + write("&#"); + write(Integer.toString(ch[i])); + write(';'); + } else { + write(ch[i]); + } } } } /** * Write an element or attribute name. - * - * @param uri - * The Namespace URI. - * @param localName - * The local name. - * @param qName - * The prefixed name, if available, or the empty string. - * @param isElement - * true if this is an element name, false if it is an attribute - * name. - * @exception org.xml.sax.SAXException - * This method will throw an IOException wrapped in a - * SAXException if there is an error writing the name. + * + * @param uri The Namespace URI. + * @param localName The local name. + * @param qName The prefixed name, if available, or the empty string. + * @param isElement true if this is an element name, false if it is an attribute name. + * @exception org.xml.sax.SAXException This method will throw an IOException wrapped in a + * SAXException if there is an error writing the name. */ - private void writeName(String uri, String localName, String qName, - boolean isElement) throws SAXException { + private void writeName(String uri, String localName, String qName, boolean isElement) + throws SAXException { final String prefix = doPrefix(uri, qName, isElement); if ((prefix != null) && !prefix.isEmpty()) { @@ -1480,15 +1217,12 @@ private void writeName(String uri, String localName, String qName, /** * Write out the list of Namespace declarations. - * - * @exception org.xml.sax.SAXException - * This method will throw an IOException wrapped in a - * SAXException if there is an error writing the Namespace - * declarations. + * + * @exception org.xml.sax.SAXException This method will throw an IOException wrapped in a + * SAXException if there is an error writing the Namespace declarations. */ private void writeNSDecls() throws SAXException { - final Enumeration prefixes = this.nsSupport - .getDeclaredPrefixes(); + final Enumeration prefixes = this.nsSupport.getDeclaredPrefixes(); while (prefixes.hasMoreElements()) { final String prefix = prefixes.nextElement(); String uri = this.nsSupport.getURI(prefix); @@ -1508,5 +1242,4 @@ private void writeNSDecls() throws SAXException { write('\"'); } } - } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java index 1f8d6e1522..928c2f5d06 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java @@ -1,16 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml.internal; import java.util.HashMap; - import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.EntityResolver; @@ -21,7 +19,7 @@ /** * Abstract SAX XML Reader. - * + * * @author Warren Janssens */ public abstract class AbstractXmlReader implements XMLReader { @@ -44,9 +42,7 @@ public abstract class AbstractXmlReader implements XMLReader { /** The properties map. */ private final HashMap properties; - /** - * Default constructor. - */ + /** Default constructor. */ public AbstractXmlReader() { this.features = new HashMap(); this.properties = new HashMap(); @@ -58,7 +54,7 @@ public AbstractXmlReader() { /** * Return the content handler. - * + * * @return The content handler. * @see XMLReader#getContentHandler() */ @@ -68,7 +64,7 @@ public ContentHandler getContentHandler() { /** * Return the DTD handler. - * + * * @return The DTD handler. * @see XMLReader#getDTDHandler() */ @@ -78,7 +74,7 @@ public DTDHandler getDTDHandler() { /** * Return the entity resolver. - * + * * @return The entity resolver. * @see XMLReader#getEntityResolver() */ @@ -88,7 +84,7 @@ public EntityResolver getEntityResolver() { /** * Return the error handler. - * + * * @return The error handler. * @see XMLReader#getErrorHandler() */ @@ -98,36 +94,33 @@ public ErrorHandler getErrorHandler() { /** * Returns the feature by name. - * - * @param name - * The feature name. + * + * @param name The feature name. * @return The feature. * @see XMLReader#getFeature(String) */ - public boolean getFeature(String name) throws SAXNotRecognizedException, - SAXNotSupportedException { + public boolean getFeature(String name) + throws SAXNotRecognizedException, SAXNotSupportedException { final Boolean result = features.get(name); return result == null ? false : result.booleanValue(); } /** * Returns the property by name. - * - * @param name - * The property name. + * + * @param name The property name. * @return The property. * @see XMLReader#getProperty(String) */ - public Object getProperty(String name) throws SAXNotRecognizedException, - SAXNotSupportedException { + public Object getProperty(String name) + throws SAXNotRecognizedException, SAXNotSupportedException { return properties.get(name); } /** * Sets the content handler. - * - * @param contentHandler - * The content handler. + * + * @param contentHandler The content handler. * @see XMLReader#setContentHandler(ContentHandler) */ public void setContentHandler(ContentHandler contentHandler) { @@ -136,9 +129,8 @@ public void setContentHandler(ContentHandler contentHandler) { /** * Sets the DTD handler. - * - * @param handler - * The DTD handler. + * + * @param handler The DTD handler. * @see XMLReader#setDTDHandler(DTDHandler) */ public void setDTDHandler(DTDHandler handler) { @@ -147,9 +139,8 @@ public void setDTDHandler(DTDHandler handler) { /** * Sets the entity resolver. - * - * @param entityResolver - * The entity resolver. + * + * @param entityResolver The entity resolver. * @see XMLReader#setEntityResolver(EntityResolver) */ public void setEntityResolver(EntityResolver entityResolver) { @@ -158,9 +149,8 @@ public void setEntityResolver(EntityResolver entityResolver) { /** * Sets the error handler. - * - * @param errorHandler - * The error handler. + * + * @param errorHandler The error handler. * @see XMLReader#setErrorHandler(ErrorHandler) */ public void setErrorHandler(ErrorHandler errorHandler) { @@ -169,11 +159,9 @@ public void setErrorHandler(ErrorHandler errorHandler) { /** * Sets a feature. - * - * @param name - * The feature name. - * @param value - * The feature value. + * + * @param name The feature name. + * @param value The feature value. * @see XMLReader#setFeature(String, boolean) */ public void setFeature(String name, boolean value) @@ -183,16 +171,13 @@ public void setFeature(String name, boolean value) /** * Sets a property. - * - * @param name - * The property name. - * @param value - * The property value. + * + * @param name The property name. + * @param value The property value. * @see XMLReader#setProperty(String, Object) */ public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { this.properties.put(name, value); } - } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java index 749d6341f6..c74217ce5a 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java @@ -1,22 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml.internal; import java.io.IOException; import java.util.logging.Level; - import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; import javax.xml.transform.stream.StreamSource; - import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -25,7 +22,7 @@ /** * URI resolver based on a Restlet Context instance. - * + * * @author Jerome Louvel */ public class ContextResolver implements URIResolver { @@ -34,9 +31,8 @@ public class ContextResolver implements URIResolver { /** * Constructor. - * - * @param context - * The Restlet context. + * + * @param context The Restlet context. */ public ContextResolver(Context context) { this.context = context; @@ -44,9 +40,8 @@ public ContextResolver(Context context) { /** * Resolves a target reference into a Source document. - * - * @see javax.xml.transform.URIResolver#resolve(java.lang.String, - * java.lang.String) + * + * @see javax.xml.transform.URIResolver#resolve(java.lang.String, java.lang.String) */ public Source resolve(String href, String base) throws TransformerException { Source result = null; @@ -64,18 +59,18 @@ public Source resolve(String href, String base) throws TransformerException { } String targetUri = targetRef.getTargetRef().toString(); - Response response = this.context.getClientDispatcher().handle( - new Request(Method.GET, targetUri)); + Response response = + this.context.getClientDispatcher().handle(new Request(Method.GET, targetUri)); - if (response.getStatus().isSuccess() - && response.isEntityAvailable()) { + if (response.getStatus().isSuccess() && response.isEntityAvailable()) { try { result = new StreamSource(response.getEntity().getStream()); result.setSystemId(targetUri); } catch (IOException e) { - this.context.getLogger().log(Level.WARNING, - "I/O error while getting the response stream", e); + this.context + .getLogger() + .log(Level.WARNING, "I/O error while getting the response stream", e); } } } diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java index d59e1feade..203f577cdb 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java @@ -1,36 +1,48 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; -import org.junit.jupiter.api.Test; -import org.restlet.*; -import org.restlet.data.*; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; import javax.xml.transform.stream.StreamSource; -import java.io.*; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.restlet.Application; +import org.restlet.Component; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.data.LocalReference; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Protocol; +import org.restlet.data.Reference; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; /** * ResolvingTransformerTestCase tests the resolving aspects of the - * Transformer/TransformerRepresentation to guarantee proper functioning of the - * xsl :import, :include and document() features. - * + * Transformer/TransformerRepresentation to guarantee proper functioning of the xsl :import, + * :include and document() features. + * * @author Marc Portier */ public class ResolvingTransformerTestCase { @@ -46,9 +58,7 @@ static class AssertResolvingHelper { this.resolver = resolver; } - /** - * Asserts that the testUri resolves into the expectedUri - */ + /** Asserts that the testUri resolves into the expectedUri */ void assertResolving(String message, String testUri, String testData) throws TransformerException, IOException { StringBuilder data = new StringBuilder(); @@ -75,7 +85,8 @@ void assertResolving(String message, String testUri, String testData) dataReader.close(); } else { // TODO support other source implementations (namely sax-source implementations) - fail("test implementation currently doesn't handle other source (e.g. sax) implementations"); + fail( + "test implementation currently doesn't handle other source (e.g., sax) implementations"); } assertEquals(testData, data.toString(), message); } @@ -99,8 +110,7 @@ public Restlet createInboundRoot() { @Override public void handle(Request request, Response response) { String remainder = request.getResourceRef().getRemainingPart(); - Representation answer = SimpleUriMapApplication.this.uriMap - .get(remainder); + Representation answer = SimpleUriMapApplication.this.uriMap.get(remainder); if (answer != null) { response.setEntity(answer); @@ -110,15 +120,14 @@ public void handle(Request request, Response response) { } } - private final static String MY_BASEPATH; + private static final String MY_BASEPATH; - private final static String MY_NAME; + private static final String MY_NAME; - private final static String MY_PATH; + private static final String MY_PATH; static { - MY_PATH = ResolvingTransformerTestCase.class.getName() - .replace('.', '/'); + MY_PATH = ResolvingTransformerTestCase.class.getName().replace('.', '/'); final int lastPos = MY_PATH.lastIndexOf('/'); MY_NAME = MY_PATH.substring(lastPos); MY_BASEPATH = MY_PATH.substring(0, lastPos); @@ -130,18 +139,19 @@ public void testResolving() throws Exception { Component comp = new Component(); // create an xml input representation - Representation xml = new StringRepresentation( - "", MediaType.TEXT_XML); + Representation xml = + new StringRepresentation("", MediaType.TEXT_XML); // create a xsl template representation - Representation xslt = new StringRepresentation( - "" - + "" - + "", - MediaType.TEXT_XML); + Representation xslt = + new StringRepresentation( + "" + + "" + + "", + MediaType.TEXT_XML); - TransformRepresentation transRep = new TransformRepresentation( - comp.getContext(), xml, xslt); + TransformRepresentation transRep = + new TransformRepresentation(comp.getContext(), xml, xslt); // create a test-stream representation to be returned when the correct // code is presented @@ -205,30 +215,39 @@ public void testTransform() throws Exception { String xsl2xmlLink = "./3rd.xml"; // and "/three/3rd.xml" would // too... - Representation xml3 = new StringRepresentation("" + thirdDocData, MediaType.TEXT_XML); - Representation xslt3 = new StringRepresentation( - "" - + "" - + " " - + " " - + " " - + " " + "", - MediaType.TEXT_XML); + Representation xml3 = + new StringRepresentation( + "" + thirdDocData, MediaType.TEXT_XML); + Representation xslt3 = + new StringRepresentation( + "" + + "" + + " " + + " " + + " " + + " " + + "", + MediaType.TEXT_XML); SimpleUriMapApplication thirdLevel = new SimpleUriMapApplication(); thirdLevel.add("3rd.xsl", xslt3); thirdLevel.add("3rd.xml", xml3); comp.getInternalRouter().attach("/three/", thirdLevel); // xml In - Representation xmlIn = new StringRepresentation( - "drie"); + Representation xmlIn = + new StringRepresentation( + "drie"); // xslOne - Reference xsltOneRef = new LocalReference("clap://thread/" + MY_BASEPATH + "/xslt/one/1st.xsl"); - Representation xsltOne = comp.getContext().getClientDispatcher() - .handle(new Request(Method.GET, xsltOneRef)).getEntity(); - TransformRepresentation tr = new TransformRepresentation( - comp.getContext(), xmlIn, xsltOne); + Reference xsltOneRef = + new LocalReference("clap://thread/" + MY_BASEPATH + "/xslt/one/1st.xsl"); + Representation xsltOne = + comp.getContext() + .getClientDispatcher() + .handle(new Request(Method.GET, xsltOneRef)) + .getEntity(); + TransformRepresentation tr = new TransformRepresentation(comp.getContext(), xmlIn, xsltOne); // TODO transformer output should go to SAX! The sax-event-stream should // then be fed into a DOMBuilder @@ -239,9 +258,10 @@ public void testTransform() throws Exception { tr.write(out); String xmlOut = out.toString(); - String expectedResult = "1st2nd" - + thirdDocData + ""; - assertEquals(expectedResult, - xmlOut, "xslt result doesn't match expectations"); + String expectedResult = + "1st2nd" + + thirdDocData + + ""; + assertEquals(expectedResult, xmlOut, "xslt result doesn't match expectations"); } } diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java index 95c0883610..c0cc337ffc 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java @@ -1,21 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test case for the {@link TransformRepresentation} class. * @@ -28,46 +27,51 @@ public class TransformRepresentationTestCase { final String output2 = "cust123"; // Create a source XML document - final Representation source = new StringRepresentation( - "" + "" - + "" + "" - + "23.45" + "" + "", - MediaType.TEXT_XML); + final Representation source = + new StringRepresentation( + "" + + "" + + "" + + "" + + "23.45" + + "" + + "", + MediaType.TEXT_XML); // Create a first transform XSLT sheet - final Representation xslt1 = new StringRepresentation( - "" - + "" - + "" - + "" - + "" - + "", MediaType.TEXT_XML); + final Representation xslt1 = + new StringRepresentation( + "" + + "" + + "" + + "" + + "" + + "", + MediaType.TEXT_XML); // Create a second transform XSLT sheet - final Representation xslt2 = new StringRepresentation( - "" - + "" - + "" - + "" - + "" + "", - MediaType.TEXT_XML); + final Representation xslt2 = + new StringRepresentation( + "" + + "" + + "" + + "" + + "" + + "", + MediaType.TEXT_XML); @Test public void testSingleTransform() throws Exception { - TransformRepresentation tr1 = new TransformRepresentation(this.source, - this.xslt1); + TransformRepresentation tr1 = new TransformRepresentation(this.source, this.xslt1); final String result = tr1.getText(); assertEquals(this.output1, result); } @Test public void testDoubleTransform() throws Exception { - TransformRepresentation tr1 = new TransformRepresentation(this.source, - this.xslt1); - TransformRepresentation tr2 = new TransformRepresentation(tr1, - this.xslt2); + TransformRepresentation tr1 = new TransformRepresentation(this.source, this.xslt1); + TransformRepresentation tr2 = new TransformRepresentation(tr1, this.xslt2); final String result = tr2.getText(); assertEquals(this.output2, result); } - } diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java index 5ee6fa6157..499d08fe7c 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java @@ -1,29 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.xml; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; import org.junit.jupiter.api.Test; import org.restlet.Component; import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - /** * Test case for the Transformer class. - * + * * @author Jerome Louvel */ public class TransformerTestCase { @@ -50,45 +48,53 @@ void trackFailure(String message, int index, Throwable e) { final String output = "cust12323.45"; // Create a source XML document - final Representation source = new StringRepresentation( - "" + "" - + "" + "" - + "23.45" + "" + "", - MediaType.TEXT_XML); + final Representation source = + new StringRepresentation( + "" + + "" + + "" + + "" + + "23.45" + + "" + + "", + MediaType.TEXT_XML); // Create a transform XSLT sheet - final Representation xslt = new StringRepresentation( - "" - + "" - + "" - + "" - + "" + "", - MediaType.TEXT_XML); + final Representation xslt = + new StringRepresentation( + "" + + "" + + "" + + "" + + "" + + "", + MediaType.TEXT_XML); @Test public void parallelTestTransform() { Component comp = new Component(); - final TransformRepresentation tr = new TransformRepresentation( - comp.getContext(), this.source, this.xslt); + final TransformRepresentation tr = + new TransformRepresentation(comp.getContext(), this.source, this.xslt); final FailureTracker tracker = new FailureTracker(); final int testVolume = 500; final Thread[] parallelTransform = new Thread[testVolume]; for (int i = 0; i < parallelTransform.length; i++) { final int index = i; - parallelTransform[i] = new Thread() { - - @Override - public void run() { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - tr.write(out); - final String result = out.toString(); - assertEquals(TransformerTestCase.this.output, result); - } catch (IOException e) { - tracker.trackFailure("Exception during write in thread ", index, e); - } - } - }; + parallelTransform[i] = + new Thread() { + + @Override + public void run() { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + tr.write(out); + final String result = out.toString(); + assertEquals(TransformerTestCase.this.output, result); + } catch (IOException e) { + tracker.trackFailure("Exception during write in thread ", index, e); + } + } + }; } for (final Thread pt : parallelTransform) { @@ -105,5 +111,4 @@ public void testTransform() throws Exception { assertEquals(this.output, result); } - } diff --git a/org.restlet/src/main/java/org/restlet/Application.java b/org.restlet/src/main/java/org/restlet/Application.java index 29715c4108..f47e2e59a2 100644 --- a/org.restlet/src/main/java/org/restlet/Application.java +++ b/org.restlet/src/main/java/org/restlet/Application.java @@ -1,14 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Filter; +import java.util.logging.Level; import org.restlet.engine.Engine; import org.restlet.engine.application.ApplicationHelper; import org.restlet.engine.resource.AnnotationUtils; @@ -16,573 +19,569 @@ import org.restlet.routing.Router; import org.restlet.routing.VirtualHost; import org.restlet.security.Role; -import org.restlet.service.*; +import org.restlet.service.ConnectorService; +import org.restlet.service.ConnegService; +import org.restlet.service.ConverterService; +import org.restlet.service.DecoderService; +import org.restlet.service.EncoderService; +import org.restlet.service.MetadataService; +import org.restlet.service.RangeService; +import org.restlet.service.StatusService; +import org.restlet.service.TunnelService; import org.restlet.util.ServiceList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Filter; -import java.util.logging.Level; - /** - * Restlet managing a coherent set of resources and services. Applications are - * guaranteed to receive calls with their base reference set relatively to the - * {@link VirtualHost} that served them. This class is both a descriptor able to - * create the root Restlet and the actual Restlet that can be attached to one or - * more VirtualHost instances.
+ * Restlet managing a coherent set of resources and services. Applications are guaranteed to receive + * calls with their base reference set relatively to the {@link VirtualHost} that served them. This + * class is both a descriptor able to create the root Restlet and the actual Restlet that can be + * attached to one or more VirtualHost instances.
*
- * Applications also have many useful services associated. Most are enabled by - * default and are available as properties that can be eventually overridden: + * Applications also have many useful services associated. Most are enabled by default and are + * available as properties that can be eventually overridden: + * *

    - *
  • "connectorService" to declare necessary client and server - * connectors.
  • - *
  • "converterService" to convert between regular objects and - * representations.
  • - *
  • "decoderService" to automatically decode or uncompress received entities. - *
  • - *
  • "encoderService" to automatically encode or compress sent entities - * (disabled by default).
  • - *
  • "metadataService" to provide access to metadata and their associated - * extension names.
  • - *
  • "rangeService" to automatically exposes ranges of response entities.
  • - *
  • "statusService" to provide common representations for exception - * status.
  • - *
  • "taskService" to run tasks asynchronously (disabled by default).
  • - *
  • "tunnelService" to tunnel method names or client preferences via query - * parameters.
  • + *
  • "connectorService" to declare necessary client and server connectors. + *
  • "converterService" to convert between regular objects and representations. + *
  • "decoderService" to automatically decode or uncompress received entities. + *
  • "encoderService" to automatically encode or compress sent entities (disabled by default). + *
  • "metadataService" to provide access to metadata and their associated extension names. + *
  • "rangeService" to automatically exposes ranges of response entities. + *
  • "statusService" to provide common representations for exception status. + *
  • "taskService" to run tasks asynchronously (disabled by default). + *
  • "tunnelService" to tunnel method names or client preferences via query parameters. *
- * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class Application extends Restlet { - private static final ThreadLocal CURRENT = new ThreadLocal<>(); - - /** - * This variable is stored internally as a thread local variable and updated - * each time a call enters an application. - * - * Warning: this method should only be used under duress. You should by default - * prefer getting the current application using methods such as - * {@link org.restlet.resource.Resource#getApplication()} - * - * @return The current context. - */ - public static Application getCurrent() { - return CURRENT.get(); - } - - /** - * Sets the context associated with the current thread. - * - * @param application The thread's context. - */ - public static void setCurrent(Application application) { - CURRENT.set(application); - } - - /** Indicates if the debugging mode is enabled. */ - private volatile boolean debugging; - - /** The helper provided by the implementation. */ - private volatile ApplicationHelper helper; - - /** The inbound root Restlet. */ - private volatile Restlet inboundRoot; - - /** The outbound root Restlet. */ - private volatile Restlet outboundRoot; - - /** The modifiable list of roles. */ - private final List roles; - - /** The list of services. */ - private final ServiceList services; - - /** - * Constructor. Note this constructor is convenient because you don't have to - * provide a context like for {@link #Application(Context)}. Therefore, the - * context will initially be null. It's only when you attach the application to - * a virtual host via one of its attach*() methods that a proper context will be - * set. - */ - public Application() { - this(null); - } - - /** - * Constructor. - * - * @param context The context to use based on parent component context. This - * context should be created using the - * {@link Context#createChildContext()} method to ensure a proper - * isolation with the other applications. - */ - public Application(Context context) { - super(context); - - if (Engine.getInstance() != null) { - this.helper = new ApplicationHelper(this); - this.helper.setContext(context); - } - - ConnegService connegService = new ConnegService(); - ConverterService converterService = new ConverterService(); - MetadataService metadataService = new MetadataService(); - - this.debugging = false; - this.outboundRoot = null; - this.inboundRoot = null; - this.roles = new CopyOnWriteArrayList(); - this.services = new ServiceList(context); - this.services.add(new TunnelService(true, true)); - this.services.add(new StatusService(true, converterService, metadataService, connegService)); - this.services.add(new DecoderService()); - this.services.add(new EncoderService(false)); - this.services.add(new RangeService()); - this.services.add(new ConnectorService()); - this.services.add(connegService); - this.services.add(converterService); - this.services.add(metadataService); - - this.services.add(new org.restlet.service.TaskService(false)); - } - - /** - * Creates a inbound root Restlet that will receive all incoming calls. In - * general, instances of Router, Filter or Finder classes will be used as - * initial application Restlet. The default implementation returns null by - * default. This method is intended to be overridden by subclasses. - * - * @return The inbound root Restlet. - */ - public Restlet createInboundRoot() { - return null; - } - - /** - * Creates a outbound root Restlet that will receive all outgoing calls from - * ClientResource. In general, instances of {@link Router} and {@link Filter} - * classes will be used. The default implementation returns a Restlet giving - * access to the the outbound service layer and finally to the - * {@link Context#getClientDispatcher()}. - *

- * This method is intended to be overridden by subclasses but in order to - * benefit from the outbound service filtering layer, the original outbound root - * must be careful attached again at the end of the user filtering layer. - * - * @return The outbound root Restlet. - */ - public Restlet createOutboundRoot() { - return getHelper().getFirstOutboundFilter(); - } - - /** - * Returns the connector service. The service is enabled by default. - * - * @return The connector service. - */ - public ConnectorService getConnectorService() { - return getServices().get(ConnectorService.class); - } - - /** - * Returns the content negotiation service. The service is enabled by default. - * - * @return The content negotiation service. - */ - public ConnegService getConnegService() { - return getServices().get(ConnegService.class); - } - - /** - * Returns the converter service. The service is enabled by default. - * - * @return The converter service. - */ - public ConverterService getConverterService() { - return getServices().get(ConverterService.class); - } - - /** - * Returns the decoder service. The service is enabled by default. - * - * @return The decoder service. - */ - public DecoderService getDecoderService() { - return getServices().get(DecoderService.class); - } - - /** - * Returns the encoder service. The service is disabled by default. - * - * @return The encoder service. - */ - public EncoderService getEncoderService() { - return getServices().get(EncoderService.class); - } - - /** - * Returns the helper provided by the implementation. - * - * @return The helper provided by the implementation. - */ - private ApplicationHelper getHelper() { - return this.helper; - } - - /** - * Returns the inbound root Restlet. - * - * @return The inbound root Restlet. - */ - public Restlet getInboundRoot() { - if (this.inboundRoot == null) { - synchronized (this) { - if (this.inboundRoot == null) { - this.inboundRoot = createInboundRoot(); - } - } - } - - return this.inboundRoot; - } - - /** - * Returns the metadata service. The service is enabled by default. - * - * @return The metadata service. - */ - public MetadataService getMetadataService() { - return getServices().get(MetadataService.class); - } - - /** - * Returns the outbound root Restlet. - * - * @return The outbound root Restlet. - */ - public Restlet getOutboundRoot() { - if (this.outboundRoot == null) { - synchronized (this) { - if (this.outboundRoot == null) { - this.outboundRoot = createOutboundRoot(); - } - } - } - - return this.outboundRoot; - } - - /** - * Returns the range service. - * - * @return The range service. - */ - public RangeService getRangeService() { - return getServices().get(RangeService.class); - } - - /** - * Returns the role associated to the given name. - * - * @param name The name of the role to find. - * @return The role matched or null. - */ - public Role getRole(String name) { - for (Role role : getRoles()) { - if (role.getName().equals(name)) { - return role; - } - } - - return null; - } - - /** - * Returns the modifiable list of roles. - * - * @return The modifiable list of roles. - */ - public List getRoles() { - return roles; - } - - /** - * Returns the modifiable list of services. - * - * @return The modifiable list of services. - */ - public ServiceList getServices() { - return services; - } - - /** - * Returns the status service. The service is enabled by default. - * - * @return The status service. - */ - public StatusService getStatusService() { - return getServices().get(StatusService.class); - } - - /** - * Returns the tunnel service. The service is enabled by default. - * - * @return The tunnel service. - */ - public TunnelService getTunnelService() { - return getServices().get(TunnelService.class); - } - - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - - if (getHelper() != null) { - getHelper().handle(request, response); - } - } - - /** - * Indicates if the debugging mode is enabled. True by default. - * - * @return True if the debugging mode is enabled. - */ - public boolean isDebugging() { - return debugging; - } - - /** - * Sets the connector service. - * - * @param connectorService The connector service. - */ - public void setConnectorService(ConnectorService connectorService) { - getServices().set(connectorService); - } - - /** - * Sets the content negotiation service. - * - * @param connegService The content negotiation service. - */ - public void setConnegService(ConnegService connegService) { - getServices().set(connegService); - } - - @Override - public void setContext(Context context) { - super.setContext(context); - getHelper().setContext(context); - getServices().setContext(context); - } - - /** - * Sets the converter service. - * - * @param converterService The converter service. - */ - public void setConverterService(ConverterService converterService) { - getServices().set(converterService); - } - - /** - * Indicates if the debugging mode is enabled. - * - * @param debugging True if the debugging mode is enabled. - */ - public void setDebugging(boolean debugging) { - this.debugging = debugging; - } - - /** - * Sets the decoder service. - * - * @param decoderService The decoder service. - */ - public void setDecoderService(DecoderService decoderService) { - getServices().set(decoderService); - } - - /** - * Sets the encoder service. - * - * @param encoderService The encoder service. - */ - public void setEncoderService(EncoderService encoderService) { - getServices().set(encoderService); - } - - /** - * Sets the inbound root Resource class. - * - * @param inboundRootClass The inbound root Resource class. - */ - public synchronized void setInboundRoot(Class inboundRootClass) { - setInboundRoot(createFinder(inboundRootClass)); - } - - /** - * Sets the inbound root Restlet. - * - * @param inboundRoot The inbound root Restlet. - */ - public synchronized void setInboundRoot(Restlet inboundRoot) { - this.inboundRoot = inboundRoot; - - if ((inboundRoot != null) && (inboundRoot.getContext() == null)) { - inboundRoot.setContext(getContext()); - } - } - - /** - * Sets the metadata service. - * - * @param metadataService The metadata service. - */ - public void setMetadataService(MetadataService metadataService) { - getServices().set(metadataService); - } - - /** - * Sets the outbound root Resource class. - * - * @param outboundRootClass The client root {@link ServerResource} subclass. - */ - public synchronized void setOutboundRoot(Class outboundRootClass) { - setOutboundRoot(createFinder(outboundRootClass)); - } - - /** - * Sets the outbound root Restlet. - * - * @param outboundRoot The outbound root Restlet. - */ - public synchronized void setOutboundRoot(Restlet outboundRoot) { - this.outboundRoot = outboundRoot; - - if ((outboundRoot != null) && (outboundRoot.getContext() == null)) { - outboundRoot.setContext(getContext()); - } - } - - /** - * Sets the range service. - * - * @param rangeService The range service. - */ - public void setRangeService(RangeService rangeService) { - getServices().set(rangeService); - } - - /** - * Sets the modifiable list of roles. This method clears the current list and - * adds all entries in the parameter list. - * - * @param roles A list of roles. - */ - public void setRoles(List roles) { - synchronized (getRoles()) { - if (roles != getRoles()) { - getRoles().clear(); - - if (roles != null) { - getRoles().addAll(roles); - } - } - } - } - - /** - * Sets the status service. - * - * @param statusService The status service. - */ - public void setStatusService(StatusService statusService) { - getServices().set(statusService); - } - - /** - * Sets the task service. - * - * @param taskService The task service. - */ - public void setTaskService(org.restlet.service.TaskService taskService) { - getServices().set(taskService); - } - - /** - * Sets the tunnel service. - * - * @param tunnelService The tunnel service. - */ - public void setTunnelService(TunnelService tunnelService) { - getServices().set(tunnelService); - } - - /** - * Starts the application, all the enabled associated services then the inbound - * and outbound roots. - */ - @Override - public synchronized void start() throws Exception { - if (isStopped()) { - if (isDebugging()) { - getLogger().log(Level.INFO, "Starting " + getClass().getName() + " application in debug mode"); - } else { - getLogger().log(Level.INFO, "Starting " + getClass().getName() + " application"); - } - - if (getHelper() != null) { - getHelper().start(); - } - - getServices().start(); - - if (getInboundRoot() != null) { - getInboundRoot().start(); - } - - if (getOutboundRoot() != null) { - getOutboundRoot().start(); - } - - // Must be invoked as a last step - super.start(); - } - } - - /** - * Stops the application, the inbound and outbound roots then all the enabled - * associated services. Finally, it clears the internal cache of annotations. - */ - @Override - public synchronized void stop() throws Exception { - if (isStarted()) { - // Must be invoked as a first step - super.stop(); - - if (getOutboundRoot() != null) { - getOutboundRoot().stop(); - } - - if (getInboundRoot() != null) { - getInboundRoot().stop(); - } - - getServices().stop(); - - if (getHelper() != null) { - getHelper().stop(); - } - - // Clear the annotations cache - AnnotationUtils.getInstance().clearCache(); - } - } - + private static final ThreadLocal CURRENT = new ThreadLocal<>(); + + /** + * This variable is stored internally as a thread local variable and updated each time a call + * enters an application. + * + *

Warning: this method should only be used under duress. You should by default prefer + * getting the current application using methods such as {@link + * org.restlet.resource.Resource#getApplication()} + * + * @return The current context. + */ + public static Application getCurrent() { + return CURRENT.get(); + } + + /** + * Sets the context associated with the current thread. + * + * @param application The thread's context. + */ + public static void setCurrent(Application application) { + CURRENT.set(application); + } + + /** Indicates if the debugging mode is enabled. */ + private volatile boolean debugging; + + /** The helper provided by the implementation. */ + private volatile ApplicationHelper helper; + + /** The inbound root Restlet. */ + private volatile Restlet inboundRoot; + + /** The outbound root Restlet. */ + private volatile Restlet outboundRoot; + + /** The modifiable list of roles. */ + private final List roles; + + /** The list of services. */ + private final ServiceList services; + + /** + * Constructor. Note this constructor is convenient because you don't have to provide a context + * like for {@link #Application(Context)}. Therefore, the context will initially be null. It's + * only when you attach the application to a virtual host via one of its attach*() methods that + * a proper context will be set. + */ + public Application() { + this(null); + } + + /** + * Constructor. + * + * @param context The context to use based on parent component context. This context should be + * created using the {@link Context#createChildContext()} method to ensure proper + * isolation with the other applications. + */ + public Application(Context context) { + super(context); + + if (Engine.getInstance() != null) { + this.helper = new ApplicationHelper(this); + this.helper.setContext(context); + } + + ConnegService connegService = new ConnegService(); + ConverterService converterService = new ConverterService(); + MetadataService metadataService = new MetadataService(); + + this.debugging = false; + this.outboundRoot = null; + this.inboundRoot = null; + this.roles = new CopyOnWriteArrayList<>(); + this.services = new ServiceList(context); + this.services.add(new TunnelService(true, true)); + this.services.add( + new StatusService(true, converterService, metadataService, connegService)); + this.services.add(new DecoderService()); + this.services.add(new EncoderService(false)); + this.services.add(new RangeService()); + this.services.add(new ConnectorService()); + this.services.add(connegService); + this.services.add(converterService); + this.services.add(metadataService); + + this.services.add(new org.restlet.service.TaskService(false)); + } + + /** + * Creates an inbound root Restlet that will receive all incoming calls. In general, instances of + * Router, Filter, or Finder classes will be used as initial application Restlet. The default + * implementation returns null by default. This method is intended to be overridden by + * subclasses. + * + * @return The inbound root Restlet. + */ + public Restlet createInboundRoot() { + return null; + } + + /** + * Creates an outbound root Restlet that will receive all outgoing calls from ClientResource. In + * general, instances of {@link Router} and {@link Filter} classes will be used. The default + * implementation returns a Restlet giving access to the outbound service layer and finally + * to the {@link Context#getClientDispatcher()}. + * + *

This method is intended to be overridden by subclasses, but to benefit from the outbound + * service filtering layer, the original outbound root must be carefully attached again at the end + * of the user filtering layer. + * + * @return The outbound root Restlet. + */ + public Restlet createOutboundRoot() { + return getHelper().getFirstOutboundFilter(); + } + + /** + * Returns the connector service. The service is enabled by default. + * + * @return The connector service. + */ + public ConnectorService getConnectorService() { + return getServices().get(ConnectorService.class); + } + + /** + * Returns the content negotiation service. The service is enabled by default. + * + * @return The content negotiation service. + */ + public ConnegService getConnegService() { + return getServices().get(ConnegService.class); + } + + /** + * Returns the converter service. The service is enabled by default. + * + * @return The converter service. + */ + public ConverterService getConverterService() { + return getServices().get(ConverterService.class); + } + + /** + * Returns the decoder service. The service is enabled by default. + * + * @return The decoder service. + */ + public DecoderService getDecoderService() { + return getServices().get(DecoderService.class); + } + + /** + * Returns the encoder service. The service is disabled by default. + * + * @return The encoder service. + */ + public EncoderService getEncoderService() { + return getServices().get(EncoderService.class); + } + + /** + * Returns the helper provided by the implementation. + * + * @return The helper provided by the implementation. + */ + private ApplicationHelper getHelper() { + return this.helper; + } + + /** + * Returns the inbound root Restlet. + * + * @return The inbound root Restlet. + */ + public Restlet getInboundRoot() { + if (this.inboundRoot == null) { + synchronized (this) { + if (this.inboundRoot == null) { + this.inboundRoot = createInboundRoot(); + } + } + } + + return this.inboundRoot; + } + + /** + * Returns the metadata service. The service is enabled by default. + * + * @return The metadata service. + */ + public MetadataService getMetadataService() { + return getServices().get(MetadataService.class); + } + + /** + * Returns the outbound root Restlet. + * + * @return The outbound root Restlet. + */ + public Restlet getOutboundRoot() { + if (this.outboundRoot == null) { + synchronized (this) { + if (this.outboundRoot == null) { + this.outboundRoot = createOutboundRoot(); + } + } + } + + return this.outboundRoot; + } + + /** + * Returns the range service. + * + * @return The range service. + */ + public RangeService getRangeService() { + return getServices().get(RangeService.class); + } + + /** + * Returns the role associated with the given name. + * + * @param name The name of the role to find. + * @return The role matched or null. + */ + public Role getRole(String name) { + for (Role role : getRoles()) { + if (role.getName().equals(name)) { + return role; + } + } + + return null; + } + + /** + * Returns the modifiable list of roles. + * + * @return The modifiable list of roles. + */ + public List getRoles() { + return roles; + } + + /** + * Returns the modifiable list of services. + * + * @return The modifiable list of services. + */ + public ServiceList getServices() { + return services; + } + + /** + * Returns the status service. The service is enabled by default. + * + * @return The status service. + */ + public StatusService getStatusService() { + return getServices().get(StatusService.class); + } + + /** + * Returns the tunnel service. The service is enabled by default. + * + * @return The tunnel service. + */ + public TunnelService getTunnelService() { + return getServices().get(TunnelService.class); + } + + @Override + public void handle(Request request, Response response) { + super.handle(request, response); + + if (getHelper() != null) { + getHelper().handle(request, response); + } + } + + /** + * Indicates if the debugging mode is enabled. True by default. + * + * @return True if the debugging mode is enabled. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Sets the connector service. + * + * @param connectorService The connector service. + */ + public void setConnectorService(ConnectorService connectorService) { + getServices().set(connectorService); + } + + /** + * Sets the content negotiation service. + * + * @param connegService The content negotiation service. + */ + public void setConnegService(ConnegService connegService) { + getServices().set(connegService); + } + + @Override + public void setContext(Context context) { + super.setContext(context); + getHelper().setContext(context); + getServices().setContext(context); + } + + /** + * Sets the converter service. + * + * @param converterService The converter service. + */ + public void setConverterService(ConverterService converterService) { + getServices().set(converterService); + } + + /** + * Indicates if the debugging mode is enabled. + * + * @param debugging True if the debugging mode is enabled. + */ + public void setDebugging(boolean debugging) { + this.debugging = debugging; + } + + /** + * Sets the decoder service. + * + * @param decoderService The decoder service. + */ + public void setDecoderService(DecoderService decoderService) { + getServices().set(decoderService); + } + + /** + * Sets the encoder service. + * + * @param encoderService The encoder service. + */ + public void setEncoderService(EncoderService encoderService) { + getServices().set(encoderService); + } + + /** + * Sets the inbound root Resource class. + * + * @param inboundRootClass The inbound root Resource class. + */ + public synchronized void setInboundRoot(Class inboundRootClass) { + setInboundRoot(createFinder(inboundRootClass)); + } + + /** + * Sets the inbound root Restlet. + * + * @param inboundRoot The inbound root Restlet. + */ + public synchronized void setInboundRoot(Restlet inboundRoot) { + this.inboundRoot = inboundRoot; + + if ((inboundRoot != null) && (inboundRoot.getContext() == null)) { + inboundRoot.setContext(getContext()); + } + } + + /** + * Sets the metadata service. + * + * @param metadataService The metadata service. + */ + public void setMetadataService(MetadataService metadataService) { + getServices().set(metadataService); + } + + /** + * Sets the outbound root Resource class. + * + * @param outboundRootClass The client root {@link ServerResource} subclass. + */ + public synchronized void setOutboundRoot(Class outboundRootClass) { + setOutboundRoot(createFinder(outboundRootClass)); + } + + /** + * Sets the outbound root Restlet. + * + * @param outboundRoot The outbound root Restlet. + */ + public synchronized void setOutboundRoot(Restlet outboundRoot) { + this.outboundRoot = outboundRoot; + + if ((outboundRoot != null) && (outboundRoot.getContext() == null)) { + outboundRoot.setContext(getContext()); + } + } + + /** + * Sets the range service. + * + * @param rangeService The range service. + */ + public void setRangeService(RangeService rangeService) { + getServices().set(rangeService); + } + + /** + * Sets the modifiable list of roles. This method clears the current list and adds all entries + * in the parameter list. + * + * @param roles A list of roles. + */ + public void setRoles(List roles) { + synchronized (getRoles()) { + if (roles != getRoles()) { + getRoles().clear(); + + if (roles != null) { + getRoles().addAll(roles); + } + } + } + } + + /** + * Sets the status service. + * + * @param statusService The status service. + */ + public void setStatusService(StatusService statusService) { + getServices().set(statusService); + } + + /** + * Sets the task service. + * + * @param taskService The task service. + */ + public void setTaskService(org.restlet.service.TaskService taskService) { + getServices().set(taskService); + } + + /** + * Sets the tunnel service. + * + * @param tunnelService The tunnel service. + */ + public void setTunnelService(TunnelService tunnelService) { + getServices().set(tunnelService); + } + + /** + * Starts the application, all the enabled associated services then the inbound and outbound + * roots. + */ + @Override + public synchronized void start() throws Exception { + if (isStopped()) { + if (isDebugging()) { + getLogger() + .log( + Level.INFO, + "Starting " + getClass().getName() + " application in debug mode"); + } else { + getLogger().log(Level.INFO, "Starting " + getClass().getName() + " application"); + } + + if (getHelper() != null) { + getHelper().start(); + } + + getServices().start(); + + if (getInboundRoot() != null) { + getInboundRoot().start(); + } + + if (getOutboundRoot() != null) { + getOutboundRoot().start(); + } + + // Must be invoked as a last step + super.start(); + } + } + + /** + * Stops the application, the inbound and outbound roots, then all the enabled associated + * services. Finally, it clears the internal cache of annotations. + */ + @Override + public synchronized void stop() throws Exception { + if (isStarted()) { + // Must be invoked as a first step + super.stop(); + + if (getOutboundRoot() != null) { + getOutboundRoot().stop(); + } + + if (getInboundRoot() != null) { + getInboundRoot().stop(); + } + + getServices().stop(); + + if (getHelper() != null) { + getHelper().stop(); + } + + // Clear the annotations cache + AnnotationUtils.getInstance().clearCache(); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/Client.java b/org.restlet/src/main/java/org/restlet/Client.java index a5ed7819fa..4205ff9710 100644 --- a/org.restlet/src/main/java/org/restlet/Client.java +++ b/org.restlet/src/main/java/org/restlet/Client.java @@ -1,35 +1,32 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; +import java.util.Arrays; +import java.util.List; import org.restlet.data.Protocol; import org.restlet.data.Status; import org.restlet.engine.Engine; import org.restlet.engine.RestletHelper; -import java.util.Arrays; -import java.util.List; - /** - * Connector acting as a generic client. It internally uses one of the available - * connector helpers registered with the Restlet engine.
+ * Connector acting as a generic client. It internally uses one of the available connector helpers + * registered with the Restlet engine.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables.
+ * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables.
*
- * For advanced cases, it is possible to obtained the wrapped - * {@link RestletHelper} instance that is used by this client to handle the - * calls via the "org.restlet.engine.helper" attribute stored in the - * {@link Context} object. - * + * For advanced cases, it is possible to obtain the wrapped {@link RestletHelper} instance that is + * used by this client to handle the calls via the "org.restlet.engine.helper" attribute stored in + * the {@link Context} object. + * * @author Jerome Louvel */ public class Client extends Connector { @@ -39,8 +36,8 @@ public class Client extends Connector { /** * Constructor. - * - * @param context The context. + * + * @param context The context. * @param protocols The connector protocols. */ public Client(Context context, List protocols) { @@ -49,19 +46,17 @@ public Client(Context context, List protocols) { /** * Constructor. - * - * @param context The context. - * @param protocols The connector protocols. + * + * @param context The context. + * @param protocols The connector protocols. * @param helperClass Optional helper class name. */ - public Client(Context context, List protocols, - String helperClass) { + public Client(Context context, List protocols, String helperClass) { super(context, protocols); if ((protocols != null) && !protocols.isEmpty()) { if (Engine.getInstance() != null) { - this.helper = Engine.getInstance().createHelper(this, - helperClass); + this.helper = Engine.getInstance().createHelper(this, helperClass); } else { this.helper = null; } @@ -70,25 +65,23 @@ public Client(Context context, List protocols, } if (context != null && this.helper != null) { - context.getAttributes().put("org.restlet.engine.helper", - this.helper); + context.getAttributes().put("org.restlet.engine.helper", this.helper); } } /** * Constructor. - * - * @param context The context. + * + * @param context The context. * @param protocols The connector protocols. */ public Client(Context context, Protocol... protocols) { - this(context, (protocols == null) ? null : Arrays.asList(protocols), - null); + this(context, (protocols == null) ? null : Arrays.asList(protocols), null); } /** * Constructor. - * + * * @param protocols The connector protocols. */ public Client(List protocols) { @@ -97,7 +90,7 @@ public Client(List protocols) { /** * Constructor. - * + * * @param protocols The connector protocols. */ public Client(Protocol... protocols) { @@ -106,7 +99,7 @@ public Client(Protocol... protocols) { /** * Constructor. - * + * * @param protocolName The connector protocol. */ public Client(String protocolName) { @@ -115,7 +108,7 @@ public Client(String protocolName) { /** * Returns the helper provided by the implementation. - * + * * @return The helper provided by the implementation. */ private RestletHelper getHelper() { @@ -129,16 +122,19 @@ public void handle(Request request, Response response) { if (getHelper() != null) { getHelper().handle(request, response); } else { - String sb = "No available client connector supports the required protocol: " + - "'" + request.getProtocol().getName() + "'." + - " Please add the JAR of a matching connector to your classpath."; + String sb = + "No available client connector supports the required protocol: " + + "'" + + request.getProtocol().getName() + + "'." + + " Please add the JAR of a matching connector to your classpath."; response.setStatus(Status.CONNECTOR_ERROR_INTERNAL, sb); } } /** * Indicates the underlying connector helper is available. - * + * * @return True if the underlying connector helper is available. */ @Override @@ -169,5 +165,4 @@ public synchronized void stop() throws Exception { } } } - } diff --git a/org.restlet/src/main/java/org/restlet/Component.java b/org.restlet/src/main/java/org/restlet/Component.java index b068b83841..6681fa42ce 100644 --- a/org.restlet/src/main/java/org/restlet/Component.java +++ b/org.restlet/src/main/java/org/restlet/Component.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.engine.Engine; import org.restlet.engine.component.ComponentHelper; import org.restlet.engine.component.InternalRouter; @@ -22,567 +23,549 @@ import org.restlet.util.ServerList; import org.restlet.util.ServiceList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - /** - * Restlet managing a set of {@link Connector}s, {@link VirtualHost}s, - * {@link Service}s and {@link Application}s. Applications are expected to be - * directly attached to virtual hosts or to the internal router (see RIAP - * pseudo-protocol for usage). Components also expose several services: access - * logging and status setting.
+ * Restlet managing a set of {@link Connector}s, {@link VirtualHost}s, {@link Service}s and {@link + * Application}s. Applications are expected to be directly attached to virtual hosts or to the + * internal router (see RIAP pseudo-protocol for usage). Components also expose several services: + * access logging and status setting.
*
- * From an architectural point of view, here is the REST definition: "A - * component is an abstract unit of software instructions and internal state - * that provides a transformation of data via its interface." Roy T. - * Fielding
+ * From an architectural point of view, here is the REST definition: "A component is an abstract + * unit of software instructions and internal state that provides a transformation of data via its + * interface." Roy T. Fielding
*
- * Components also have useful services associated. They are all enabled by - * default and are available as properties that can be eventually overridden: + * Components also have useful services associated. They are all enabled by default and are + * available as properties that can be eventually overridden: + * *

    - *
  • "logService" to configure access logging.
  • - *
  • "statusService" to provide common representations for exception - * status.
  • - *
  • "taskService" to run tasks asynchronously.
  • + *
  • "logService" to configure access logging. + *
  • "statusService" to provide common representations for exception status. + *
  • "taskService" to run tasks asynchronously. *
- * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * - * @see Source - * dissertation - * + * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * + * @see Source + * dissertation * @author Jerome Louvel */ public class Component extends Restlet { - /** The modifiable list of client connectors. */ - private final ClientList clients; - - /** The default host. */ - private volatile VirtualHost defaultHost; - - /** The helper provided by the implementation. */ - private volatile ComponentHelper helper; - - /** The modifiable list of virtual hosts. */ - private final List hosts; - - /** - * The private internal router that can be addressed via the RIAP client - * connector. - */ - private volatile Router internalRouter; - - /** The modifiable list of security realms. */ - private final List realms; - - /** The modifiable list of server connectors. */ - private final ServerList servers; - - /** The list of services. */ - private final ServiceList services; - - /** - * Constructor. - */ - public Component() { - super(); - this.hosts = new CopyOnWriteArrayList(); - this.clients = new ClientList(null); - this.servers = new ServerList(null, this); - this.realms = new CopyOnWriteArrayList(); - this.services = new ServiceList(getContext()); - - if (Engine.getInstance() != null) { - // To be done before setting the helper... - this.services.add(new org.restlet.service.TaskService()); - - this.helper = new ComponentHelper(this); - Context childContext = getContext().createChildContext(); - this.defaultHost = new VirtualHost(childContext); - this.internalRouter = new InternalRouter(childContext); - this.services.add(new LogService()); - getLogService().setContext(childContext); - this.services.add(new StatusService()); - this.clients.setContext(childContext); - this.servers.setContext(childContext); - } - } - - /** - * Returns a modifiable list of client connectors. - * - * @return A modifiable list of client connectors. - */ - public ClientList getClients() { - return this.clients; - } - - /** - * Returns the default virtual host. - * - * @return The default virtual host. - */ - public VirtualHost getDefaultHost() { - return this.defaultHost; - } - - /** - * Returns the helper provided by the implementation. - * - * @return The helper provided by the implementation. - */ - private ComponentHelper getHelper() { - return this.helper; - } - - /** - * Returns the modifiable list of virtual hosts. Note that the order of virtual - * hosts in this list will be used to check the first one that matches. - * - * @return The modifiable list of virtual hosts. - */ - public List getHosts() { - return this.hosts; - } - - /** - * Returns the private internal router where Restlets like Applications can be - * attached. Those Restlets can be addressed via the - * {@link org.restlet.data.Protocol#RIAP} (Restlet Internal Access Protocol) - * client connector. This is used to manage private, internal and optimized - * access to local applications.
- *
- * The first use case is the modularization of a large application into modules - * or layers. This can also be achieved using the - * {@link Context#getServerDispatcher()} method, but the internal router is - * easily addressable via an URI scheme and can be fully private to the current - * Component.
- *
- * The second use case is the composition/mash-up of several representations via - * the org.restlet.ext.xml.Transformer class for example. For this you can - * leverage the XPath's document() function or the XSLT's include and import - * elements with RIAP URIs. - * - * @return The private internal router. - */ - public Router getInternalRouter() { - return this.internalRouter; - } - - /** - * Returns the global log service. On the first call, if no log service was - * defined via the {@link #setLogService(LogService)} method, then a default - * logger service is created. This service will be enabled by default and has a - * logger name composed the "org.restlet." prefix followed by the simple - * component class name (without packages), followed by the ".LogService" - * suffix. - * - * @return The global log service. - */ - public LogService getLogService() { - return getServices().get(LogService.class); - } - - /** - * Finds the realm with the given name. - * - * @param name The name. - * @return The realm found or null. - */ - public Realm getRealm(String name) { - if (name != null) { - for (Realm realm : getRealms()) { - if (name.equals(realm.getName())) { - return realm; - } - } - } - - return null; - } - - /** - * Returns the modifiable list of security realms. - * - * @return The modifiable list of security realms. - */ - public List getRealms() { - return realms; - } - - /** - * Returns the modifiable list of server connectors. - * - * @return The modifiable list of server connectors. - */ - public ServerList getServers() { - return this.servers; - } - - /** - * Returns the modifiable list of services. - * - * @return The modifiable list of services. - */ - public ServiceList getServices() { - return services; - } - - /** - * Returns a task service to run concurrent tasks. The service is enabled by - * default. - * - * @return A task service. - */ - public org.restlet.service.TaskService getTaskService() { - return getServices().get(org.restlet.service.TaskService.class); - } - - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - - if (getHelper() != null) { - getHelper().handle(request, response); - } - } - - /** - * Sets the modifiable list of client connectors. This method clears the current - * list and adds all entries in the parameter list. - * - * @param clients A list of client connectors. - */ - public void setClients(ClientList clients) { - synchronized (getClients()) { - if (clients != getClients()) { - getClients().clear(); - - if (clients != null) { - getClients().addAll(clients); - } - } - } - } - - @Override - public void setContext(Context context) { - super.setContext(context); - getServices().setContext(context); - } - - /** - * Sets the default virtual host. - * - * @param defaultHost The default virtual host. - */ - public void setDefaultHost(VirtualHost defaultHost) { - this.defaultHost = defaultHost; - } - - /** - * Sets the modifiable list of virtual hosts. Note that the order of virtual - * hosts in this list will be used to check the first one that matches. This - * method clears the current list and adds all entries in the parameter list. - * - * @param hosts A list of virtual hosts. - */ - public void setHosts(List hosts) { - synchronized (getHosts()) { - if (hosts != getHosts()) { - getHosts().clear(); - - if (hosts != null) { - getHosts().addAll(hosts); - } - } - } - } - - /** - * Sets the private internal router were Restlets like Applications can be - * attached. - * - * @param internalRouter The private internal router. - * @see #getInternalRouter() - */ - public void setInternalRouter(Router internalRouter) { - this.internalRouter = internalRouter; - } - - /** - * Sets the global log service. - * - * @param logService The global log service. - */ - public void setLogService(LogService logService) { - getServices().set(logService); - } - - /** - * Sets the list of realms. This method clears the current list and adds all - * entries in the parameter list. - * - * @param realms A list of realms. - */ - public void setRealms(List realms) { - synchronized (getRealms()) { - if (realms != getRealms()) { - getRealms().clear(); - - if (realms != null) { - getRealms().addAll(realms); - } - } - } - } - - /** - * Sets the modifiable list of server connectors. This method clears the current - * list and adds all entries in the parameter list. - * - * @param servers A list of server connectors. - */ - public void setServers(ServerList servers) { - synchronized (getServers()) { - if (servers != getServers()) { - getServers().clear(); - - if (servers != null) { - getServers().addAll(servers); - } - } - } - } - - /** - * Sets the task service. - * - * @param taskService The task service. - */ - public void setTaskService(org.restlet.service.TaskService taskService) { - getServices().set(taskService); - } - - /** - * Starts the component. First it starts all the connectors (clients then - * servers), the routers, the services, the realms and then the component's - * internal helper. Finally, it calls the start method of the super class. - * - * @see #startClients() - * @see #startServers() - * @see #startRouters() - * @see #startServices() - * @see #startRealms() - * @see #startHelper() - */ - @Override - public synchronized void start() throws Exception { - if (isStopped()) { - startClients(); - startServers(); - startRouters(); - startServices(); - startRealms(); - startHelper(); - - // Must be invoked as a last step - super.start(); - } - } - - /** - * Starts the client connectors. - * - * @throws Exception - */ - protected synchronized void startClients() throws Exception { - if (this.clients != null) { - for (final Client client : this.clients) { - client.start(); - } - } - } - - /** - * Starts the internal helper allowing incoming requests to be served. - * - * @throws Exception - */ - protected synchronized void startHelper() throws Exception { - if (getHelper() != null) { - getHelper().start(); - } - } - - /** - * Starts the realms. - * - * @throws Exception - */ - protected synchronized void startRealms() throws Exception { - if (this.realms != null) { - for (Realm realm : this.realms) { - realm.start(); - } - } - } - - /** - * Starts the virtual hosts and the internal router. - * - * @throws Exception - */ - protected synchronized void startRouters() throws Exception { - if (this.internalRouter != null) { - this.internalRouter.start(); - } - - if (this.defaultHost != null) { - this.defaultHost.start(); - } - - for (VirtualHost host : getHosts()) { - host.start(); - } - } - - /** - * Starts the server connectors. - * - * @throws Exception - */ - protected synchronized void startServers() throws Exception { - if (this.servers != null) { - for (final Server server : this.servers) { - server.start(); - } - } - } - - /** - * Starts the associated services. - * - * @throws Exception - */ - protected synchronized void startServices() throws Exception { - getServices().start(); - } - - /** - * Stops the component. First it stops the component's internal helper, the - * realms, the services, the routers and then stops all the connectors (servers - * then clients) Finally it calls the stop method of the super class. - * - * @see #stopHelper() - * @see #stopRealms() - * @see #stopServices() - * @see #stopRouters() - * @see #stopServers() - * @see #stopClients() - */ - @Override - public synchronized void stop() throws Exception { - stopHelper(); - stopRealms(); - stopServices(); - stopRouters(); - stopServers(); - stopClients(); - super.stop(); - } - - /** - * Stops the client connectors. - * - * @throws Exception - */ - protected synchronized void stopClients() throws Exception { - if (this.clients != null) { - for (final Client client : this.clients) { - client.stop(); - } - } - } - - /** - * Stops the internal helper allowing incoming requests to be served. - * - * @throws Exception - */ - protected synchronized void stopHelper() throws Exception { - if (getHelper() != null) { - getHelper().stop(); - } - } - - /** - * Stops the realms. - * - * @throws Exception - */ - protected synchronized void stopRealms() throws Exception { - if (this.realms != null) { - for (Realm realm : this.realms) { - realm.stop(); - } - } - } - - /** - * Stops the virtual hosts and the internal router. - * - * @throws Exception - */ - protected synchronized void stopRouters() throws Exception { - for (VirtualHost host : getHosts()) { - host.stop(); - } - - if (this.defaultHost != null) { - this.defaultHost.stop(); - } - - if (this.internalRouter != null) { - this.internalRouter.stop(); - } - } - - /** - * Stops the server connectors. - * - * @throws Exception - */ - protected synchronized void stopServers() throws Exception { - if (this.servers != null) { - for (final Server server : this.servers) { - server.stop(); - } - } - } - - /** - * Stops the associated services. - * - * @throws Exception - */ - protected synchronized void stopServices() throws Exception { - getServices().stop(); - } - - /** - * Updates the component to take into account changes to the virtual hosts. This - * method doesn't stop the connectors or the applications or Restlets attached - * to the virtual hosts. It just updates the internal routes between the virtual - * hosts and the attached Restlets or applications. - * - * @throws Exception - */ - public synchronized void updateHosts() throws Exception { - getHelper().update(); - } + /** The modifiable list of client connectors. */ + private final ClientList clients; + + /** The default host. */ + private volatile VirtualHost defaultHost; + + /** The helper provided by the implementation. */ + private volatile ComponentHelper helper; + + /** The modifiable list of virtual hosts. */ + private final List hosts; + + /** The private internal router that can be addressed via the RIAP client connector. */ + private volatile Router internalRouter; + + /** The modifiable list of security realms. */ + private final List realms; + + /** The modifiable list of server connectors. */ + private final ServerList servers; + + /** The list of services. */ + private final ServiceList services; + + /** Constructor. */ + public Component() { + super(); + this.hosts = new CopyOnWriteArrayList(); + this.clients = new ClientList(null); + this.servers = new ServerList(null, this); + this.realms = new CopyOnWriteArrayList(); + this.services = new ServiceList(getContext()); + + if (Engine.getInstance() != null) { + // To be done before setting the helper... + this.services.add(new org.restlet.service.TaskService()); + + this.helper = new ComponentHelper(this); + Context childContext = getContext().createChildContext(); + this.defaultHost = new VirtualHost(childContext); + this.internalRouter = new InternalRouter(childContext); + this.services.add(new LogService()); + getLogService().setContext(childContext); + this.services.add(new StatusService()); + this.clients.setContext(childContext); + this.servers.setContext(childContext); + } + } + + /** + * Returns a modifiable list of client connectors. + * + * @return A modifiable list of client connectors. + */ + public ClientList getClients() { + return this.clients; + } + + /** + * Returns the default virtual host. + * + * @return The default virtual host. + */ + public VirtualHost getDefaultHost() { + return this.defaultHost; + } + + /** + * Returns the helper provided by the implementation. + * + * @return The helper provided by the implementation. + */ + private ComponentHelper getHelper() { + return this.helper; + } + + /** + * Returns the modifiable list of virtual hosts. Note that the order of virtual hosts in this + * list will be used to check the first one that matches. + * + * @return The modifiable list of virtual hosts. + */ + public List getHosts() { + return this.hosts; + } + + /** + * Returns the private internal router where Restlets like Applications can be attached. Those + * Restlets can be addressed via the {@link org.restlet.data.Protocol#RIAP} (Restlet Internal + * Access Protocol) client connector. This is used to manage private, internal, and optimized + * access to local applications.
+ *
+ * The first use case is the modularization of a large application into modules or layers. This + * can also be achieved using the {@link Context#getServerDispatcher()} method, but the internal + * router is easily addressable via a URI scheme and can be fully private to the current + * Component.
+ *
+ * The second use case is the composition/mash-up of several representations via the + * org.restlet.ext.xml.Transformer class, for example. For this you can leverage the XPath's + * document() function or the XSLT's include and import elements with RIAP URIs. + * + * @return The private internal router. + */ + public Router getInternalRouter() { + return this.internalRouter; + } + + /** + * Returns the global log service. On the first call, if no log service was defined via the + * {@link #setLogService(LogService)} method, then a default logger service is created. This + * service will be enabled by default and has a logger name composed the "org.restlet." Prefix + * followed by the simple component class name (without packages), followed by the ".LogService" + * suffix. + * + * @return The global log service. + */ + public LogService getLogService() { + return getServices().get(LogService.class); + } + + /** + * Finds the realm with the given name. + * + * @param name The name. + * @return The realm found or null. + */ + public Realm getRealm(String name) { + if (name != null) { + for (Realm realm : getRealms()) { + if (name.equals(realm.getName())) { + return realm; + } + } + } + + return null; + } + + /** + * Returns the modifiable list of security realms. + * + * @return The modifiable list of security realms. + */ + public List getRealms() { + return realms; + } + + /** + * Returns the modifiable list of server connectors. + * + * @return The modifiable list of server connectors. + */ + public ServerList getServers() { + return this.servers; + } + + /** + * Returns the modifiable list of services. + * + * @return The modifiable list of services. + */ + public ServiceList getServices() { + return services; + } + + /** + * Returns a task service to run concurrent tasks. The service is enabled by default. + * + * @return A task service. + */ + public org.restlet.service.TaskService getTaskService() { + return getServices().get(org.restlet.service.TaskService.class); + } + + @Override + public void handle(Request request, Response response) { + super.handle(request, response); + + if (getHelper() != null) { + getHelper().handle(request, response); + } + } + + /** + * Sets the modifiable list of client connectors. This method clears the current list and adds + * all entries in the parameter list. + * + * @param clients A list of client connectors. + */ + public void setClients(ClientList clients) { + synchronized (getClients()) { + if (clients != getClients()) { + getClients().clear(); + + if (clients != null) { + getClients().addAll(clients); + } + } + } + } + + @Override + public void setContext(Context context) { + super.setContext(context); + getServices().setContext(context); + } + + /** + * Sets the default virtual host. + * + * @param defaultHost The default virtual host. + */ + public void setDefaultHost(VirtualHost defaultHost) { + this.defaultHost = defaultHost; + } + + /** + * Sets the modifiable list of virtual hosts. Note that the order of virtual hosts in this list + * will be used to check the first one that matches. This method clears the current list and + * adds all entries in the parameter list. + * + * @param hosts A list of virtual hosts. + */ + public void setHosts(List hosts) { + synchronized (getHosts()) { + if (hosts != getHosts()) { + getHosts().clear(); + + if (hosts != null) { + getHosts().addAll(hosts); + } + } + } + } + + /** + * Sets the private internal router where Restlets like Applications can be attached. + * + * @param internalRouter The private internal router. + * @see #getInternalRouter() + */ + public void setInternalRouter(Router internalRouter) { + this.internalRouter = internalRouter; + } + + /** + * Sets the global log service. + * + * @param logService The global log service. + */ + public void setLogService(LogService logService) { + getServices().set(logService); + } + + /** + * Sets the list of realms. This method clears the current list and adds all entries in the + * parameter list. + * + * @param realms A list of realms. + */ + public void setRealms(List realms) { + synchronized (getRealms()) { + if (realms != getRealms()) { + getRealms().clear(); + + if (realms != null) { + getRealms().addAll(realms); + } + } + } + } + + /** + * Sets the modifiable list of server connectors. This method clears the current list and adds + * all entries in the parameter list. + * + * @param servers A list of server connectors. + */ + public void setServers(ServerList servers) { + synchronized (getServers()) { + if (servers != getServers()) { + getServers().clear(); + + if (servers != null) { + getServers().addAll(servers); + } + } + } + } + + /** + * Sets the task service. + * + * @param taskService The task service. + */ + public void setTaskService(org.restlet.service.TaskService taskService) { + getServices().set(taskService); + } + + /** + * Starts the component. First it starts all the connectors (clients then servers), the routers, + * the services, the realms, and then the component's internal helper. Finally, it calls the + * start method of the super class. + * + * @see #startClients() + * @see #startServers() + * @see #startRouters() + * @see #startServices() + * @see #startRealms() + * @see #startHelper() + */ + @Override + public synchronized void start() throws Exception { + if (isStopped()) { + startClients(); + startServers(); + startRouters(); + startServices(); + startRealms(); + startHelper(); + + // Must be invoked as a last step + super.start(); + } + } + + /** + * Starts the client connectors. + * + * @throws Exception + */ + protected synchronized void startClients() throws Exception { + if (this.clients != null) { + for (final Client client : this.clients) { + client.start(); + } + } + } + + /** + * Starts the internal helper allowing incoming requests to be served. + * + * @throws Exception + */ + protected synchronized void startHelper() throws Exception { + if (getHelper() != null) { + getHelper().start(); + } + } + + /** + * Starts the realms. + * + * @throws Exception + */ + protected synchronized void startRealms() throws Exception { + if (this.realms != null) { + for (Realm realm : this.realms) { + realm.start(); + } + } + } + + /** + * Starts the virtual hosts and the internal router. + * + * @throws Exception + */ + protected synchronized void startRouters() throws Exception { + if (this.internalRouter != null) { + this.internalRouter.start(); + } + + if (this.defaultHost != null) { + this.defaultHost.start(); + } + + for (VirtualHost host : getHosts()) { + host.start(); + } + } + + /** + * Starts the server connectors. + * + * @throws Exception + */ + protected synchronized void startServers() throws Exception { + if (this.servers != null) { + for (final Server server : this.servers) { + server.start(); + } + } + } + + /** + * Starts the associated services. + * + * @throws Exception + */ + protected synchronized void startServices() throws Exception { + getServices().start(); + } + + /** + * Stops the component. First, it stops the component's internal helper, the realms, the + * services, the routers and then stops all the connectors (servers then clients). Finally, it + * calls the stop method of the super class. + * + * @see #stopHelper() + * @see #stopRealms() + * @see #stopServices() + * @see #stopRouters() + * @see #stopServers() + * @see #stopClients() + */ + @Override + public synchronized void stop() throws Exception { + stopHelper(); + stopRealms(); + stopServices(); + stopRouters(); + stopServers(); + stopClients(); + super.stop(); + } + + /** + * Stops the client connectors. + * + * @throws Exception + */ + protected synchronized void stopClients() throws Exception { + if (this.clients != null) { + for (final Client client : this.clients) { + client.stop(); + } + } + } + + /** + * Stops the internal helper allowing incoming requests to be served. + * + * @throws Exception + */ + protected synchronized void stopHelper() throws Exception { + if (getHelper() != null) { + getHelper().stop(); + } + } + + /** + * Stops the realms. + * + * @throws Exception + */ + protected synchronized void stopRealms() throws Exception { + if (this.realms != null) { + for (Realm realm : this.realms) { + realm.stop(); + } + } + } + + /** + * Stops the virtual hosts and the internal router. + * + * @throws Exception + */ + protected synchronized void stopRouters() throws Exception { + for (VirtualHost host : getHosts()) { + host.stop(); + } + + if (this.defaultHost != null) { + this.defaultHost.stop(); + } + + if (this.internalRouter != null) { + this.internalRouter.stop(); + } + } + + /** + * Stops the server connectors. + * + * @throws Exception + */ + protected synchronized void stopServers() throws Exception { + if (this.servers != null) { + for (final Server server : this.servers) { + server.stop(); + } + } + } + + /** + * Stops the associated services. + * + * @throws Exception + */ + protected synchronized void stopServices() throws Exception { + getServices().stop(); + } + + /** + * Updates the component to take into account changes to the virtual hosts. This method doesn't + * stop the connectors or the applications or Restlets attached to the virtual hosts. It just + * updates the internal routes between the virtual hosts and the attached Restlets or + * applications. + * + * @throws Exception + */ + public synchronized void updateHosts() throws Exception { + getHelper().update(); + } } diff --git a/org.restlet/src/main/java/org/restlet/Connector.java b/org.restlet/src/main/java/org/restlet/Connector.java index a74598d402..a6ee939947 100644 --- a/org.restlet/src/main/java/org/restlet/Connector.java +++ b/org.restlet/src/main/java/org/restlet/Connector.java @@ -1,105 +1,98 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; -import org.restlet.data.Protocol; - import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.data.Protocol; /** - * Restlet enabling communication between Components. "A connector is an - * abstract mechanism that mediates communication, coordination, or cooperation - * among components. Connectors enable communication between components by - * transferring data elements from one interface to another without changing the - * data." Roy T. Fielding
+ * Restlet enabling communication between Components. "A connector is an abstract mechanism that + * mediates communication, coordination, or cooperation among components. Connectors enable + * communication between components by transferring data elements from one interface to another + * without changing the data." Roy T. Fielding
*
- * "Encapsulate the activities of accessing resources and transferring resource - * representations. The connectors present an abstract interface for component - * communication, enhancing simplicity by providing a clean separation of - * concerns and hiding the underlying implementation of resources and - * communication mechanisms" Roy T. Fielding
+ * "Encapsulate the activities of accessing resources and transferring resource representations. The + * connectors present an abstract interface for component communication, enhancing simplicity by + * providing a clean separation of concerns and hiding the underlying implementation of resources + * and communication mechanisms" Roy T. Fielding
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * - * @see Source - * dissertation - * @see Source - * dissertation + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * + * @see Source + * dissertation + * @see Source + * dissertation * @author Jerome Louvel */ public abstract class Connector extends Restlet { - /** The list of protocols simultaneously supported. */ - private final List protocols; - - /** - * Constructor. - * - * @param context The context. - */ - public Connector(Context context) { - this(context, null); - } + /** The list of protocols simultaneously supported. */ + private final List protocols; - /** - * Constructor. - * - * @param context The context. - * @param protocols The supported protocols. - */ - public Connector(Context context, List protocols) { - super(context); + /** + * Constructor. + * + * @param context The context. + */ + public Connector(Context context) { + this(context, null); + } - if (protocols == null) { - this.protocols = new CopyOnWriteArrayList(); - } else { - this.protocols = new CopyOnWriteArrayList(protocols); - } - } + /** + * Constructor. + * + * @param context The context. + * @param protocols The supported protocols. + */ + public Connector(Context context, List protocols) { + super(context); - /** - * Returns the modifiable list of protocols simultaneously supported. - * - * @return The protocols simultaneously supported. - */ - public List getProtocols() { - return this.protocols; - } + if (protocols == null) { + this.protocols = new CopyOnWriteArrayList<>(); + } else { + this.protocols = new CopyOnWriteArrayList<>(protocols); + } + } - /** - * Indicates the underlying connector helper is available. - * - * @return True if the underlying connector helper is available. - */ - public abstract boolean isAvailable(); + /** + * Returns the modifiable list of protocols simultaneously supported. + * + * @return The protocols simultaneously supported. + */ + public List getProtocols() { + return this.protocols; + } - /** - * Sets the list of protocols simultaneously supported. This method clears the - * current list and adds all entries in the parameter list. - * - * @param protocols A list of protocols. - */ - public void setProtocols(List protocols) { - synchronized (getProtocols()) { - if (protocols != getProtocols()) { - getProtocols().clear(); + /** + * Indicates the underlying connector helper is available. + * + * @return True if the underlying connector helper is available. + */ + public abstract boolean isAvailable(); - if (protocols != null) { - getProtocols().addAll(protocols); - } - } - } - } + /** + * Sets the list of protocols simultaneously supported. This method clears the current list and + * adds all entries in the parameter list. + * + * @param protocols A list of protocols. + */ + public void setProtocols(List protocols) { + synchronized (getProtocols()) { + if (protocols != getProtocols()) { + getProtocols().clear(); + if (protocols != null) { + getProtocols().addAll(protocols); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/Context.java b/org.restlet/src/main/java/org/restlet/Context.java index 497647e0e6..e2fe6e711c 100644 --- a/org.restlet/src/main/java/org/restlet/Context.java +++ b/org.restlet/src/main/java/org/restlet/Context.java @@ -1,366 +1,350 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; -import org.restlet.data.Parameter; -import org.restlet.engine.Engine; -import org.restlet.util.Series; - import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; import java.util.logging.Logger; +import org.restlet.data.Parameter; +import org.restlet.engine.Engine; +import org.restlet.util.Series; /** - * Contextual data and services provided to a set of Restlets. The context is - * the means by which a Restlet may access the software environment within the - * framework. It is typically provided by the immediate parent Restlet - * (Application is the most common case).
+ * Contextual data and services provided to a set of Restlets. The context is the means by which a + * Restlet may access the software environment within the framework. It is typically provided by the + * immediate parent Restlet (Application is the most common case).
*
- * Concurrency note: attributes and parameters of a context are stored in - * concurrent collections that guarantee thread safe access and modification. If - * several threads concurrently access objects and modify these collections, - * they should synchronize on the lock of the Context instance. - * + * Concurrency note: attributes and parameters of a context are stored in concurrent collections + * that guarantee thread-safe access and modification. If several threads concurrently access + * objects and modify these collections, they should synchronize on the lock of the Context + * instance. + * * @author Jerome Louvel */ public class Context { - private static final ThreadLocal CURRENT = new ThreadLocal(); - - /** - * Returns the context associated to the current {@link Restlet}. The context - * can be the one of a {@link Component}, an {@link Application}, a - * {@link org.restlet.routing.Filter} or any other {@link Restlet} subclass.
- *
- * Warning: this method should only be used under duress. You should by default - * prefer obtaining the current context using methods such as - * {@link org.restlet.Restlet#getContext()} or - * {@link org.restlet.resource.Resource#getContext()}.
- *
- * This variable is stored internally as a thread local variable and updated - * each time a request is handled by a {@link Restlet} via the - * {@link Restlet#handle(org.restlet.Request, org.restlet.Response)} method. - * - * @return The current context. - */ - public static Context getCurrent() { - return CURRENT.get(); - } - - /** - * Returns the current context's logger. - * - * @return The current context's logger. - */ - public static Logger getCurrentLogger() { - return (Context.getCurrent() != null) ? Context.getCurrent().getLogger() : Engine.getLogger("org.restlet"); - } - - /** - * Sets the context to associated with the current thread. - * - * @param context The thread's context. - */ - public static void setCurrent(Context context) { - CURRENT.set(context); - } - - /** The client dispatcher. */ - private volatile Restlet clientDispatcher; - - /** The server dispatcher. */ - private volatile Restlet serverDispatcher; - - /** The modifiable attributes map. */ - private final ConcurrentMap attributes; - - /** The logger instance to use. */ - private volatile Logger logger; - - /** The modifiable series of parameters. */ - private final Series parameters; - - /** - * The enroler that can add the user roles based on Restlet default - * authorization model. - */ - private volatile org.restlet.security.Enroler defaultEnroler; - - /** - * The verifier that can check the validity of user/secret couples based on - * Restlet default authorization model. - */ - private volatile org.restlet.security.Verifier defaultVerifier; - - /** The executor service. */ - private volatile ScheduledExecutorService executorService; - - /** - * Constructor. Writes log messages to "org.restlet". - */ - public Context() { - this("org.restlet"); - } - - /** - * Constructor. - * - * @param logger The logger instance of use. - */ - public Context(Logger logger) { - this.attributes = new ConcurrentHashMap(); - this.logger = logger; - this.parameters = new Series(Parameter.class, new CopyOnWriteArrayList()); - this.clientDispatcher = null; - - this.defaultEnroler = null; - this.serverDispatcher = null; - this.defaultVerifier = null; - } - - /** - * Constructor. - * - * @param loggerName The name of the logger to use. - */ - public Context(String loggerName) { - this(Engine.getLogger(loggerName)); - } - - /** - * Creates a protected child context. This is especially useful for new - * application attached to their parent component, to ensure their isolation - * from the other applications. By default it creates a new context instance - * with empty or null properties, except the client and server dispatchers that - * are wrapped for isolation purpose. - * - * @return The child context. - */ - public Context createChildContext() { - return new org.restlet.engine.util.ChildContext(this); - } - - /** - * Returns a modifiable attributes map that can be used by developers to save - * information relative to the context. This is a convenient means to provide - * common objects to all the Restlets and Resources composing an - * Application.
- *
- * - * In addition, this map is a shared space between the developer and the Restlet - * implementation. For this purpose, all attribute names starting with - * "org.restlet" are reserved. Currently the following attributes are used: - * - * - * - * - * - * - * - * - * - * - * - * - *
list of currently used attributes
Attribute nameClass nameDescription
org.restlet.applicationorg.restlet.ApplicationThe parent application providing this context, if any.
- * - * @return The modifiable attributes map. - */ - public ConcurrentMap getAttributes() { - return this.attributes; - } - - /** - * Returns a request dispatcher to available client connectors. When you ask the - * dispatcher to handle a request, it will automatically select the appropriate - * client connector for your request, based on the request.protocol property or - * on the resource URI's scheme. This call is blocking and will return an - * updated response object. - * - * @return A request dispatcher to available client connectors. - */ - public Restlet getClientDispatcher() { - return this.clientDispatcher; - } - - /** - * Returns an enroler that can add the user roles based on authenticated user - * principals. - * - * @return An enroler. - */ - public org.restlet.security.Enroler getDefaultEnroler() { - return defaultEnroler; - } - - /** - * Returns a verifier that can check the validity of the credentials associated - * to a request. - * - * @return A verifier. - */ - public org.restlet.security.Verifier getDefaultVerifier() { - return this.defaultVerifier; - } - - /** - * Returns the executor service. - * - * @return The executor service. - */ - public ScheduledExecutorService getExecutorService() { - return this.executorService; - } - - /** - * Returns the logger. - * - * @return The logger. - */ - public Logger getLogger() { - return this.logger; - } - - /** - * Returns the modifiable series of parameters. A parameter is a pair composed - * of a name and a value and is typically used for configuration purpose, like - * Java properties. Note that multiple parameters with the same name can be - * declared and accessed. - * - * @return The modifiable series of parameters. - */ - public Series getParameters() { - return this.parameters; - } - - /** - * Returns a request dispatcher to component's virtual hosts. This is useful for - * application that want to optimize calls to other applications hosted in the - * same component or to the application itself.
- *
- * The processing is the same as what would have been done if the request came - * from one of the component's server connectors. It first must match one of the - * registered virtual hosts. Then it can be routed to one of the attached - * Restlets, typically an Application.
- *
- * Note that the RIAP pseudo protocol isn't supported by this dispatcher, you - * should instead rely on the {@link #getClientDispatcher()} method. - * - * @return A request dispatcher to the server connectors' router. - */ - public Restlet getServerDispatcher() { - return this.serverDispatcher; - } - - /** - * Sets the modifiable map of attributes. This method clears the current map and - * puts all entries in the parameter map. - * - * @param attributes A map of attributes. - */ - public void setAttributes(Map attributes) { - synchronized (getAttributes()) { - if (attributes != getAttributes()) { - getAttributes().clear(); - - if (attributes != null) { - getAttributes().putAll(attributes); - } - } - } - } - - /** - * Sets the client dispatcher. - * - * @param clientDispatcher The new client dispatcher. - */ - public void setClientDispatcher(Restlet clientDispatcher) { - this.clientDispatcher = clientDispatcher; - } - - /** - * Sets an enroler that can add the user roles based on authenticated user - * principals. - * - * @param enroler An enroler. - */ - public void setDefaultEnroler(org.restlet.security.Enroler enroler) { - this.defaultEnroler = enroler; - } - - /** - * Sets a local verifier that can check the validity of user/secret couples - * based on Restlet default authorization model. - * - * @param verifier A local verifier. - */ - public void setDefaultVerifier(org.restlet.security.Verifier verifier) { - this.defaultVerifier = verifier; - } - - /** - * Sets the executor service. - * - * @param executorService The executor service. - */ - public void setExecutorService(ScheduledExecutorService executorService) { - this.executorService = executorService; - } - - /** - * Sets the logger. - * - * @param logger The logger. - */ - public void setLogger(Logger logger) { - this.logger = logger; - } - - /** - * Sets the logger. - * - * @param loggerName The name of the logger to use. - */ - public void setLogger(String loggerName) { - setLogger(Engine.getLogger(loggerName)); - } - - /** - * Sets the modifiable series of parameters. This method clears the current - * series and adds all entries in the parameter series. - * - * @param parameters A series of parameters. - */ - public void setParameters(Series parameters) { - synchronized (getParameters()) { - if (parameters != getParameters()) { - getParameters().clear(); - - if (parameters != null) { - getParameters().addAll(parameters); - } - } - } - } - - /** - * Sets the server dispatcher. - * - * @param serverDispatcher The new server dispatcher. - */ - public void setServerDispatcher(Restlet serverDispatcher) { - this.serverDispatcher = serverDispatcher; - } - + private static final ThreadLocal CURRENT = new ThreadLocal(); + + /** + * Returns the context associated with the current {@link Restlet}. The context can be the one + * of a {@link Component}, an {@link Application}, a {@link org.restlet.routing.Filter} or any + * other {@link Restlet} subclass.
+ *
+ * Warning: this method should only be used under duress. You should by default prefer getting + * the current context using methods such as {@link org.restlet.Restlet#getContext()} or {@link + * org.restlet.resource.Resource#getContext()}.
+ *
+ * This variable is stored internally as a thread local variable and updated each time a request + * is handled by a {@link Restlet} via the {@link Restlet#handle(org.restlet.Request, + * org.restlet.Response)} method. + * + * @return The current context. + */ + public static Context getCurrent() { + return CURRENT.get(); + } + + /** + * Returns the current context's logger. + * + * @return The current context's logger. + */ + public static Logger getCurrentLogger() { + return (Context.getCurrent() != null) + ? Context.getCurrent().getLogger() + : Engine.getLogger("org.restlet"); + } + + /** + * Sets the context to associated with the current thread. + * + * @param context The thread's context. + */ + public static void setCurrent(Context context) { + CURRENT.set(context); + } + + /** The client dispatcher. */ + private volatile Restlet clientDispatcher; + + /** The server dispatcher. */ + private volatile Restlet serverDispatcher; + + /** The modifiable attributes map. */ + private final ConcurrentMap attributes; + + /** The logger instance to use. */ + private volatile Logger logger; + + /** The modifiable series of parameters. */ + private final Series parameters; + + /** The enroler that can add the user roles based on a Restlet default authorization model. */ + private volatile org.restlet.security.Enroler defaultEnroler; + + /** + * The verifier that can check the validity of user/secret couples based on a Restlet default + * authorization model. + */ + private volatile org.restlet.security.Verifier defaultVerifier; + + /** The executor service. */ + private volatile ScheduledExecutorService executorService; + + /** Constructor. Writes log messages to "org.restlet". */ + public Context() { + this("org.restlet"); + } + + /** + * Constructor. + * + * @param logger The logger instance of use. + */ + public Context(Logger logger) { + this.attributes = new ConcurrentHashMap<>(); + this.logger = logger; + this.parameters = new Series<>(Parameter.class, new CopyOnWriteArrayList<>()); + this.clientDispatcher = null; + + this.defaultEnroler = null; + this.serverDispatcher = null; + this.defaultVerifier = null; + } + + /** + * Constructor. + * + * @param loggerName The name of the logger to use. + */ + public Context(String loggerName) { + this(Engine.getLogger(loggerName)); + } + + /** + * Creates a protected child context. This is especially useful for new application attached to + * their parent component to ensure their isolation from the other applications. By default, it + * creates a new context instance with empty or null properties, except the client and server + * dispatchers that are wrapped for isolation purposes. + * + * @return The child context. + */ + public Context createChildContext() { + return new org.restlet.engine.util.ChildContext(this); + } + + /** + * Returns a modifiable attributes map that developers can use to save information + * relative to the context. This is a convenient means to provide common objects to all the + * Restlets and Resources composing an Application.
+ *
+ * In addition, this map is a shared space between the developer and the Restlet implementation. + * For this purpose, all attribute names starting with "org.restlet" are reserved. Currently, the + * following attributes are used: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
list of currently used attributes
Attribute nameClass nameDescription
org.restlet.applicationorg.restlet.ApplicationThe parent application providing this context, if any.
+ * + * @return The modifiable attributes map. + */ + public ConcurrentMap getAttributes() { + return this.attributes; + } + + /** + * Returns a request dispatcher to available client connectors. When you ask the dispatcher to + * handle a request, it will automatically select the appropriate client connector for your + * request, based on the request.protocol property or on the resource URI's scheme. This call is + * blocking and will return an updated response object. + * + * @return A request dispatcher to available client connectors. + */ + public Restlet getClientDispatcher() { + return this.clientDispatcher; + } + + /** + * Returns an enroler that can add the user roles based on authenticated user principals. + * + * @return An enroler. + */ + public org.restlet.security.Enroler getDefaultEnroler() { + return defaultEnroler; + } + + /** + * Returns a verifier that can check the validity of the credentials associated with a request. + * + * @return A verifier. + */ + public org.restlet.security.Verifier getDefaultVerifier() { + return this.defaultVerifier; + } + + /** + * Returns the executor service. + * + * @return The executor service. + */ + public ScheduledExecutorService getExecutorService() { + return this.executorService; + } + + /** + * Returns the logger. + * + * @return The logger. + */ + public Logger getLogger() { + return this.logger; + } + + /** + * Returns the modifiable series of parameters. A parameter is a pair composed of a name and a + * value and is typically used for configuration purpose, like Java properties. Note that + * multiple parameters with the same name can be declared and accessed. + * + * @return The modifiable series of parameters. + */ + public Series getParameters() { + return this.parameters; + } + + /** + * Returns a request dispatcher to the component's virtual hosts. This is useful for application + * that wants to optimize calls to other applications hosted in the same component or to the + * application itself.
+ *
+ * The processing is the same as what would have been done if the request came from one of the + * component's server connectors. It first must match one of the registered virtual hosts. Then + * it can be routed to one of the attached Restlets, typically an Application.
+ *
+ * Note that this dispatcher doesn't support the RIAP pseudo protocol, you should instead + * rely on the {@link #getClientDispatcher()} method. + * + * @return A request dispatcher to the server connectors' router. + */ + public Restlet getServerDispatcher() { + return this.serverDispatcher; + } + + /** + * Sets the modifiable map of attributes. This method clears the current map and puts all + * entries in the parameter map. + * + * @param attributes A map of attributes. + */ + public void setAttributes(Map attributes) { + synchronized (getAttributes()) { + if (attributes != getAttributes()) { + getAttributes().clear(); + + if (attributes != null) { + getAttributes().putAll(attributes); + } + } + } + } + + /** + * Sets the client dispatcher. + * + * @param clientDispatcher The new client dispatcher. + */ + public void setClientDispatcher(Restlet clientDispatcher) { + this.clientDispatcher = clientDispatcher; + } + + /** + * Sets an enroler that can add the user roles based on authenticated user principals. + * + * @param enroler An enroler. + */ + public void setDefaultEnroler(org.restlet.security.Enroler enroler) { + this.defaultEnroler = enroler; + } + + /** + * Sets a local verifier that can check the validity of user/secret couples based on a Restlet + * default authorization model. + * + * @param verifier A local verifier. + */ + public void setDefaultVerifier(org.restlet.security.Verifier verifier) { + this.defaultVerifier = verifier; + } + + /** + * Sets the executor service. + * + * @param executorService The executor service. + */ + public void setExecutorService(ScheduledExecutorService executorService) { + this.executorService = executorService; + } + + /** + * Sets the logger. + * + * @param logger The logger. + */ + public void setLogger(Logger logger) { + this.logger = logger; + } + + /** + * Sets the logger. + * + * @param loggerName The name of the logger to use. + */ + public void setLogger(String loggerName) { + setLogger(Engine.getLogger(loggerName)); + } + + /** + * Sets the modifiable series of parameters. This method clears the current series and adds all + * entries in the parameter series. + * + * @param parameters A series of parameters. + */ + public void setParameters(Series parameters) { + synchronized (getParameters()) { + if (parameters != getParameters()) { + getParameters().clear(); + + if (parameters != null) { + getParameters().addAll(parameters); + } + } + } + } + + /** + * Sets the server dispatcher. + * + * @param serverDispatcher The new server dispatcher. + */ + public void setServerDispatcher(Restlet serverDispatcher) { + this.serverDispatcher = serverDispatcher; + } } diff --git a/org.restlet/src/main/java/org/restlet/Message.java b/org.restlet/src/main/java/org/restlet/Message.java index 6834cbef17..1f5bad8c0c 100644 --- a/org.restlet/src/main/java/org/restlet/Message.java +++ b/org.restlet/src/main/java/org/restlet/Message.java @@ -1,19 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; -import org.restlet.data.*; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ClientResource; -import org.restlet.util.Series; +import static org.restlet.engine.header.HeaderConstants.ATTRIBUTE_HEADERS; +import static org.restlet.representation.Representation.UNKNOWN_SIZE; import java.io.IOException; import java.util.Date; @@ -22,440 +18,437 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; - -import static org.restlet.engine.header.HeaderConstants.ATTRIBUTE_HEADERS; -import static org.restlet.representation.Representation.UNKNOWN_SIZE; +import org.restlet.data.CacheDirective; +import org.restlet.data.Header; +import org.restlet.data.MediaType; +import org.restlet.data.RecipientInfo; +import org.restlet.data.Warning; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.resource.ClientResource; +import org.restlet.util.Series; /** * Generic message exchanged between components. - * + * * @author Jerome Louvel */ public abstract class Message { - /** The modifiable attributes map. */ - private volatile ConcurrentMap attributes; - - /** The caching directives. */ - private volatile List cacheDirectives; - - /** The date and time at which the message was originated. */ - private volatile Date date; - - /** The payload of the message. */ - private volatile Representation entity; - - /** The optional cached text. */ - private volatile String entityText; - - /** Callback invoked when an error occurs when sending the message. */ - private volatile Uniform onError; - - /** Callback invoked after sending the message. */ - private volatile Uniform onSent; - - /** The intermediary recipients info. */ - private volatile List recipientsInfo; - - /** The additional warnings information. */ - private volatile List warnings; - - /** - * Constructor. - */ - public Message() { - this(null); - } - - /** - * Constructor. - * - * @param entity The payload of the message. - */ - public Message(Representation entity) { - this.attributes = null; - this.cacheDirectives = null; - this.date = null; - this.entity = entity; - this.entityText = null; - this.onSent = null; - this.recipientsInfo = null; - this.warnings = null; - } - - /** - * If the entity is transient or its size unknown in advance but available, then - * the entity is wrapped with a - * {@link org.restlet.representation.BufferingRepresentation}.
- *
- * Be careful as this method could create potentially very large byte buffers in - * memory that could impact your application performance. - * - * @see org.restlet.representation.BufferingRepresentation - * @see ClientResource#setRequestEntityBuffering(boolean) - * @see ClientResource#setResponseEntityBuffering(boolean) - */ - public void bufferEntity() { - if ((getEntity() != null) && (getEntity().isTransient() || getEntity().getSize() == UNKNOWN_SIZE) - && getEntity().isAvailable()) { - setEntity(new org.restlet.representation.BufferingRepresentation(getEntity())); - } - } - - /** - * Asks the underlying connector to immediately flush the network buffers. - * - * @throws IOException - */ - public void flushBuffers() throws IOException { - } - - /** - * Returns the modifiable map of attributes that can be used by developers to - * save information relative to the message. Creates a new instance if no one - * has been set. This is an easier alternative to the creation of a wrapper - * instance around the whole message.
- *
- * - * In addition, this map is a shared space between the developer and the - * connectors. In this case, it is used to exchange information that is not - * uniform across all protocols and couldn't therefore be directly included in - * the API. For this purpose, all attribute names starting with "org.restlet" - * are reserved. Currently the following attributes are used: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
list of currently used attributes
Attribute nameClass nameDescription
org.restlet.http.headersorg.restlet.util.Series<org.restlet.engine.header.Header>Server HTTP connectors must provide all request headers and client HTTP - * connectors must provide all response headers, exactly as they were received. - * In addition, developers can also use this attribute to specify - * non-standard headers that should be added to the request or to the - * response.
org.restlet.https.clientCertificatesList<java.security.cert.Certificate>For requests received via a secure connector, indicates the ordered list - * of client certificates, if they are available and accessible.
- *
- * Most of the standard HTTP headers are directly supported via the Restlet API. - * Thus, adding such HTTP headers is forbidden because it could conflict with - * the connector's internal behavior, limit portability or prevent future - * optimizations. The other standard HTTP headers (that are not supported) can - * be added as attributes via the "org.restlet.http.headers" key.
- * - * @return The modifiable attributes map. - */ - public ConcurrentMap getAttributes() { - // Lazy initialization with double-check. - ConcurrentMap r = this.attributes; - if (r == null) { - synchronized (this) { - r = this.attributes; - if (r == null) { - this.attributes = r = new ConcurrentHashMap(); - } - } - } - - return this.attributes; - } - - /** - * Returns the cache directives.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Cache-Control" header. - * - * @return The cache directives. - */ - public List getCacheDirectives() { - // Lazy initialization with double-check. - List r = this.cacheDirectives; - if (r == null) { - synchronized (this) { - r = this.cacheDirectives; - if (r == null) { - this.cacheDirectives = r = new CopyOnWriteArrayList(); - } - } - } - return r; - } - - /** - * Returns the date and time at which the message was originated. - * - * @return The date and time at which the message was originated. - */ - public Date getDate() { - return date; - } - - /** - * Returns the entity representation. - * - * @return The entity representation. - */ - public Representation getEntity() { - return this.entity; - } - - /** - * Returns the entity as text. This method can be called several times and will - * always return the same text. Note that if the entity is large this method can - * result in important memory consumption. - * - * @return The entity as text. - */ - public String getEntityAsText() { - if (this.entityText == null) { - try { - this.entityText = (getEntity() == null) ? null : getEntity().getText(); - } catch (java.io.IOException e) { - Context.getCurrentLogger().log(java.util.logging.Level.FINE, "Unable to get the entity text.", e); - } - } - - return this.entityText; - } - - /** - * Returns the series of lower-level HTTP headers. Please not that this method - * should rarely be used as most HTTP headers are already surfaced by the - * Restlet API. The result series can be used to deal with HTTP extension - * headers. - * - * @return The HTTP headers. - */ - @SuppressWarnings("unchecked") - public Series

getHeaders() { - Series
headers = (Series
) getAttributes().get(ATTRIBUTE_HEADERS); - if (headers == null) { - headers = new Series
(Header.class); - getAttributes().put(ATTRIBUTE_HEADERS, headers); - } - return headers; - } - - /** - * Returns the callback invoked when an error occurs when sending the message. - * - * @return The callback invoked when an error occurs when sending the message. - */ - public Uniform getOnError() { - return onError; - } - - /** - * Returns the callback invoked after sending the message. - * - * @return The callback invoked after sending the message. - */ - public Uniform getOnSent() { - return onSent; - } - - /** - * Returns the intermediary recipient information.
- *
- * Note that when used with HTTP connectors, this property maps to the "Via" - * headers. - * - * @return The intermediary recipient information. - */ - public List getRecipientsInfo() { - // Lazy initialization with double-check. - List r = this.recipientsInfo; - if (r == null) { - synchronized (this) { - r = this.recipientsInfo; - if (r == null) { - this.recipientsInfo = r = new CopyOnWriteArrayList(); - } - } - } - return r; - } - - /** - * Returns the additional warnings information.
- *
- * Note that when used with HTTP connectors, this property maps to the "Warning" - * headers. - * - * @return The additional warnings information. - */ - public List getWarnings() { - // Lazy initialization with double-check. - List r = this.warnings; - if (r == null) { - synchronized (this) { - r = this.warnings; - if (r == null) { - this.warnings = r = new CopyOnWriteArrayList(); - } - } - } - return r; - } - - /** - * Indicates if the message was or will be exchanged confidentially, for example - * via a SSL-secured connection. - * - * @return True if the message is confidential. - */ - public abstract boolean isConfidential(); - - /** - * Indicates if a content is available and can be sent or received. Several - * conditions must be met: the content must exists and have some available data. - * - * @return True if a content is available and can be sent. - */ - public boolean isEntityAvailable() { - return (getEntity() != null) && getEntity().isAvailable(); - } - - /** - * Releases the message's entity if present. - * - * @see org.restlet.representation.Representation#release() - */ - public void release() { - if (getEntity() != null) { - getEntity().release(); - } - } - - /** - * Sets the modifiable map of attributes. This method clears the current map and - * puts all entries in the parameter map. - * - * @param attributes A map of attributes - */ - public void setAttributes(Map attributes) { - synchronized (getAttributes()) { - if (attributes != getAttributes()) { - getAttributes().clear(); - - if (attributes != null) { - getAttributes().putAll(attributes); - } - } - } - } - - /** - * Sets the cache directives. Note that when used with HTTP connectors, this - * property maps to the "Cache-Control" header. This method clears the current - * list and adds all entries in the parameter list. - * - * @param cacheDirectives The cache directives. - */ - public void setCacheDirectives(List cacheDirectives) { - synchronized (getCacheDirectives()) { - if (cacheDirectives != getCacheDirectives()) { - getCacheDirectives().clear(); - - if (cacheDirectives != null) { - getCacheDirectives().addAll(cacheDirectives); - } - } - } - } - - /** - * Sets the date and time at which the message was originated. - * - * @param date The date and time at which the message was originated. - */ - public void setDate(Date date) { - this.date = date; - } - - /** - * Sets the entity representation. - * - * @param entity The entity representation. - */ - public void setEntity(Representation entity) { - this.entity = entity; - } - - /** - * Sets a textual entity. - * - * @param value The represented string. - * @param mediaType The representation's media type. - */ - public void setEntity(String value, MediaType mediaType) { - setEntity(new StringRepresentation(value, mediaType)); - } - - /** - * Sets the callback invoked when an error occurs when sending the message. - * - * @param onError The callback invoked when an error occurs when sending the - * message. - */ - public void setOnError(Uniform onError) { - this.onError = onError; - } - - /** - * Sets the callback invoked after sending the message. - * - * @param onSentCallback The callback invoked after sending the message. - */ - public void setOnSent(Uniform onSentCallback) { - this.onSent = onSentCallback; - } - - /** - * Sets the modifiable list of intermediary recipients. Note that when used with - * HTTP connectors, this property maps to the "Via" headers. This method clears - * the current list and adds all entries in the parameter list. - * - * @param recipientsInfo A list of intermediary recipients. - */ - public void setRecipientsInfo(List recipientsInfo) { - synchronized (getRecipientsInfo()) { - if (recipientsInfo != getRecipientsInfo()) { - getRecipientsInfo().clear(); - - if (recipientsInfo != null) { - getRecipientsInfo().addAll(recipientsInfo); - } - } - } - } - - /** - * Sets the additional warnings information. Note that when used with HTTP - * connectors, this property maps to the "Warning" headers. This method clears - * the current list and adds all entries in the parameter list. - * - * @param warnings The warnings. - */ - public void setWarnings(List warnings) { - synchronized (getWarnings()) { - if (warnings != getWarnings()) { - getWarnings().clear(); - - if (warnings != null) { - getWarnings().addAll(warnings); - } - } - } - } - + /** The modifiable attributes map. */ + private volatile ConcurrentMap attributes; + + /** The caching directives. */ + private volatile List cacheDirectives; + + /** The date and time at which the message was originated. */ + private volatile Date date; + + /** The payload of the message. */ + private volatile Representation entity; + + /** The optional cached text. */ + private volatile String entityText; + + /** Callback invoked when an error occurs when sending the message. */ + private volatile Uniform onError; + + /** Callback invoked after sending the message. */ + private volatile Uniform onSent; + + /** The intermediary recipients' info. */ + private volatile List recipientsInfo; + + /** The additional warnings' information. */ + private volatile List warnings; + + /** Constructor. */ + public Message() { + this(null); + } + + /** + * Constructor. + * + * @param entity The payload of the message. + */ + public Message(Representation entity) { + this.attributes = null; + this.cacheDirectives = null; + this.date = null; + this.entity = entity; + this.entityText = null; + this.onSent = null; + this.recipientsInfo = null; + this.warnings = null; + } + + /** + * If the entity is transient or its size unknown in advance but available, then the entity is + * wrapped with a {@link org.restlet.representation.BufferingRepresentation}.
+ *
+ * Be careful as this method could create potentially very large byte buffers in memory that + * could impact your application performance. + * + * @see org.restlet.representation.BufferingRepresentation + * @see ClientResource#setRequestEntityBuffering(boolean) + * @see ClientResource#setResponseEntityBuffering(boolean) + */ + public void bufferEntity() { + if ((getEntity() != null) + && (getEntity().isTransient() || getEntity().getSize() == UNKNOWN_SIZE) + && getEntity().isAvailable()) { + setEntity(new org.restlet.representation.BufferingRepresentation(getEntity())); + } + } + + /** + * Asks the underlying connector to immediately flush the network buffers. + * + * @throws IOException + */ + public void flushBuffers() throws IOException {} + + /** + * Returns the modifiable map of attributes that developers can use to save information + * relative to the message. Creates a new instance if no one has been set. This is an easier + * alternative to the creation of a wrapper instance around the whole message.
+ *
+ * In addition, this map is a shared space between the developer and the connectors. In this + * case, it is used to exchange information that is not uniform across all protocols and + * couldn't therefore be directly included in the API. For this purpose, all attribute names + * starting with "org.restlet" are reserved. Currently the following attributes are used: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
list of currently used attributes
Attribute nameClass nameDescription
org.restlet.http.headersorg.restlet.util.Series<org.restlet.engine.header.Header>Server HTTP connectors must provide all request headers, and client HTTP + * connectors must provide all response headers, exactly as they were received. + * In addition, developers can also use this attribute to specify + * non-standard headers that should be added to the request or to the + * response.
org.restlet.https.clientCertificatesList<java.security.cert.Certificate>For requests received via a secure connector, indicates the ordered list + * of client certificates if they are available and accessible.
+ * + *
+ * Most of the standard HTTP headers are directly supported via the Restlet API. Thus, adding + * such HTTP headers is forbidden because it could conflict with the connector's internal + * behavior, limit portability, or prevent future optimizations. The other standard HTTP headers + * (that are not supported) can be added as attributes via the "org.restlet.http.headers" key. + *
+ * + * @return The modifiable attributes map. + */ + public ConcurrentMap getAttributes() { + // Lazy initialization with double-check. + ConcurrentMap r = this.attributes; + if (r == null) { + synchronized (this) { + r = this.attributes; + if (r == null) { + this.attributes = r = new ConcurrentHashMap<>(); + } + } + } + + return this.attributes; + } + + /** + * Returns the cache directives.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Cache-Control" header. + * + * @return The cache directives. + */ + public List getCacheDirectives() { + // Lazy initialization with double-check. + List r = this.cacheDirectives; + if (r == null) { + synchronized (this) { + r = this.cacheDirectives; + if (r == null) { + this.cacheDirectives = r = new CopyOnWriteArrayList<>(); + } + } + } + return r; + } + + /** + * Returns the date and time at which the message was originated. + * + * @return The date and time at which the message was originated. + */ + public Date getDate() { + return date; + } + + /** + * Returns the entity representation. + * + * @return The entity representation. + */ + public Representation getEntity() { + return this.entity; + } + + /** + * Returns the entity as text. This method can be called several times and will always return + * the same text. Note that if the entity is large, this method can result in important memory + * consumption. + * + * @return The entity as text. + */ + public String getEntityAsText() { + if (this.entityText == null) { + try { + this.entityText = (getEntity() == null) ? null : getEntity().getText(); + } catch (java.io.IOException e) { + Context.getCurrentLogger() + .log(java.util.logging.Level.FINE, "Unable to get the entity text.", e); + } + } + + return this.entityText; + } + + /** + * Returns the series of lower-level HTTP headers. Please note that this method should rarely be + * used as most HTTP headers are already surfaced by the Restlet API. The result series can be + * used to deal with HTTP extension headers. + * + * @return The HTTP headers. + */ + @SuppressWarnings("unchecked") + public Series
getHeaders() { + Series
headers = (Series
) getAttributes().get(ATTRIBUTE_HEADERS); + if (headers == null) { + headers = new Series
(Header.class); + getAttributes().put(ATTRIBUTE_HEADERS, headers); + } + return headers; + } + + /** + * Returns the callback invoked when an error occurs when sending the message. + * + * @return The callback invoked when an error occurs when sending the message. + */ + public Uniform getOnError() { + return onError; + } + + /** + * Returns the callback invoked after sending the message. + * + * @return The callback invoked after sending the message. + */ + public Uniform getOnSent() { + return onSent; + } + + /** + * Returns the intermediary recipient information.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Via" headers. + * + * @return The intermediary recipient information. + */ + public List getRecipientsInfo() { + // Lazy initialization with double-check. + List r = this.recipientsInfo; + if (r == null) { + synchronized (this) { + r = this.recipientsInfo; + if (r == null) { + this.recipientsInfo = r = new CopyOnWriteArrayList(); + } + } + } + return r; + } + + /** + * Returns the additional warnings information.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Warning" headers. + * + * @return The additional warnings' information. + */ + public List getWarnings() { + // Lazy initialization with double-check. + List r = this.warnings; + if (r == null) { + synchronized (this) { + r = this.warnings; + if (r == null) { + this.warnings = r = new CopyOnWriteArrayList(); + } + } + } + return r; + } + + /** + * Indicates if the message was or will be exchanged confidentially, for example, via a + * SSL-secured connection. + * + * @return True if the message is confidential. + */ + public abstract boolean isConfidential(); + + /** + * Indicates if a content is available and can be sent or received. Several conditions must be + * met: the content must exist and have some available data. + * + * @return True if a content is available and can be sent. + */ + public boolean isEntityAvailable() { + return (getEntity() != null) && getEntity().isAvailable(); + } + + /** + * Releases the message's entity if present. + * + * @see org.restlet.representation.Representation#release() + */ + public void release() { + if (getEntity() != null) { + getEntity().release(); + } + } + + /** + * Sets the modifiable map of attributes. This method clears the current map and puts all + * entries in the parameter map. + * + * @param attributes A map of attributes + */ + public void setAttributes(Map attributes) { + synchronized (getAttributes()) { + if (attributes != getAttributes()) { + getAttributes().clear(); + + if (attributes != null) { + getAttributes().putAll(attributes); + } + } + } + } + + /** + * Sets the cache directives. Note that when used with HTTP connectors, this property maps to + * the "Cache-Control" header. This method clears the current list and adds all entries in the + * parameter list. + * + * @param cacheDirectives The cache directives. + */ + public void setCacheDirectives(List cacheDirectives) { + synchronized (getCacheDirectives()) { + if (cacheDirectives != getCacheDirectives()) { + getCacheDirectives().clear(); + + if (cacheDirectives != null) { + getCacheDirectives().addAll(cacheDirectives); + } + } + } + } + + /** + * Sets the date and time at which the message was originated. + * + * @param date The date and time at which the message was originated. + */ + public void setDate(Date date) { + this.date = date; + } + + /** + * Sets the entity representation. + * + * @param entity The entity representation. + */ + public void setEntity(Representation entity) { + this.entity = entity; + } + + /** + * Sets a textual entity. + * + * @param value The represented string. + * @param mediaType The representation's media-type. + */ + public void setEntity(String value, MediaType mediaType) { + setEntity(new StringRepresentation(value, mediaType)); + } + + /** + * Sets the callback invoked when an error occurs when sending the message. + * + * @param onError The callback invoked when an error occurs when sending the message. + */ + public void setOnError(Uniform onError) { + this.onError = onError; + } + + /** + * Sets the callback invoked after sending the message. + * + * @param onSentCallback The callback invoked after sending the message. + */ + public void setOnSent(Uniform onSentCallback) { + this.onSent = onSentCallback; + } + + /** + * Sets the modifiable list of intermediary recipients. Note that when used with HTTP + * connectors, this property maps to the "Via" headers. This method clears the current list and + * adds all entries in the parameter list. + * + * @param recipientsInfo A list of intermediary recipients. + */ + public void setRecipientsInfo(List recipientsInfo) { + synchronized (getRecipientsInfo()) { + if (recipientsInfo != getRecipientsInfo()) { + getRecipientsInfo().clear(); + + if (recipientsInfo != null) { + getRecipientsInfo().addAll(recipientsInfo); + } + } + } + } + + /** + * Sets the additional warnings information. Note that when used with HTTP connectors, this + * property maps to the "Warning" headers. This method clears the current list and adds all + * entries in the parameter list. + * + * @param warnings The warnings. + */ + public void setWarnings(List warnings) { + synchronized (getWarnings()) { + if (warnings != getWarnings()) { + getWarnings().clear(); + + if (warnings != null) { + getWarnings().addAll(warnings); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/Request.java b/org.restlet/src/main/java/org/restlet/Request.java index 458a0dae6e..e57b42152a 100644 --- a/org.restlet/src/main/java/org/restlet/Request.java +++ b/org.restlet/src/main/java/org/restlet/Request.java @@ -1,915 +1,908 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; -import org.restlet.data.*; -import org.restlet.representation.Representation; -import org.restlet.util.Series; - import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; +import org.restlet.data.CacheDirective; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.CharacterSet; +import org.restlet.data.ClientInfo; +import org.restlet.data.Conditions; +import org.restlet.data.Cookie; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Preference; +import org.restlet.data.Protocol; +import org.restlet.data.Range; +import org.restlet.data.Reference; +import org.restlet.data.Tag; +import org.restlet.data.Warning; +import org.restlet.representation.Representation; +import org.restlet.util.Series; /** - * Generic request sent by client connectors. It is then received by server - * connectors and processed by {@link Restlet}s. This request can also be - * processed by a chain of Restlets, on both client and server sides. Requests - * are uniform across all types of connectors, protocols and components. - * + * Generic request sent by client connectors. It is then received by server connectors and processed + * by {@link Restlet}s. This request can also be processed by a chain of Restlets, on both client + * and server sides. Requests are uniform across all types of connectors, protocols and components. + * * @see org.restlet.Response * @see org.restlet.Uniform * @author Jerome Louvel */ public class Request extends Message { - /** - * Returns the request associated to the current thread. This is reusing the - * {@link Response#getCurrent()} method.
- *
- * Warning: this method should only be used under duress. You should by default - * prefer obtaining the current context using methods such as - * {@link org.restlet.resource.Resource#getRequest()}. - * - * @return The thread's request. - */ - public static Request getCurrent() { - return (Response.getCurrent() == null) ? null : Response.getCurrent().getRequest(); - } - - /** - * Used when issuing a preflight CORS request to let the origin server knows - * what headers the client is willing to send in future request to this - * resource. - */ - private volatile Set accessControlRequestHeaders; - - /** - * Used when issuing a preflight CORS request to let the origin server knows - * what method the client is willing to send in future request to this resource. - */ - private volatile Method accessControlRequestMethod; - - /** The authentication response sent by a client to an origin server. */ - private volatile ChallengeResponse challengeResponse; - - /** The client-specific information. */ - private volatile ClientInfo clientInfo; - - /** The condition data. */ - private volatile Conditions conditions; - - /** The cookies provided by the client. */ - private volatile Series cookies; - - /** The host reference. */ - private volatile Reference hostRef; - - /** Indicates if the call is loggable. */ - private volatile boolean loggable; - - /** The maximum number of intermediaries. */ - private volatile int maxForwards; - - /** The method. */ - private volatile Method method; - - /** Callback invoked on response reception. */ - private volatile Uniform onResponse; - - /** The original reference. */ - private volatile Reference originalRef; - - /** The protocol. */ - private volatile Protocol protocol; - - /** The authentication response sent by a client to a proxy. */ - private volatile ChallengeResponse proxyChallengeResponse; - - /** The ranges to return from the target resource's representation. */ - private volatile List ranges; - - /** The referrer reference. */ - private volatile Reference referrerRef; - - /** The resource reference. */ - private volatile Reference resourceRef; - - /** The application root reference. */ - private volatile Reference rootRef; - - /** - * Constructor. - */ - public Request() { - this(null, (Reference) null, null); - } - - /** - * Constructor. - * - * @param method The call's method. - * @param resourceRef The resource reference. - */ - public Request(Method method, Reference resourceRef) { - this(method, resourceRef, null); - } - - /** - * Constructor. - * - * @param method The call's method. - * @param resourceRef The resource reference. - * @param entity The entity. - */ - public Request(Method method, Reference resourceRef, Representation entity) { - super(entity); - this.accessControlRequestHeaders = null; - this.accessControlRequestMethod = null; - this.challengeResponse = null; - this.clientInfo = null; - this.conditions = null; - this.cookies = null; - this.hostRef = null; - this.loggable = true; - this.maxForwards = -1; - this.method = method; - this.originalRef = null; - this.onResponse = null; - this.proxyChallengeResponse = null; - this.protocol = null; - this.ranges = null; - this.referrerRef = null; - this.resourceRef = resourceRef; - this.rootRef = null; - } - - /** - * Constructor. - * - * @param method The call's method. - * @param resourceUri The resource URI. - */ - public Request(Method method, String resourceUri) { - this(method, new Reference(resourceUri)); - } - - /** - * Constructor. - * - * @param method The call's method. - * @param resourceUri The resource URI. - * @param entity The entity. - */ - public Request(Method method, String resourceUri, Representation entity) { - this(method, new Reference(resourceUri), entity); - } - - /** - * Copy constructor. - * - * @param request The request to copy. - */ - public Request(Request request) { - this(request.getMethod(), new Reference(request.getResourceRef()), request.getEntity()); - challengeResponse = request.getChallengeResponse(); - - // Copy client info - ClientInfo rci = request.getClientInfo(); - clientInfo = new ClientInfo(); - - for (Preference o : rci.getAcceptedCharacterSets()) { - clientInfo.getAcceptedCharacterSets().add(o); - } - - for (Preference o : rci.getAcceptedEncodings()) { - clientInfo.getAcceptedEncodings().add(o); - } - - for (Preference o : rci.getAcceptedLanguages()) { - clientInfo.getAcceptedLanguages().add(o); - } - - for (Preference o : rci.getAcceptedMediaTypes()) { - clientInfo.getAcceptedMediaTypes().add(o); - } - - clientInfo.setAddress(rci.getAddress()); - clientInfo.setAgent(rci.getAgent()); - - for (String o : rci.getForwardedAddresses()) { - clientInfo.getForwardedAddresses().add(o); - } - - clientInfo.setFrom(rci.getFrom()); - clientInfo.setPort(rci.getPort()); - - clientInfo.setAgentAttributes(rci.getAgentAttributes()); - clientInfo.setAgentProducts(rci.getAgentProducts()); - clientInfo.setAuthenticated(rci.isAuthenticated()); - - for (org.restlet.data.Expectation o : rci.getExpectations()) { - clientInfo.getExpectations().add(o); - } - - for (java.security.Principal o : rci.getPrincipals()) { - clientInfo.getPrincipals().add(o); - } - - for (org.restlet.security.Role o : rci.getRoles()) { - clientInfo.getRoles().add(o); - } - - clientInfo.setUser(rci.getUser()); - - // Copy conditions - conditions = new Conditions(); - - for (Tag o : request.getConditions().getMatch()) { - conditions.getMatch().add(o); - } - - conditions.setModifiedSince(request.getConditions().getModifiedSince()); - - for (Tag o : request.getConditions().getNoneMatch()) { - conditions.getNoneMatch().add(o); - } - - conditions.setRangeDate(request.getConditions().getRangeDate()); - conditions.setRangeTag(request.getConditions().getRangeTag()); - conditions.setUnmodifiedSince(request.getConditions().getUnmodifiedSince()); - - for (Cookie o : request.getCookies()) { - getCookies().add(o); - } - - this.hostRef = request.getHostRef(); - this.maxForwards = request.getMaxForwards(); - this.originalRef = (request.getOriginalRef() == null) ? null : new Reference(request.getOriginalRef()); - this.onResponse = request.getOnResponse(); - this.proxyChallengeResponse = request.getProxyChallengeResponse(); - this.protocol = request.getProtocol(); - - for (Range o : request.getRanges()) { - getRanges().add(o); - } - - this.referrerRef = (request.getReferrerRef() == null) ? null : new Reference(request.getReferrerRef()); - this.rootRef = (request.getRootRef() == null) ? null : request.getRootRef(); - - for (Entry e : request.getAttributes().entrySet()) { - getAttributes().put(e.getKey(), e.getValue()); - } - - for (CacheDirective o : request.getCacheDirectives()) { - getCacheDirectives().add(o); - } - - this.setOnSent(request.getOnSent()); - - for (Warning o : request.getWarnings()) { - getWarnings().add(o); - } - - this.setDate(request.getDate()); - } - - /** - * Ask the connector to attempt to abort the related network connection, for - * example immediately closing the socket. - * - * @return True if the request was aborted. - */ - public boolean abort() { - return false; - } - - /** - * Asks the server connector to immediately commit the given response associated - * to this request, making it ready to be sent back to the client. Note that all - * server connectors don't necessarily support this feature. - * - * @param response The response to commit. - * - */ - public void commit(Response response) { - } - - /** - * Returns the modifiable set of headers the client is willing to send in future - * request to this resource. Used when issuing a preflight CORS request to let - * the origin server knows what headers will be sent later.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Headers" header. - * - * @return The headers the client is willing to send in future request to this - * resource. Useful for CORS support. - */ - public Set getAccessControlRequestHeaders() { - // Lazy initialization with double-check. - Set a = this.accessControlRequestHeaders; - if (a == null) { - synchronized (this) { - a = this.accessControlRequestHeaders; - if (a == null) { - this.accessControlRequestHeaders = a = new CopyOnWriteArraySet(); - } - } - } - return a; - } - - /** - * Returns the method the client is willing to use in future request to this - * resource. Used when issuing a preflight CORS request to let the origin server - * knows what method will be sent later.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Request-Method" header. - * - * @return The method the client is willing to send in future request to this - * resource. Useful for CORS support. - */ - public Method getAccessControlRequestMethod() { - return this.accessControlRequestMethod; - } - - /** - * Returns the authentication response sent by a client to an origin server. - * Note that when used with HTTP connectors, this property maps to the - * "Authorization" header. - * - * @return The authentication response sent by a client to an origin server. - */ - public ChallengeResponse getChallengeResponse() { - return this.challengeResponse; - } - - /** - * Returns the client-specific information. Creates a new instance if no one has - * been set. - * - * @return The client-specific information. - */ - public ClientInfo getClientInfo() { - // Lazy initialization with double-check. - ClientInfo c = this.clientInfo; - if (c == null) { - synchronized (this) { - c = this.clientInfo; - if (c == null) { - this.clientInfo = c = new ClientInfo(); - } - } - } - return c; - } - - /** - * Returns the modifiable conditions applying to this request. Creates a new - * instance if no one has been set. - * - * @return The conditions applying to this call. - */ - public Conditions getConditions() { - // Lazy initialization with double-check. - Conditions c = this.conditions; - if (c == null) { - synchronized (this) { - c = this.conditions; - if (c == null) { - this.conditions = c = new Conditions(); - } - } - } - return c; - } - - /** - * Returns the modifiable series of cookies provided by the client. Creates a - * new instance if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the "Cookie" - * header. - * - * @return The cookies provided by the client. - */ - public Series getCookies() { - // Lazy initialization with double-check. - Series c = this.cookies; - if (c == null) { - synchronized (this) { - c = this.cookies; - if (c == null) { - this.cookies = c = new Series(Cookie.class); - } - } - } - return c; - } - - /** - * Returns the host reference. This may be different from the resourceRef's - * host, for example, for URNs and other URIs that don't contain host - * information.
- *
- * Note that when used with HTTP connectors, this property maps to the "Host" - * header. - * - * @return The host reference. - */ - public Reference getHostRef() { - return this.hostRef; - } - - /** - * Returns the maximum number of intermediaries. - * - * @return The maximum number of intermediaries. - */ - public int getMaxForwards() { - return maxForwards; - } - - /** - * Returns the method. - * - * @return The method. - */ - public Method getMethod() { - return this.method; - } - - /** - * Returns the callback invoked on response reception. If the value is not null, - * then the associated request will be executed asynchronously. - * - * @return The callback invoked on response reception. - */ - public Uniform getOnResponse() { - return onResponse; - } - - /** - * Returns the original reference as requested by the client. Note that this - * property is not used during request routing. See the - * {@link #getResourceRef()} method for details. - * - * @return The original reference. - * @see #getResourceRef() - */ - public Reference getOriginalRef() { - return this.originalRef; - } - - /** - * Returns the protocol used or to be used, if known. - * - * @return The protocol used or to be used. - */ - public Protocol getProtocol() { - Protocol result = this.protocol; - - if ((result == null) && (getResourceRef() != null)) { - // Attempt to guess the protocol to use - // from the target reference scheme - result = getResourceRef().getSchemeProtocol(); - // Fallback: look at base reference scheme - if (result == null) { - result = (getResourceRef().getBaseRef() != null) ? getResourceRef().getBaseRef().getSchemeProtocol() - : null; - } - } - - return result; - } - - /** - * Returns the authentication response sent by a client to a proxy. Note that - * when used with HTTP connectors, this property maps to the - * "Proxy-Authorization" header. - * - * @return The authentication response sent by a client to a proxy. - */ - public ChallengeResponse getProxyChallengeResponse() { - return this.proxyChallengeResponse; - } - - /** - * Returns the ranges to return from the target resource's representation. Note - * that when used with HTTP connectors, this property maps to the "Range" - * header. - * - * @return The ranges to return. - */ - public List getRanges() { - // Lazy initialization with double-check. - List r = this.ranges; - if (r == null) { - synchronized (this) { - r = this.ranges; - if (r == null) { - this.ranges = r = new CopyOnWriteArrayList(); - } - } - } - return r; - } - - /** - * Returns the referrer reference if available. Note that when used with HTTP - * connectors, this property maps to the "Referer" header. - * - * @return The referrer reference. - */ - public Reference getReferrerRef() { - return this.referrerRef; - } - - /** - * Returns the reference of the target resource. This reference is especially - * important during routing, dispatching and resource finding. During such - * processing, its base reference is constantly updated to reflect the reference - * of the parent Restlet or resource and the remaining part of the URI that must - * be routed or analyzed. - * - * If you need to get the URI reference originally requested by the client, then - * you should use the {@link #getOriginalRef()} method instead. Also, note that - * beside the update of its base property, the resource reference can be - * modified during the request processing. - * - * For example, the {@link org.restlet.service.TunnelService} associated to an - * application can extract some special extensions or query parameters and - * replace them by semantically equivalent properties on the request object. - * Therefore, the resource reference can become different from the original - * reference. - * - * Finally, when sending out requests via a dispatcher such as - * {@link Context#getClientDispatcher()} or - * {@link Context#getServerDispatcher()}, if the reference contains URI template - * variables, those variables are automatically resolved using the request's - * attributes. - * - * @return The reference of the target resource. - * @see #getOriginalRef() - * @see #getHostRef() - */ - public Reference getResourceRef() { - return this.resourceRef; - } - - /** - * Returns the application root reference. - * - * @return The application root reference. - */ - public Reference getRootRef() { - return this.rootRef; - } - - /** - * Indicates if the request is asynchronous. The test consist in verifying that - * the {@link #getOnResponse()} method returns a callback object. - * - * @return True if the request is synchronous. - */ - public boolean isAsynchronous() { - return getOnResponse() != null; - } - - /** - * Implemented based on the {@link Protocol#isConfidential()} method for the - * request's protocol returned by {@link #getProtocol()}; - */ - @Override - public boolean isConfidential() { - return (getProtocol() == null) ? false : getProtocol().isConfidential(); - } - - /** - * Indicates if a content is available and can be sent. Several conditions must - * be met: the method must allow the sending of content, the content must exists - * and have some available data. - * - * @return True if a content is available and can be sent. - */ - @Override - public boolean isEntityAvailable() { - if ((Method.GET.equals(getMethod()) || Method.HEAD.equals(getMethod()))) { - return false; - } - - return super.isEntityAvailable(); - } - - /** - * Indicates if an associated response is expected. - * - * @return True if an associated response is expected. - */ - public boolean isExpectingResponse() { - return (getMethod() == null) ? false : getMethod().isReplying(); - } - - /** - * Indicates if the call is loggable - * - * @return True if the call is loggable - */ - public boolean isLoggable() { - return loggable; - } - - /** - * Indicates if the request is synchronous. The test consist in verifying that - * the {@link #getOnResponse()} method returns null. - * - * @return True if the request is synchronous. - */ - public boolean isSynchronous() { - return getOnResponse() == null; - } - - /** - * Sets the set of headers the client is willing to use in future request to - * this resource. Used when issuing a preflight CORS request to let the origin - * server knows what headers will be sent later.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Request-Method" header. - * - * @param accessControlRequestHeaders The set of headers the client is willing - * to send in future request to this - * resource. Useful for CORS support. - */ - public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { - synchronized (getAccessControlRequestHeaders()) { - if (accessControlRequestHeaders != this.accessControlRequestHeaders) { - this.accessControlRequestHeaders.clear(); - - if (accessControlRequestHeaders != null) { - this.accessControlRequestHeaders.addAll(accessControlRequestHeaders); - } - } - } - } - - /** - * Sets the method the client is willing to use in future request to this - * resource. Used when issuing a preflight CORS request to let the origin server - * knows what method will be sent later.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Request-Method" header. - * - * @param accessControlRequestMethod The method the client is willing to send in - * future request to this resource. Useful for - * CORS support. - */ - public void setAccessControlRequestMethod(Method accessControlRequestMethod) { - this.accessControlRequestMethod = accessControlRequestMethod; - } - - /** - * Sets the authentication response sent by a client to an origin server. Note - * that when used with HTTP connectors, this property maps to the - * "Authorization" header. - * - * @param challengeResponse The authentication response sent by a client to an - * origin server. - */ - public void setChallengeResponse(ChallengeResponse challengeResponse) { - this.challengeResponse = challengeResponse; - } - - /** - * Sets the client-specific information. - * - * @param clientInfo The client-specific information. - */ - public void setClientInfo(ClientInfo clientInfo) { - this.clientInfo = clientInfo; - } - - /** - * Sets the conditions applying to this request. - * - * @param conditions The conditions applying to this request. - */ - public void setConditions(Conditions conditions) { - this.conditions = conditions; - } - - /** - * Sets the modifiable series of cookies provided by the client. Note that when - * used with HTTP connectors, this property maps to the "Cookie" header. This - * method clears the current series and adds all entries in the parameter - * series. - * - * @param cookies A series of cookies provided by the client. - */ - public void setCookies(Series cookies) { - synchronized (getCookies()) { - if (cookies != getCookies()) { - if (getCookies() != null) { - getCookies().clear(); - } - - if (cookies != null) { - getCookies().addAll(cookies); - } - } - } - } - - /** - * Sets the host reference. Note that when used with HTTP connectors, this - * property maps to the "Host" header. - * - * @param hostRef The host reference. - */ - public void setHostRef(Reference hostRef) { - this.hostRef = hostRef; - } - - /** - * Sets the host reference using a URI string. Note that when used with HTTP - * connectors, this property maps to the "Host" header. - * - * @param hostUri The host URI. - */ - public void setHostRef(String hostUri) { - setHostRef(new Reference(hostUri)); - } - - /** - * Indicates if the call is loggable - * - * @param loggable True if the call is loggable - */ - public void setLoggable(boolean loggable) { - this.loggable = loggable; - } - - /** - * Sets the maximum number of intermediaries. - * - * @param maxForwards The maximum number of intermediaries. - */ - public void setMaxForwards(int maxForwards) { - this.maxForwards = maxForwards; - } - - /** - * Sets the method called. - * - * @param method The method called. - */ - public void setMethod(Method method) { - this.method = method; - } - - /** - * Sets the callback invoked on response reception. If the value is not null, - * then the associated request will be executed asynchronously. - * - * @param onResponseCallback The callback invoked on response reception. - */ - public void setOnResponse(Uniform onResponseCallback) { - this.onResponse = onResponseCallback; - } - - /** - * Sets the original reference requested by the client. - * - * @param originalRef The original reference. - * @see #getOriginalRef() - */ - public void setOriginalRef(Reference originalRef) { - this.originalRef = originalRef; - } - - /** - * Sets the protocol used or to be used. - * - * @param protocol The protocol used or to be used. - */ - public void setProtocol(Protocol protocol) { - this.protocol = protocol; - } - - /** - * Sets the authentication response sent by a client to a proxy. Note that when - * used with HTTP connectors, this property maps to the "Proxy-Authorization" - * header. - * - * @param challengeResponse The authentication response sent by a client to a - * proxy. - */ - public void setProxyChallengeResponse(ChallengeResponse challengeResponse) { - this.proxyChallengeResponse = challengeResponse; - } - - /** - * Sets the modifiable list of ranges to return from the target resource's - * representation. Note that when used with HTTP connectors, this property maps - * to the "Range" header. This method clears the current list and adds all - * entries in the parameter list. - * - * @param ranges A list of ranges. - */ - public void setRanges(List ranges) { - synchronized (getRanges()) { - if (ranges != getRanges()) { - getRanges().clear(); - - if (ranges != null) { - getRanges().addAll(ranges); - } - } - } - } - - /** - * Sets the referrer reference if available. Note that when used with HTTP - * connectors, this property maps to the "Referer" header. - * - * @param referrerRef The referrer reference. - */ - public void setReferrerRef(Reference referrerRef) { - this.referrerRef = referrerRef; - - // A referrer reference must not include a fragment. - if ((this.referrerRef != null) && (this.referrerRef.getFragment() != null)) { - this.referrerRef.setFragment(null); - } - } - - /** - * Sets the referrer reference if available using an URI string. Note that when - * used with HTTP connectors, this property maps to the "Referer" header. - * - * @param referrerUri The referrer URI. - * @see #setReferrerRef(Reference) - */ - public void setReferrerRef(String referrerUri) { - setReferrerRef(new Reference(referrerUri)); - } - - /** - * Sets the target resource reference. If the reference is relative, it will be - * resolved as an absolute reference. Also, the context's base reference will be - * reset. Finally, the reference will be normalized to ensure a consistent - * handling of the call. - * - * @param resourceRef The resource reference. - * @see #getResourceRef() - */ - public void setResourceRef(Reference resourceRef) { - this.resourceRef = resourceRef; - } - - /** - * Sets the target resource reference using an URI string. Note that the URI can - * be either absolute or relative to the context's base reference. - * - * @param resourceUri The resource URI. - * @see #setResourceRef(Reference) - */ - public void setResourceRef(String resourceUri) { - if (getResourceRef() != null) { - // Allow usage of URIs relative to the current base reference - setResourceRef(new Reference(getResourceRef().getBaseRef(), resourceUri)); - } else { - setResourceRef(new Reference(resourceUri)); - } - } - - /** - * Sets the application root reference. - * - * @param rootRef The application root reference. - */ - public void setRootRef(Reference rootRef) { - this.rootRef = rootRef; - } - - /** - * Displays a synthesis of the request like an HTTP request line. - * - * @return A synthesis of the request like an HTTP request line. - */ - public String toString() { - return ((getMethod() == null) ? "" : getMethod().toString()) + " " - + ((getResourceRef() == null) ? "" : getResourceRef().toString()) + " " - + ((getProtocol() == null) ? "" - : (getProtocol().getName() - + ((getProtocol().getVersion() == null) ? "" : "/" + getProtocol().getVersion()))); - } - + /** + * Returns the request associated with the current thread. This is reusing the {@link + * Response#getCurrent()} method.
+ *
+ * Warning: this method should only be used under duress. You should by default prefer getting + * the current context using methods such as {@link org.restlet.resource.Resource#getRequest()}. + * + * @return The thread's request. + */ + public static Request getCurrent() { + return (Response.getCurrent() == null) ? null : Response.getCurrent().getRequest(); + } + + /** + * Used when issuing a preflight CORS request to let the origin server knows what headers the + * client is willing to send in future request to this resource. + */ + private volatile Set accessControlRequestHeaders; + + /** + * Used when issuing a preflight CORS request to let the origin server knows what method the + * client is willing to send in a future request to this resource. + */ + private volatile Method accessControlRequestMethod; + + /** The authentication response sent by a client to an origin server. */ + private volatile ChallengeResponse challengeResponse; + + /** The client-specific information. */ + private volatile ClientInfo clientInfo; + + /** The condition data. */ + private volatile Conditions conditions; + + /** The cookies provided by the client. */ + private volatile Series cookies; + + /** The host reference. */ + private volatile Reference hostRef; + + /** Indicates if the call is loggable. */ + private volatile boolean loggable; + + /** The maximum number of intermediaries. */ + private volatile int maxForwards; + + /** The method. */ + private volatile Method method; + + /** Callback invoked on response reception. */ + private volatile Uniform onResponse; + + /** The original reference. */ + private volatile Reference originalRef; + + /** The protocol. */ + private volatile Protocol protocol; + + /** The authentication response sent by a client to a proxy. */ + private volatile ChallengeResponse proxyChallengeResponse; + + /** The ranges to return from the target resource's representation. */ + private volatile List ranges; + + /** The referrer reference. */ + private volatile Reference referrerRef; + + /** The resource reference. */ + private volatile Reference resourceRef; + + /** The application root reference. */ + private volatile Reference rootRef; + + /** Constructor. */ + public Request() { + this(null, (Reference) null, null); + } + + /** + * Constructor. + * + * @param method The call's method. + * @param resourceRef The resource reference. + */ + public Request(Method method, Reference resourceRef) { + this(method, resourceRef, null); + } + + /** + * Constructor. + * + * @param method The call's method. + * @param resourceRef The resource reference. + * @param entity The entity. + */ + public Request(Method method, Reference resourceRef, Representation entity) { + super(entity); + this.accessControlRequestHeaders = null; + this.accessControlRequestMethod = null; + this.challengeResponse = null; + this.clientInfo = null; + this.conditions = null; + this.cookies = null; + this.hostRef = null; + this.loggable = true; + this.maxForwards = -1; + this.method = method; + this.originalRef = null; + this.onResponse = null; + this.proxyChallengeResponse = null; + this.protocol = null; + this.ranges = null; + this.referrerRef = null; + this.resourceRef = resourceRef; + this.rootRef = null; + } + + /** + * Constructor. + * + * @param method The call's method. + * @param resourceUri The resource URI. + */ + public Request(Method method, String resourceUri) { + this(method, new Reference(resourceUri)); + } + + /** + * Constructor. + * + * @param method The call's method. + * @param resourceUri The resource URI. + * @param entity The entity. + */ + public Request(Method method, String resourceUri, Representation entity) { + this(method, new Reference(resourceUri), entity); + } + + /** + * Copy constructor. + * + * @param request The request to copy. + */ + public Request(Request request) { + this(request.getMethod(), new Reference(request.getResourceRef()), request.getEntity()); + challengeResponse = request.getChallengeResponse(); + + // Copy client info + ClientInfo rci = request.getClientInfo(); + clientInfo = new ClientInfo(); + + for (Preference o : rci.getAcceptedCharacterSets()) { + clientInfo.getAcceptedCharacterSets().add(o); + } + + for (Preference o : rci.getAcceptedEncodings()) { + clientInfo.getAcceptedEncodings().add(o); + } + + for (Preference o : rci.getAcceptedLanguages()) { + clientInfo.getAcceptedLanguages().add(o); + } + + for (Preference o : rci.getAcceptedMediaTypes()) { + clientInfo.getAcceptedMediaTypes().add(o); + } + + clientInfo.setAddress(rci.getAddress()); + clientInfo.setAgent(rci.getAgent()); + + for (String o : rci.getForwardedAddresses()) { + clientInfo.getForwardedAddresses().add(o); + } + + clientInfo.setFrom(rci.getFrom()); + clientInfo.setPort(rci.getPort()); + + clientInfo.setAgentAttributes(rci.getAgentAttributes()); + clientInfo.setAgentProducts(rci.getAgentProducts()); + clientInfo.setAuthenticated(rci.isAuthenticated()); + + for (org.restlet.data.Expectation o : rci.getExpectations()) { + clientInfo.getExpectations().add(o); + } + + for (java.security.Principal o : rci.getPrincipals()) { + clientInfo.getPrincipals().add(o); + } + + for (org.restlet.security.Role o : rci.getRoles()) { + clientInfo.getRoles().add(o); + } + + clientInfo.setUser(rci.getUser()); + + // Copy conditions + conditions = new Conditions(); + + for (Tag o : request.getConditions().getMatch()) { + conditions.getMatch().add(o); + } + + conditions.setModifiedSince(request.getConditions().getModifiedSince()); + + for (Tag o : request.getConditions().getNoneMatch()) { + conditions.getNoneMatch().add(o); + } + + conditions.setRangeDate(request.getConditions().getRangeDate()); + conditions.setRangeTag(request.getConditions().getRangeTag()); + conditions.setUnmodifiedSince(request.getConditions().getUnmodifiedSince()); + + for (Cookie o : request.getCookies()) { + getCookies().add(o); + } + + this.hostRef = request.getHostRef(); + this.maxForwards = request.getMaxForwards(); + this.originalRef = + (request.getOriginalRef() == null) ? null : new Reference(request.getOriginalRef()); + this.onResponse = request.getOnResponse(); + this.proxyChallengeResponse = request.getProxyChallengeResponse(); + this.protocol = request.getProtocol(); + + for (Range o : request.getRanges()) { + getRanges().add(o); + } + + this.referrerRef = + (request.getReferrerRef() == null) ? null : new Reference(request.getReferrerRef()); + this.rootRef = (request.getRootRef() == null) ? null : request.getRootRef(); + + for (Entry e : request.getAttributes().entrySet()) { + getAttributes().put(e.getKey(), e.getValue()); + } + + for (CacheDirective o : request.getCacheDirectives()) { + getCacheDirectives().add(o); + } + + this.setOnSent(request.getOnSent()); + + for (Warning o : request.getWarnings()) { + getWarnings().add(o); + } + + this.setDate(request.getDate()); + } + + /** + * Ask the connector to attempt to abort the related network connection, for example immediately + * closing the socket. + * + * @return True if the request was aborted. + */ + public boolean abort() { + return false; + } + + /** + * Asks the server connector to immediately commit the given response associated with this + * request, making it ready to be sent back to the client. Note that all server connectors don't + * necessarily support this feature. + * + * @param response The response to commit. + */ + public void commit(Response response) {} + + /** + * Returns the modifiable set of headers the client is willing to send in future request to this + * resource. Used when issuing a preflight CORS request to let the origin server know what + * headers will be sent later.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Headers" header. + * + * @return The headers the client is willing to send in future request to this resource. Useful + * for CORS support. + */ + public Set getAccessControlRequestHeaders() { + // Lazy initialization with double-check. + Set a = this.accessControlRequestHeaders; + if (a == null) { + synchronized (this) { + a = this.accessControlRequestHeaders; + if (a == null) { + this.accessControlRequestHeaders = a = new CopyOnWriteArraySet(); + } + } + } + return a; + } + + /** + * Returns the method the client is willing to use in future request to this resource. Used when + * issuing a preflight CORS request to let the origin server know what method will be sent + * later.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Request-Method" header. + * + * @return The method the client is willing to send in a future request to this resource. Useful + * for CORS support. + */ + public Method getAccessControlRequestMethod() { + return this.accessControlRequestMethod; + } + + /** + * Returns the authentication response sent by a client to an origin server. Note that when used + * with HTTP connectors, this property maps to the "Authorization" header. + * + * @return The authentication response sent by a client to an origin server. + */ + public ChallengeResponse getChallengeResponse() { + return this.challengeResponse; + } + + /** + * Returns the client-specific information. Creates a new instance if no one has been set. + * + * @return The client-specific information. + */ + public ClientInfo getClientInfo() { + // Lazy initialization with double-check. + ClientInfo c = this.clientInfo; + if (c == null) { + synchronized (this) { + c = this.clientInfo; + if (c == null) { + this.clientInfo = c = new ClientInfo(); + } + } + } + return c; + } + + /** + * Returns the modifiable conditions applying to this request. Creates a new instance if no one + * has been set. + * + * @return The conditions applying to this call. + */ + public Conditions getConditions() { + // Lazy initialization with double-check. + Conditions c = this.conditions; + if (c == null) { + synchronized (this) { + c = this.conditions; + if (c == null) { + this.conditions = c = new Conditions(); + } + } + } + return c; + } + + /** + * Returns the modifiable series of cookies provided by the client. Creates a new instance if no + * one has been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Cookie" header. + * + * @return The cookies provided by the client. + */ + public Series getCookies() { + // Lazy initialization with double-check. + Series c = this.cookies; + if (c == null) { + synchronized (this) { + c = this.cookies; + if (c == null) { + this.cookies = c = new Series<>(Cookie.class); + } + } + } + return c; + } + + /** + * Returns the host reference. This may be different from the resourceRef's host, for example, + * for URNs and other URIs that don't contain host information.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Host" header. + * + * @return The host reference. + */ + public Reference getHostRef() { + return this.hostRef; + } + + /** + * Returns the maximum number of intermediaries. + * + * @return The maximum number of intermediaries. + */ + public int getMaxForwards() { + return maxForwards; + } + + /** + * Returns the method. + * + * @return The method. + */ + public Method getMethod() { + return this.method; + } + + /** + * Returns the callback invoked on response reception. If the value is not null, then the + * associated request will be executed asynchronously. + * + * @return The callback invoked on response reception. + */ + public Uniform getOnResponse() { + return onResponse; + } + + /** + * Returns the original reference as requested by the client. Note that this property is not + * used during request routing. See the {@link #getResourceRef()} method for details. + * + * @return The original reference. + * @see #getResourceRef() + */ + public Reference getOriginalRef() { + return this.originalRef; + } + + /** + * Returns the protocol used or to be used, if known. + * + * @return The protocol used or to be used. + */ + public Protocol getProtocol() { + Protocol result = this.protocol; + + if ((result == null) && (getResourceRef() != null)) { + // Attempt to guess the protocol to use + // from the target reference scheme + result = getResourceRef().getSchemeProtocol(); + // Fallback: look at a base reference scheme + if (result == null) { + result = + (getResourceRef().getBaseRef() != null) + ? getResourceRef().getBaseRef().getSchemeProtocol() + : null; + } + } + + return result; + } + + /** + * Returns the authentication response sent by a client to a proxy. Note that when used with + * HTTP connectors, this property maps to the "Proxy-Authorization" header. + * + * @return The authentication response sent by a client to a proxy. + */ + public ChallengeResponse getProxyChallengeResponse() { + return this.proxyChallengeResponse; + } + + /** + * Returns the ranges to return from the target resource's representation. Note that when used + * with HTTP connectors, this property maps to the "Range" header. + * + * @return The ranges to return. + */ + public List getRanges() { + // Lazy initialization with double-check. + List r = this.ranges; + if (r == null) { + synchronized (this) { + r = this.ranges; + if (r == null) { + this.ranges = r = new CopyOnWriteArrayList(); + } + } + } + return r; + } + + /** + * Returns the referrer reference if available. Note that when used with HTTP connectors, this + * property maps to the "Referer" header. + * + * @return The referrer reference. + */ + public Reference getReferrerRef() { + return this.referrerRef; + } + + /** + * Returns the reference of the target resource. This reference is especially important during + * routing, dispatching, and resource finding. During such processing, its base reference is + * constantly updated to reflect the reference of the parent Restlet or resource and the + * remaining part of the URI that must be routed or analyzed. + * + *

If you need to get the URI reference originally requested by the client, then you should + * use the {@link #getOriginalRef()} method instead. Also, note that besides the update of its + * base property, the resource reference can be modified during the request processing. + * + *

For example, the {@link org.restlet.service.TunnelService} associated with an application + * can extract some special extensions or query parameters and replace them by semantically + * equivalent properties on the request object. Therefore, the resource reference can become + * different from the original reference. + * + *

Finally, when sending out requests via a dispatcher such as {@link + * Context#getClientDispatcher()} or {@link Context#getServerDispatcher()}, if the reference + * contains URI template variables, those variables are automatically resolved using the + * request's attributes. + * + * @return The reference of the target resource. + * @see #getOriginalRef() + * @see #getHostRef() + */ + public Reference getResourceRef() { + return this.resourceRef; + } + + /** + * Returns the application root reference. + * + * @return The application root reference. + */ + public Reference getRootRef() { + return this.rootRef; + } + + /** + * Indicates if the request is asynchronous. The test consist in verifying that the {@link + * #getOnResponse()} method returns a callback object. + * + * @return True if the request is synchronous. + */ + public boolean isAsynchronous() { + return getOnResponse() != null; + } + + /** + * Implemented based on the {@link Protocol#isConfidential()} method for the request's protocol + * returned by {@link #getProtocol()}; + */ + @Override + public boolean isConfidential() { + return (getProtocol() == null) ? false : getProtocol().isConfidential(); + } + + /** + * Indicates if a content is available and can be sent. Several conditions must be met: the + * method must allow the sending of content, the content must exist and have some available + * data. + * + * @return True if a content is available and can be sent. + */ + @Override + public boolean isEntityAvailable() { + if ((Method.GET.equals(getMethod()) || Method.HEAD.equals(getMethod()))) { + return false; + } + + return super.isEntityAvailable(); + } + + /** + * Indicates if an associated response is expected. + * + * @return True if an associated response is expected. + */ + public boolean isExpectingResponse() { + return (getMethod() == null) ? false : getMethod().isReplying(); + } + + /** + * Indicates if the call is loggable + * + * @return True if the call is loggable + */ + public boolean isLoggable() { + return loggable; + } + + /** + * Indicates if the request is synchronous. The test consist in verifying that the {@link + * #getOnResponse()} method returns null. + * + * @return True if the request is synchronous. + */ + public boolean isSynchronous() { + return getOnResponse() == null; + } + + /** + * Sets the set of headers the client is willing to use in future request to this resource. Used + * when issuing a preflight CORS request to let the origin server know what headers will be + * sent later.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Request-Method" header. + * + * @param accessControlRequestHeaders The set of headers the client is willing to send in a future + * request to this resource. Useful for CORS support. + */ + public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { + synchronized (getAccessControlRequestHeaders()) { + if (accessControlRequestHeaders != this.accessControlRequestHeaders) { + this.accessControlRequestHeaders.clear(); + + if (accessControlRequestHeaders != null) { + this.accessControlRequestHeaders.addAll(accessControlRequestHeaders); + } + } + } + } + + /** + * Sets the method the client is willing to use in future request to this resource. Used when + * issuing a preflight CORS request to let the origin server know what method will be sent + * later.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Request-Method" header. + * + * @param accessControlRequestMethod The method the client is willing to send in future request + * to this resource. Useful for CORS support. + */ + public void setAccessControlRequestMethod(Method accessControlRequestMethod) { + this.accessControlRequestMethod = accessControlRequestMethod; + } + + /** + * Sets the authentication response sent by a client to an origin server. Note that when used + * with HTTP connectors, this property maps to the "Authorization" header. + * + * @param challengeResponse The authentication response sent by a client to an origin server. + */ + public void setChallengeResponse(ChallengeResponse challengeResponse) { + this.challengeResponse = challengeResponse; + } + + /** + * Sets the client-specific information. + * + * @param clientInfo The client-specific information. + */ + public void setClientInfo(ClientInfo clientInfo) { + this.clientInfo = clientInfo; + } + + /** + * Sets the conditions applying to this request. + * + * @param conditions The conditions applying to this request. + */ + public void setConditions(Conditions conditions) { + this.conditions = conditions; + } + + /** + * Sets the modifiable series of cookies provided by the client. Note that when used with HTTP + * connectors, this property maps to the "Cookie" header. This method clears the current series + * and adds all entries in the parameter series. + * + * @param cookies A series of cookies provided by the client. + */ + public void setCookies(Series cookies) { + synchronized (getCookies()) { + if (cookies != getCookies()) { + if (getCookies() != null) { + getCookies().clear(); + } + + if (cookies != null) { + getCookies().addAll(cookies); + } + } + } + } + + /** + * Sets the host reference. Note that when used with HTTP connectors, this property maps to the + * "Host" header. + * + * @param hostRef The host reference. + */ + public void setHostRef(Reference hostRef) { + this.hostRef = hostRef; + } + + /** + * Sets the host reference using a URI string. Note that when used with HTTP connectors, this + * property maps to the "Host" header. + * + * @param hostUri The host URI. + */ + public void setHostRef(String hostUri) { + setHostRef(new Reference(hostUri)); + } + + /** + * Indicates if the call is loggable + * + * @param loggable True if the call is loggable + */ + public void setLoggable(boolean loggable) { + this.loggable = loggable; + } + + /** + * Sets the maximum number of intermediaries. + * + * @param maxForwards The maximum number of intermediaries. + */ + public void setMaxForwards(int maxForwards) { + this.maxForwards = maxForwards; + } + + /** + * Sets the method called. + * + * @param method The method called. + */ + public void setMethod(Method method) { + this.method = method; + } + + /** + * Sets the callback invoked on response reception. If the value is not null, then the + * associated request will be executed asynchronously. + * + * @param onResponseCallback The callback invoked on response reception. + */ + public void setOnResponse(Uniform onResponseCallback) { + this.onResponse = onResponseCallback; + } + + /** + * Sets the original reference requested by the client. + * + * @param originalRef The original reference. + * @see #getOriginalRef() + */ + public void setOriginalRef(Reference originalRef) { + this.originalRef = originalRef; + } + + /** + * Sets the protocol used or to be used. + * + * @param protocol The protocol used or to be used. + */ + public void setProtocol(Protocol protocol) { + this.protocol = protocol; + } + + /** + * Sets the authentication response sent by a client to a proxy. Note that when used with HTTP + * connectors, this property maps to the "Proxy-Authorization" header. + * + * @param challengeResponse The authentication response sent by a client to a proxy. + */ + public void setProxyChallengeResponse(ChallengeResponse challengeResponse) { + this.proxyChallengeResponse = challengeResponse; + } + + /** + * Sets the modifiable list of ranges to return from the target resource's representation. Note + * that when used with HTTP connectors, this property maps to the "Range" header. This method + * clears the current list and adds all entries in the parameter list. + * + * @param ranges A list of ranges. + */ + public void setRanges(List ranges) { + synchronized (getRanges()) { + if (ranges != getRanges()) { + getRanges().clear(); + + if (ranges != null) { + getRanges().addAll(ranges); + } + } + } + } + + /** + * Sets the referrer reference if available. Note that when used with HTTP connectors, this + * property maps to the "Referer" header. + * + * @param referrerRef The referrer reference. + */ + public void setReferrerRef(Reference referrerRef) { + this.referrerRef = referrerRef; + + // A referrer reference must not include a fragment. + if ((this.referrerRef != null) && (this.referrerRef.getFragment() != null)) { + this.referrerRef.setFragment(null); + } + } + + /** + * Sets the referrer reference if available using a URI string. Note that when used with HTTP + * connectors, this property maps to the "Referer" header. + * + * @param referrerUri The referrer URI. + * @see #setReferrerRef(Reference) + */ + public void setReferrerRef(String referrerUri) { + setReferrerRef(new Reference(referrerUri)); + } + + /** + * Sets the target resource reference. If the reference is relative, it will be resolved as an + * absolute reference. Also, the context's base reference will be reset. Finally, the reference + * will be normalized to ensure a consistent handling of the call. + * + * @param resourceRef The resource reference. + * @see #getResourceRef() + */ + public void setResourceRef(Reference resourceRef) { + this.resourceRef = resourceRef; + } + + /** + * Sets the target resource reference using a URI string. Note that the URI can be either + * absolute or relative to the context's base reference. + * + * @param resourceUri The resource URI. + * @see #setResourceRef(Reference) + */ + public void setResourceRef(String resourceUri) { + if (getResourceRef() != null) { + // Allow usage of URIs relative to the current base reference + setResourceRef(new Reference(getResourceRef().getBaseRef(), resourceUri)); + } else { + setResourceRef(new Reference(resourceUri)); + } + } + + /** + * Sets the application root reference. + * + * @param rootRef The application root reference. + */ + public void setRootRef(Reference rootRef) { + this.rootRef = rootRef; + } + + /** + * Displays a synthesis of the request like an HTTP request line. + * + * @return A synthesis of the request like an HTTP request line. + */ + public String toString() { + return ((getMethod() == null) ? "" : getMethod().toString()) + + " " + + ((getResourceRef() == null) ? "" : getResourceRef().toString()) + + " " + + ((getProtocol() == null) + ? "" + : (getProtocol().getName() + + ((getProtocol().getVersion() == null) + ? "" + : "/" + getProtocol().getVersion()))); + } } diff --git a/org.restlet/src/main/java/org/restlet/Response.java b/org.restlet/src/main/java/org/restlet/Response.java index 7d85e33162..a9f1e12261 100644 --- a/org.restlet/src/main/java/org/restlet/Response.java +++ b/org.restlet/src/main/java/org/restlet/Response.java @@ -1,1024 +1,993 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; -import org.restlet.data.*; -import org.restlet.util.Series; - import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; +import org.restlet.data.AuthenticationInfo; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.CookieSetting; +import org.restlet.data.Dimension; +import org.restlet.data.Method; +import org.restlet.data.Reference; +import org.restlet.data.ServerInfo; +import org.restlet.data.Status; +import org.restlet.util.Series; /** - * Generic response sent by server connectors. It is then received by client - * connectors. Responses are uniform across all types of connectors, protocols - * and components. - * + * Generic response sent by server connectors. It is then received by client connectors. Responses + * are uniform across all types of connectors, protocols and components. + * * @see org.restlet.Request * @see org.restlet.Uniform * @author Jerome Louvel */ public class Response extends Message { - private static final ThreadLocal CURRENT = new ThreadLocal(); - - /** - * Returns the response associated to the current thread. - * - * Warning: this method should only be used under duress. You should by default - * prefer obtaining the current context using methods such as - * {@link org.restlet.resource.Resource#getResponse()}. - * - * This variable is stored internally as a thread local variable and updated - * each time a call is handled by a Restlet via the - * {@link Restlet#handle(org.restlet.Request, org.restlet.Response)} method. - * - * @return The current context. - */ - public static Response getCurrent() { - return CURRENT.get(); - } - - /** - * Sets the response associated with the current thread. - * - * @param response The thread's response. - */ - public static void setCurrent(Response response) { - CURRENT.set(response); - } - - /** - * When used as part of a response to a preflight CORS request, this indicates - * whether or not the actual request can be made using credentials. - */ - private volatile Boolean accessControlAllowCredentials; - - /** - * When used as part of a response to a preflight CORS request, this lists the - * headers allowed by the actual request on the current resource. - */ - private volatile Set accessControlAllowHeaders; - - /** - * When used as part of a response to a preflight CORS request, this lists the - * methods allowed by the actual request on the current resource. - */ - private volatile Set accessControlAllowMethods; - - /** - * When used in the context of CORS support, it specifies the URI an origin - * server allows for the requested resource. Use "*" as a wildcard character. - */ - private volatile String accessControlAllowOrigin; - - /** - * The whitelist of headers an origin server allows for the requested resource. - */ - private volatile Set accessControlExposeHeaders; - - /** - * When used in the context of CORS support, it indicates how long the results - * of a preflight request can be cached in a preflight result cache. - */ - private volatile int accessControlMaxAge; - - /** - * Estimated amount of time since a response was generated or revalidated by the - * origin server. - */ - private volatile int age; - - /** The set of methods allowed on the requested resource. */ - private volatile Set allowedMethods; - - /** - * The authentication information sent by an origin server to a client in the - * case of a successful authentication attempt. - */ - private volatile AuthenticationInfo authenticationInfo; - - /** Indicates if the response should be automatically committed. */ - private volatile boolean autoCommitting; - - /** The authentication requests sent by an origin server to a client. */ - private volatile List challengeRequests; - - /** Indicates if the response has been committed. */ - private volatile boolean committed; - - /** The cookie settings provided by the server. */ - private volatile Series cookieSettings; - - /** The set of dimensions on which the response entity may vary. */ - private volatile Set dimensions; - - /** The reference used for redirections or creations. */ - private volatile Reference locationRef; - - /** The authentication requests sent by a proxy to a client. */ - private volatile List proxyChallengeRequests; - - /** The associated request. */ - private volatile Request request; - - /** - * Indicates how long the service is expected to be unavailable to the - * requesting client. - */ - private volatile Date retryAfter; - - /** The server-specific information. */ - private volatile ServerInfo serverInfo; - - /** The status. */ - private volatile Status status; - - /** - * Constructor. - * - * @param request The request associated to this response. - */ - public Response(Request request) { - this.age = 0; - this.accessControlAllowCredentials = null; - this.accessControlAllowHeaders = null; - this.accessControlAllowMethods = null; - this.accessControlAllowOrigin = null; - this.accessControlExposeHeaders = null; - this.allowedMethods = null; - this.autoCommitting = true; - this.challengeRequests = null; - this.cookieSettings = null; - this.committed = false; - this.dimensions = null; - this.locationRef = null; - this.proxyChallengeRequests = null; - this.request = request; - this.retryAfter = null; - this.serverInfo = null; - this.status = Status.SUCCESS_OK; - } - - /** - * Ask the connector to abort the related network connection, for example - * immediately closing the socket. - */ - public void abort() { - getRequest().abort(); - } - - /** - * Asks the server connector to immediately commit the given response, making it - * ready to be sent back to the client. Note that all server connectors don't - * necessarily support this feature.
- *
- * When the response is in autoCommit mode (see related property), then calling - * this method isn't necessary. Also, be aware that committing the response - * doesn't necessarily means that is will be immediately be written on the - * network as some buffering can occurs. If you want to ensure that response - * buffers are flushed,
- *
- * Note that this calls back {@link Request#commit(Response)} on the parent - * request which holds the link with the underlying network connection. - */ - public void commit() { - getRequest().commit(this); - } - - /** - * Asks the server connector to immediately flush the network buffers. Note that - * this calls back {@link Request#flushBuffers()} on the parent request which - * holds the link with the underlying network connection. - * - * @throws IOException - */ - @Override - public void flushBuffers() throws IOException { - getRequest().flushBuffers(); - } - - /** - * When used as part of a response to a preflight CORS request, this indicates - * whether or not the actual request can be made using credentials.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Credentials" header. - * - * @return True if the requested resource allows credential. - */ - public Boolean getAccessControlAllowCredentials() { - return this.accessControlAllowCredentials; - } - - /** - * Returns the modifiable set of headers allowed by the actual request on the - * current resource when used as part of a response to a preflight CORS - * request.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Headers" header. - * - * @return The set of headers allowed by the actual request on the current - * resource. - */ - public Set getAccessControlAllowHeaders() { - // Lazy initialization with double-check. - Set a = this.accessControlAllowHeaders; - if (a == null) { - synchronized (this) { - a = this.accessControlAllowHeaders; - if (a == null) { - this.accessControlAllowHeaders = a = new CopyOnWriteArraySet(); - } - } - } - return a; - } - - /** - * Returns the modifiable set of methods allowed by the actual request on the - * current resource when used as part of a response to a preflight CORS - * request
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Methods" header. - * - * @return The set of methods allowed by the actual request on the current - * resource. - */ - public Set getAccessControlAllowMethods() { - // Lazy initialization with double-check. - Set a = this.accessControlAllowMethods; - if (a == null) { - synchronized (this) { - a = this.accessControlAllowMethods; - if (a == null) { - this.accessControlAllowMethods = a = new CopyOnWriteArraySet(); - } - } - } - return a; - } - - /** - * When used in the context of CORS support, it returns the URI an origin server - * allows for the requested resource. Use "*" as a wildcard character.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Origin" header. - * - * @return The origin allowed by the requested resource. - */ - public String getAccessControlAllowOrigin() { - return this.accessControlAllowOrigin; - } - - /** - * Returns a modifiable whitelist of headers an origin server allows for the - * requested resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Expose-Headers" header. - * - * @return The set of headers an origin server allows for the requested - * resource. - */ - public Set getAccessControlExposeHeaders() { - // Lazy initialization with double-check. - Set a = this.accessControlExposeHeaders; - if (a == null) { - synchronized (this) { - a = this.accessControlExposeHeaders; - if (a == null) { - this.accessControlExposeHeaders = a = new CopyOnWriteArraySet(); - } - } - } - return a; - } - - /** - * Indicates how long the results of a preflight CORS request can be cached in a - * preflight result cache.
- * In case of a negative value, the results of a preflight request is not meant - * to be cached.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Max-Age" header. - * - * @return Indicates how long the results of a preflight request can be cached - * in a preflight result cache. - */ - public int getAccessControlMaxAge() { - return accessControlMaxAge; - } - - /** - * Returns the estimated amount of time since a response was generated or - * revalidated by the origin server. Origin servers should leave the 0 default - * value. Only caches are expected to set this property.
- *
- * Note that when used with HTTP connectors, this property maps to the "Age" - * header. - * - * @return The response age. - */ - public int getAge() { - return age; - } - - /** - * Returns the modifiable set of methods allowed on the requested resource. This - * property only has to be updated when a status CLIENT_ERROR_METHOD_NOT_ALLOWED - * is set. Creates a new instance if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the "Allow" - * header. - * - * @return The set of allowed methods. - */ - public Set getAllowedMethods() { - // Lazy initialization with double-check. - Set a = this.allowedMethods; - if (a == null) { - synchronized (this) { - a = this.allowedMethods; - if (a == null) { - this.allowedMethods = a = new CopyOnWriteArraySet(); - } - } - } - return a; - } - - /** - * Returns information sent by an origin server related to an successful - * authentication attempt. If none is available, null is returned.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Authentication-Info" header. - * - * @return The authentication information provided by the server. - */ - public AuthenticationInfo getAuthenticationInfo() { - return this.authenticationInfo; - } - - /** - * Returns the list of authentication requests sent by an origin server to a - * client. If none is available, an empty list is returned.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "WWW-Authenticate" header. - * - * @return The list of authentication requests. - */ - public List getChallengeRequests() { - // Lazy initialization with double-check. - List cr = this.challengeRequests; - if (cr == null) { - synchronized (this) { - cr = this.challengeRequests; - if (cr == null) { - this.challengeRequests = cr = new CopyOnWriteArrayList(); - } - } - } - return cr; - } - - /** - * Returns the modifiable series of cookie settings provided by the server. - * Creates a new instance if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Set-Cookie" and "Set-Cookie2" headers. - * - * @return The cookie settings provided by the server. - */ - public Series getCookieSettings() { - // Lazy initialization with double-check. - Series c = this.cookieSettings; - if (c == null) { - synchronized (this) { - c = this.cookieSettings; - if (c == null) { - this.cookieSettings = c = new Series(CookieSetting.class); - } - } - } - return c; - } - - /** - * Returns the modifiable set of selecting dimensions on which the response - * entity may vary. If some server-side content negotiation is done, this set - * should be properly updated, other it can be left empty. Creates a new - * instance if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the "Vary" - * header. - * - * @return The set of dimensions on which the response entity may vary. - */ - public Set getDimensions() { - if (this.dimensions == null) { - this.dimensions = new CopyOnWriteArraySet(); - } - return this.dimensions; - } - - /** - * Returns the location reference. This is the reference that the client should - * follow for redirections or resource creations.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Location" header. - * - * @return The redirection reference. - */ - public Reference getLocationRef() { - return this.locationRef; - } - - /** - * Returns the list of authentication requests sent by an origin server to a - * client. If none is available, an empty list is returned.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Proxy-Authenticate" header. - * - * @return The list of authentication requests. - */ - public List getProxyChallengeRequests() { - // Lazy initialization with double-check. - List cr = this.proxyChallengeRequests; - if (cr == null) { - synchronized (this) { - cr = this.proxyChallengeRequests; - if (cr == null) { - this.proxyChallengeRequests = cr = new CopyOnWriteArrayList(); - } - } - } - return cr; - } - - /** - * Returns the associated request - * - * @return The associated request - */ - public Request getRequest() { - return this.request; - } - - /** - * Indicates how long the service is expected to be unavailable to the - * requesting client. Default value is null.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Retry-After" header. - * - * @return Date after with a retry attempt could occur. - */ - public Date getRetryAfter() { - return retryAfter; - } - - /** - * Returns the server-specific information. Creates a new instance if no one has - * been set. - * - * @return The server-specific information. - */ - public ServerInfo getServerInfo() { - // Lazy initialization with double-check. - ServerInfo s = this.serverInfo; - if (s == null) { - synchronized (this) { - s = this.serverInfo; - if (s == null) { - this.serverInfo = s = new ServerInfo(); - } - } - } - return s; - } - - /** - * Returns the status. - * - * @return The status. - */ - public Status getStatus() { - return this.status; - } - - /** - * Indicates if the response should be automatically committed. When processing - * a request on the server-side, setting this property to 'false' let you ask to - * the server connector to wait before sending the response back to the client - * when the initial calling thread returns. This will let you do further updates - * to the response and manually calling {@link #commit()} later on, using - * another thread. - * - * @return True if the response should be automatically committed. - */ - public boolean isAutoCommitting() { - return autoCommitting; - } - - /** - * Indicates if the response has already been committed. - * - * @return True if the response has already been committed. - */ - public boolean isCommitted() { - return committed; - } - - @Override - public boolean isConfidential() { - return getRequest().isConfidential(); - } - - /** - * Indicates if the response is final or provisional. It relies on the - * {@link Status#isInformational()} method. - * - * @return True if the response is final. - */ - public boolean isFinal() { - return !getStatus().isInformational(); - } - - /** - * Indicates if the response is provisional or final. It relies on the - * {@link Status#isInformational()} method. - * - * @return True if the response is provisional. - */ - public boolean isProvisional() { - return getStatus().isInformational(); - } - - /** - * Permanently redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetRef The target URI reference. - */ - public void redirectPermanent(Reference targetRef) { - setLocationRef(targetRef); - setStatus(Status.REDIRECTION_PERMANENT); - } - - /** - * Permanently redirects the client to a target URI. The client is expected to - * reuse the same method for the new request.
- *
- * If you pass a relative target URI, it will be resolved with the current base - * reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}. - * - * @param targetUri The target URI. - */ - public void redirectPermanent(String targetUri) { - setLocationRef(targetUri); - setStatus(Status.REDIRECTION_PERMANENT); - } - - /** - * Redirects the client to a different URI that SHOULD be retrieved using a GET - * method on that resource. This method exists primarily to allow the output of - * a POST-activated script to redirect the user agent to a selected resource. - * The new URI is not a substitute reference for the originally requested - * resource. - * - * @param targetRef The target reference. - */ - public void redirectSeeOther(Reference targetRef) { - setLocationRef(targetRef); - setStatus(Status.REDIRECTION_SEE_OTHER); - } - - /** - * Redirects the client to a different URI that SHOULD be retrieved using a GET - * method on that resource. This method exists primarily to allow the output of - * a POST-activated script to redirect the user agent to a selected resource. - * The new URI is not a substitute reference for the originally requested - * resource.
- *
- * If you pass a relative target URI, it will be resolved with the current base - * reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}. - * - * @param targetUri The target URI. - */ - public void redirectSeeOther(String targetUri) { - setLocationRef(targetUri); - setStatus(Status.REDIRECTION_SEE_OTHER); - } - - /** - * Temporarily redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetRef The target reference. - */ - public void redirectTemporary(Reference targetRef) { - setLocationRef(targetRef); - setStatus(Status.REDIRECTION_TEMPORARY); - } - - /** - * Temporarily redirects the client to a target URI. The client is expected to - * reuse the same method for the new request.
- *
- * If you pass a relative target URI, it will be resolved with the current base - * reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}. - * - * @param targetUri The target URI. - */ - public void redirectTemporary(String targetUri) { - setLocationRef(targetUri); - setStatus(Status.REDIRECTION_TEMPORARY); - } - - /** - * When used as part of a response to a preflight CORS request, indicates - * whether or not the actual request can be made using credentials.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Credentials" header. - * - * @param accessControlAllowCredentials True if the requested resource allows - * credential. - */ - public void setAccessControlAllowCredentials(Boolean accessControlAllowCredentials) { - this.accessControlAllowCredentials = accessControlAllowCredentials; - } - - /** - * When used as part of a response to a preflight CORS request, indicates how - * long (in seconds) the results of a preflight request can be cached in a - * preflight result cache.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Max-Age" header.
- * In case of negative value, the header is not set. - * - * @param accessControlMaxAge How long the results of a preflight request can be - * cached in a preflight result cache. - */ - public void setAccessControlMaxAge(int accessControlMaxAge) { - this.accessControlMaxAge = accessControlMaxAge; - } - - /** - * Sets the set of headers allowed by the actual request on the current resource - * when used as part of a response to a preflight CORS request.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Headers" header. - * - * @param accessControlAllowHeaders The set of headers allowed by the actual - * request on the current resource. - */ - public void setAccessControlAllowHeaders(Set accessControlAllowHeaders) { - synchronized (getAccessControlAllowHeaders()) { - if (accessControlAllowHeaders != this.accessControlAllowHeaders) { - this.accessControlAllowHeaders.clear(); - - if (accessControlAllowHeaders != null) { - this.accessControlAllowHeaders.addAll(accessControlAllowHeaders); - } - } - } - } - - /** - * Sets the set of methods allowed by the actual request on the current resource - * when used as part of a response to a preflight CORS request.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Methods" header. - * - * @param accessControlAllowMethods The set of methods allowed by the actual - * request on the current resource. - */ - public void setAccessControlAllowMethods(Set accessControlAllowMethods) { - synchronized (getAccessControlAllowMethods()) { - if (accessControlAllowMethods != this.accessControlAllowMethods) { - this.accessControlAllowMethods.clear(); - - if (accessControlAllowMethods != null) { - this.accessControlAllowMethods.addAll(accessControlAllowMethods); - } - } - } - } - - /** - * When used in the context of CORS support, it sets the URI an origin server - * allows for the requested resource. Use "*" as a wildcard character.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Origin" header. - * - * @param accessControlAllowOrigin The origin allowed by the requested resource. - */ - public void setAccessControlAllowOrigin(String accessControlAllowOrigin) { - // TODO Add some input validation here. - this.accessControlAllowOrigin = accessControlAllowOrigin; - } - - /** - * Sets the list of headers an origin server allows for the requested - * resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Expose-Headers" header. - * - * @param accessControlExposeHeaders The set of headers an origin server allows - * for the requested resource. - */ - public void setAccessControlExposeHeaders(Set accessControlExposeHeaders) { - synchronized (getAccessControlExposeHeaders()) { - if (accessControlExposeHeaders != this.accessControlExposeHeaders) { - this.accessControlExposeHeaders.clear(); - - if (accessControlExposeHeaders != null) { - this.accessControlExposeHeaders.addAll(accessControlExposeHeaders); - } - } - } - } - - /** - * Sets the estimated amount of time since a response was generated or - * revalidated by the origin server. Origin servers should leave the 0 default - * value. Only caches are expected to set this property.
- *
- * Note that when used with HTTP connectors, this property maps to the "Age" - * header. - * - * @param age The response age. - */ - public void setAge(int age) { - this.age = age; - } - - /** - * Sets the set of methods allowed on the requested resource. The set instance - * set must be thread-safe (use {@link CopyOnWriteArraySet} for example.
- *
- * Note that when used with HTTP connectors, this property maps to the "Allow" - * header. - * - * @param allowedMethods The set of methods allowed on the requested resource. - */ - public void setAllowedMethods(Set allowedMethods) { - synchronized (getAllowedMethods()) { - if (allowedMethods != this.allowedMethods) { - this.allowedMethods.clear(); - - if (allowedMethods != null) { - this.allowedMethods.addAll(allowedMethods); - } - } - } - } - - /** - * Sets the authentication information sent by an origin server to a client - * after a successful authentication attempt.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Authentication-Info" header. - * - * @param authenticationInfo The data returned by the server in response to - * successful authentication. - */ - public void setAuthenticationInfo(AuthenticationInfo authenticationInfo) { - this.authenticationInfo = authenticationInfo; - } - - /** - * Indicates if the response should be automatically committed. - * - * @param autoCommitting True if the response should be automatically committed - */ - public void setAutoCommitting(boolean autoCommitting) { - this.autoCommitting = autoCommitting; - } - - /** - * Sets the list of authentication requests sent by an origin server to a - * client. Note that when used with HTTP connectors, this property maps to the - * "WWW-Authenticate" header. This method clears the current list and adds all - * entries in the parameter list. - * - * @param challengeRequests A list of authentication requests sent by an origin - * server to a client. - */ - public void setChallengeRequests(List challengeRequests) { - synchronized (getChallengeRequests()) { - if (challengeRequests != getChallengeRequests()) { - getChallengeRequests().clear(); - - if (challengeRequests != null) { - getChallengeRequests().addAll(challengeRequests); - } - } - } - } - - /** - * Indicates if the response has already been committed. - * - * @param committed True if the response has already been committed. - */ - public void setCommitted(boolean committed) { - this.committed = committed; - } - - /** - * Sets the modifiable series of cookie settings provided by the server. Note - * that when used with HTTP connectors, this property maps to the "Set-Cookie" - * and "Set-Cookie2" headers. This method clears the current series and adds all - * entries in the parameter series. - * - * @param cookieSettings A series of cookie settings provided by the server. - */ - public void setCookieSettings(Series cookieSettings) { - synchronized (getCookieSettings()) { - if (cookieSettings != getCookieSettings()) { - getCookieSettings().clear(); - - if (cookieSettings != null) { - getCookieSettings().addAll(cookieSettings); - } - } - } - } - - /** - * Sets the set of dimensions on which the response entity may vary. Note that - * when used with HTTP connectors, this property maps to the "Vary" header. This - * method clears the current set and adds all entries in the parameter set. - * - * @param dimensions The set of dimensions on which the response entity may - * vary. - */ - public void setDimensions(Set dimensions) { - synchronized (getDimensions()) { - if (dimensions != getDimensions()) { - getDimensions().clear(); - - if (dimensions != null) { - getDimensions().addAll(dimensions); - } - } - } - } - - /** - * Sets the reference that the client should follow for redirections or resource - * creations. Note that when used with HTTP connectors, this property maps to - * the "Location" header. - * - * @param locationRef The reference to set. - */ - public void setLocationRef(Reference locationRef) { - this.locationRef = locationRef; - } - - /** - * Sets the reference that the client should follow for redirections or resource - * creations. If you pass a relative location URI, it will be resolved with the - * current base reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Location" header. - * - * @param locationUri The URI to set. - * @see #setLocationRef(Reference) - */ - public void setLocationRef(String locationUri) { - Reference baseRef = null; - - if (getRequest().getResourceRef() != null) { - if (getRequest().getResourceRef().getBaseRef() != null) { - baseRef = getRequest().getResourceRef().getBaseRef(); - } else { - baseRef = getRequest().getResourceRef(); - } - } - - setLocationRef(new Reference(baseRef, locationUri).getTargetRef()); - } - - /** - * Sets the modifiable list of authentication requests sent by a proxy to a - * client. The list instance set must be thread-safe (use - * {@link CopyOnWriteArrayList} for example. Note that when used with HTTP - * connectors, this property maps to the "Proxy-Authenticate" header. This - * method clears the current list and adds all entries in the parameter list. - * - * @param proxyChallengeRequests A list of authentication requests sent by a - * proxy to a client. - */ - public void setProxyChallengeRequests(List proxyChallengeRequests) { - synchronized (getProxyChallengeRequests()) { - if (proxyChallengeRequests != getProxyChallengeRequests()) { - getProxyChallengeRequests().clear(); - - if (proxyChallengeRequests != null) { - getProxyChallengeRequests().addAll(proxyChallengeRequests); - } - } - } - } - - /** - * Sets the associated request. - * - * @param request The associated request - */ - public void setRequest(Request request) { - this.request = request; - } - - /** - * Indicates how long the service is expected to be unavailable to the - * requesting client. Default value is null.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Retry-After" header. - * - * @param retryAfter Date after with a retry attempt could occur. - */ - public void setRetryAfter(Date retryAfter) { - this.retryAfter = retryAfter; - } - - /** - * Sets the server-specific information. - * - * @param serverInfo The server-specific information. - */ - public void setServerInfo(ServerInfo serverInfo) { - this.serverInfo = serverInfo; - } - - /** - * Sets the status. - * - * @param status The status to set. - */ - public void setStatus(Status status) { - this.status = status; - } - - /** - * Sets the status. - * - * @param status The status to set (code and reason phrase). - * @param description The longer status description. - */ - public void setStatus(Status status, String description) { - setStatus(new Status(status, description)); - } - - /** - * Sets the status. - * - * @param status The status to set. - * @param throwable The related error or exception. - */ - public void setStatus(Status status, Throwable throwable) { - setStatus(new Status(status, throwable)); - } - - /** - * Sets the status. - * - * @param status The status to set. - * @param throwable The related error or exception. - * @param message The status message. - */ - public void setStatus(Status status, Throwable throwable, String message) { - setStatus(new Status(status, throwable, message)); - } - - /** - * Displays a synthesis of the response like an HTTP status line. - * - * @return A synthesis of the response like an HTTP status line. - */ - public String toString() { - return ((getRequest() == null) ? "?" : getRequest().getProtocol()) + " - " + getStatus(); - } + private static final ThreadLocal CURRENT = new ThreadLocal(); + + /** + * Returns the response associated with the current thread. + * + *

Warning: this method should only be used under duress. You should by default prefer + * obtaining the current context using methods such as {@link + * org.restlet.resource.Resource#getResponse()}. + * + *

This variable is stored internally as a thread local variable and updated each time a call + * is handled by a Restlet via the {@link Restlet#handle(org.restlet.Request, + * org.restlet.Response)} method. + * + * @return The current context. + */ + public static Response getCurrent() { + return CURRENT.get(); + } + + /** + * Sets the response associated with the current thread. + * + * @param response The thread's response. + */ + public static void setCurrent(Response response) { + CURRENT.set(response); + } + + /** + * When used as part of a response to a preflight CORS request, this indicates whether + * the actual request can be made using credentials. + */ + private volatile Boolean accessControlAllowCredentials; + + /** + * When used as part of a response to a preflight CORS request, this lists the headers allowed + * by the actual request on the current resource. + */ + private volatile Set accessControlAllowHeaders; + + /** + * When used as part of a response to a preflight CORS request, this lists the methods allowed + * by the actual request on the current resource. + */ + private volatile Set accessControlAllowMethods; + + /** + * When used in the context of CORS support, it specifies the URI an origin server allows for + * the requested resource. Use "*" as a wildcard character. + */ + private volatile String accessControlAllowOrigin; + + /** The whitelist of headers an origin server allows for the requested resource. */ + private volatile Set accessControlExposeHeaders; + + /** + * When used in the context of CORS support, it indicates how long the results of a preflight + * request can be cached in a preflight result cache. + */ + private volatile int accessControlMaxAge; + + /** + * Estimated amount of time since a response was generated or revalidated by the origin server. + */ + private volatile int age; + + /** The set of methods allowed on the requested resource. */ + private volatile Set allowedMethods; + + /** + * The authentication information sent by an origin server to a client in the case of a + * successful authentication attempt. + */ + private volatile AuthenticationInfo authenticationInfo; + + /** Indicates if the response should be automatically committed. */ + private volatile boolean autoCommitting; + + /** The authentication requests sent by an origin server to a client. */ + private volatile List challengeRequests; + + /** Indicates if the response has been committed. */ + private volatile boolean committed; + + /** The cookie settings provided by the server. */ + private volatile Series cookieSettings; + + /** The set of dimensions on which the response entity may vary. */ + private volatile Set dimensions; + + /** The reference used for redirections or creations. */ + private volatile Reference locationRef; + + /** The authentication requests sent by a proxy to a client. */ + private volatile List proxyChallengeRequests; + + /** The associated request. */ + private volatile Request request; + + /** Indicates how long the service is expected to be unavailable to the requesting client. */ + private volatile Date retryAfter; + + /** The server-specific information. */ + private volatile ServerInfo serverInfo; + + /** The status. */ + private volatile Status status; + + /** + * Constructor. + * + * @param request The request associated with this response. + */ + public Response(Request request) { + this.age = 0; + this.accessControlAllowCredentials = null; + this.accessControlAllowHeaders = null; + this.accessControlAllowMethods = null; + this.accessControlAllowOrigin = null; + this.accessControlExposeHeaders = null; + this.allowedMethods = null; + this.autoCommitting = true; + this.challengeRequests = null; + this.cookieSettings = null; + this.committed = false; + this.dimensions = null; + this.locationRef = null; + this.proxyChallengeRequests = null; + this.request = request; + this.retryAfter = null; + this.serverInfo = null; + this.status = Status.SUCCESS_OK; + } + + /** + * Ask the connector to abort the related network connection, for example immediately closing + * the socket. + */ + public void abort() { + getRequest().abort(); + } + + /** + * Asks the server connector to immediately commit the given response, making it ready to be + * sent back to the client. Note that all server connectors don't necessarily support this + * feature.
+ *
+ * When the response is in autoCommit mode (see related property), then calling this method + * isn't necessary. Also, be aware that committing the response doesn't necessarily mean that + * it will immediately be written on the network as some buffering can occur. If you want to + * ensure that response buffers are flushed.
+ *
+ * Note that this calls back {@link Request#commit(Response)} on the parent request which holds + * the link with the underlying network connection. + */ + public void commit() { + getRequest().commit(this); + } + + /** + * Asks the server connector to immediately flush the network buffers. Note that this calls back + * {@link Request#flushBuffers()} on the parent request which holds the link with the underlying + * network connection. + * + * @throws IOException + */ + @Override + public void flushBuffers() throws IOException { + getRequest().flushBuffers(); + } + + /** + * When used as part of a response to a preflight CORS request, this indicates whether + * the actual request can be made using credentials.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Credentials" header. + * + * @return True if the requested resource allows credential. + */ + public Boolean getAccessControlAllowCredentials() { + return this.accessControlAllowCredentials; + } + + /** + * Returns the modifiable set of headers allowed by the actual request on the current resource + * when used as part of a response to a preflight CORS request.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Headers" header. + * + * @return The set of headers allowed by the actual request on the current resource. + */ + public Set getAccessControlAllowHeaders() { + // Lazy initialization with double-check. + Set a = this.accessControlAllowHeaders; + if (a == null) { + synchronized (this) { + a = this.accessControlAllowHeaders; + if (a == null) { + this.accessControlAllowHeaders = a = new CopyOnWriteArraySet(); + } + } + } + return a; + } + + /** + * Returns the modifiable set of methods allowed by the actual request on the current resource + * when used as part of a response to a preflight CORS request
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Methods" header. + * + * @return The set of methods allowed by the actual request on the current resource. + */ + public Set getAccessControlAllowMethods() { + // Lazy initialization with double-check. + Set a = this.accessControlAllowMethods; + if (a == null) { + synchronized (this) { + a = this.accessControlAllowMethods; + if (a == null) { + this.accessControlAllowMethods = a = new CopyOnWriteArraySet(); + } + } + } + return a; + } + + /** + * When used in the context of CORS support, it returns the URI an origin server allows for the + * requested resource. Use "*" as a wildcard character.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Origin" header. + * + * @return The origin allowed by the requested resource. + */ + public String getAccessControlAllowOrigin() { + return this.accessControlAllowOrigin; + } + + /** + * Returns a modifiable whitelist of headers an origin server allows for the requested resource. + *
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Expose-Headers" header. + * + * @return The set of headers an origin server allows for the requested resource. + */ + public Set getAccessControlExposeHeaders() { + // Lazy initialization with double-check. + Set a = this.accessControlExposeHeaders; + if (a == null) { + synchronized (this) { + a = this.accessControlExposeHeaders; + if (a == null) { + this.accessControlExposeHeaders = a = new CopyOnWriteArraySet(); + } + } + } + return a; + } + + /** + * Indicates how long the results of a preflight CORS request can be cached in a preflight + * result cache.
+ * In case of a negative value, the results of a preflight request are not meant to be cached. + *
+ * Note that when used with HTTP connectors, this property maps to the "Access-Control-Max-Age" + * header. + * + * @return Indicates how long the results of a preflight request can be cached in a preflight + * result cache. + */ + public int getAccessControlMaxAge() { + return accessControlMaxAge; + } + + /** + * Returns the estimated amount of time since a response was generated or revalidated by the + * origin server. Origin servers should leave the 0 default value. Only caches are expected to + * set this property.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Age" header. + * + * @return The response age. + */ + public int getAge() { + return age; + } + + /** + * Returns the modifiable set of methods allowed on the requested resource. This property only + * has to be updated when a status CLIENT_ERROR_METHOD_NOT_ALLOWED is set. Creates a new + * instance if no one has been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Allow" header. + * + * @return The set of allowed methods. + */ + public Set getAllowedMethods() { + // Lazy initialization with double-check. + Set a = this.allowedMethods; + if (a == null) { + synchronized (this) { + a = this.allowedMethods; + if (a == null) { + this.allowedMethods = a = new CopyOnWriteArraySet(); + } + } + } + return a; + } + + /** + * Returns information sent by an origin server related to a successful authentication attempt. + * If none is available, null is returned.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Authentication-Info" + * header. + * + * @return The authentication information provided by the server. + */ + public AuthenticationInfo getAuthenticationInfo() { + return this.authenticationInfo; + } + + /** + * Returns the list of authentication requests sent by an origin server to a client. If none is + * available, an empty list is returned.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "WWW-Authenticate" + * header. + * + * @return The list of authentication requests. + */ + public List getChallengeRequests() { + // Lazy initialization with double-check. + List cr = this.challengeRequests; + if (cr == null) { + synchronized (this) { + cr = this.challengeRequests; + if (cr == null) { + this.challengeRequests = cr = new CopyOnWriteArrayList(); + } + } + } + return cr; + } + + /** + * Returns the modifiable series of cookie settings provided by the server. Creates a new + * instance if no one has been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Set-Cookie" and + * "Set-Cookie2" headers. + * + * @return The cookie settings provided by the server. + */ + public Series getCookieSettings() { + // Lazy initialization with double-check. + Series c = this.cookieSettings; + if (c == null) { + synchronized (this) { + c = this.cookieSettings; + if (c == null) { + this.cookieSettings = c = new Series<>(CookieSetting.class); + } + } + } + return c; + } + + /** + * Returns the modifiable set of selecting dimensions on which the response entity may vary. If + * some server-side content negotiation is done, this set should be properly updated, other it + * can be left empty. Creates a new instance if no one has been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Vary" header. + * + * @return The set of dimensions on which the response entity may vary. + */ + public Set getDimensions() { + if (this.dimensions == null) { + this.dimensions = new CopyOnWriteArraySet(); + } + return this.dimensions; + } + + /** + * Returns the location reference. This is the reference that the client should follow for + * redirections or resource creations.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Location" header. + * + * @return The redirection reference. + */ + public Reference getLocationRef() { + return this.locationRef; + } + + /** + * Returns the list of authentication requests sent by an origin server to a client. If none is + * available, an empty list is returned.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Proxy-Authenticate" + * header. + * + * @return The list of authentication requests. + */ + public List getProxyChallengeRequests() { + // Lazy initialization with double-check. + List cr = this.proxyChallengeRequests; + if (cr == null) { + synchronized (this) { + cr = this.proxyChallengeRequests; + if (cr == null) { + this.proxyChallengeRequests = cr = new CopyOnWriteArrayList(); + } + } + } + return cr; + } + + /** + * Returns the associated request + * + * @return The associated request + */ + public Request getRequest() { + return this.request; + } + + /** + * Indicates how long the service is expected to be unavailable to the requesting client. + * The default value is null.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Retry-After" header. + * + * @return Date after with a retry attempt could occur. + */ + public Date getRetryAfter() { + return retryAfter; + } + + /** + * Returns the server-specific information. Creates a new instance if no one has been set. + * + * @return The server-specific information. + */ + public ServerInfo getServerInfo() { + // Lazy initialization with double-check. + ServerInfo s = this.serverInfo; + if (s == null) { + synchronized (this) { + s = this.serverInfo; + if (s == null) { + this.serverInfo = s = new ServerInfo(); + } + } + } + return s; + } + + /** + * Returns the status. + * + * @return The status. + */ + public Status getStatus() { + return this.status; + } + + /** + * Indicates if the response should be automatically committed. When processing a request on the + * server-side, setting this property to 'false' let you ask to the server connector to wait + * before sending the response back to the client when the initial calling thread returns. This + * will let you do further updates to the response and manually calling {@link #commit()} later + * on, using another thread. + * + * @return True if the response should be automatically committed. + */ + public boolean isAutoCommitting() { + return autoCommitting; + } + + /** + * Indicates if the response has already been committed. + * + * @return True if the response has already been committed. + */ + public boolean isCommitted() { + return committed; + } + + @Override + public boolean isConfidential() { + return getRequest().isConfidential(); + } + + /** + * Indicates if the response is final or provisional. It relies on the {@link + * Status#isInformational()} method. + * + * @return True if the response is final. + */ + public boolean isFinal() { + return !getStatus().isInformational(); + } + + /** + * Indicates if the response is provisional or final. It relies on the {@link + * Status#isInformational()} method. + * + * @return True if the response is provisional. + */ + public boolean isProvisional() { + return getStatus().isInformational(); + } + + /** + * Permanently redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetRef The target URI reference. + */ + public void redirectPermanent(Reference targetRef) { + setLocationRef(targetRef); + setStatus(Status.REDIRECTION_PERMANENT); + } + + /** + * Permanently redirects the client to a target URI. The client is expected to reuse the same + * method for the new request.
+ *
+ * If you pass a relative target URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}. + * + * @param targetUri The target URI. + */ + public void redirectPermanent(String targetUri) { + setLocationRef(targetUri); + setStatus(Status.REDIRECTION_PERMANENT); + } + + /** + * Redirects the client to a different URI that SHOULD be retrieved using a GET method on that + * resource. This method exists primarily to allow the output of a POST-activated script to + * redirect the user agent to a selected resource. The new URI is not a substitute reference for + * the originally requested resource. + * + * @param targetRef The target reference. + */ + public void redirectSeeOther(Reference targetRef) { + setLocationRef(targetRef); + setStatus(Status.REDIRECTION_SEE_OTHER); + } + + /** + * Redirects the client to a different URI that SHOULD be retrieved using a GET method on that + * resource. This method exists primarily to allow the output of a POST-activated script to + * redirect the user agent to a selected resource. The new URI is not a substitute reference for + * the originally requested resource.
+ *
+ * If you pass a relative target URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}. + * + * @param targetUri The target URI. + */ + public void redirectSeeOther(String targetUri) { + setLocationRef(targetUri); + setStatus(Status.REDIRECTION_SEE_OTHER); + } + + /** + * Temporarily redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetRef The target reference. + */ + public void redirectTemporary(Reference targetRef) { + setLocationRef(targetRef); + setStatus(Status.REDIRECTION_TEMPORARY); + } + + /** + * Temporarily redirects the client to a target URI. The client is expected to reuse the same + * method for the new request.
+ *
+ * If you pass a relative target URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}. + * + * @param targetUri The target URI. + */ + public void redirectTemporary(String targetUri) { + setLocationRef(targetUri); + setStatus(Status.REDIRECTION_TEMPORARY); + } + + /** + * When used as part of a response to a preflight CORS request, indicates whether or not the + * actual request can be made using credentials.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Credentials" header. + * + * @param accessControlAllowCredentials True if the requested resource allows credential. + */ + public void setAccessControlAllowCredentials(Boolean accessControlAllowCredentials) { + this.accessControlAllowCredentials = accessControlAllowCredentials; + } + + /** + * When used as part of a response to a preflight CORS request, indicates how long (in seconds) + * the results of a preflight request can be cached in a preflight result cache.
+ * Note that when used with HTTP connectors, this property maps to the "Access-Control-Max-Age" + * header.
+ * In case of negative value, the header is not set. + * + * @param accessControlMaxAge How long the results of a preflight request can be cached in a + * preflight result cache. + */ + public void setAccessControlMaxAge(int accessControlMaxAge) { + this.accessControlMaxAge = accessControlMaxAge; + } + + /** + * Sets the set of headers allowed by the actual request on the current resource when used as + * part of a response to a preflight CORS request.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Headers" header. + * + * @param accessControlAllowHeaders The set of headers allowed by the actual request on the + * current resource. + */ + public void setAccessControlAllowHeaders(Set accessControlAllowHeaders) { + synchronized (getAccessControlAllowHeaders()) { + if (accessControlAllowHeaders != this.accessControlAllowHeaders) { + this.accessControlAllowHeaders.clear(); + + if (accessControlAllowHeaders != null) { + this.accessControlAllowHeaders.addAll(accessControlAllowHeaders); + } + } + } + } + + /** + * Sets the set of methods allowed by the actual request on the current resource when used as + * part of a response to a preflight CORS request.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Methods" header. + * + * @param accessControlAllowMethods The set of methods allowed by the actual request on the + * current resource. + */ + public void setAccessControlAllowMethods(Set accessControlAllowMethods) { + synchronized (getAccessControlAllowMethods()) { + if (accessControlAllowMethods != this.accessControlAllowMethods) { + this.accessControlAllowMethods.clear(); + + if (accessControlAllowMethods != null) { + this.accessControlAllowMethods.addAll(accessControlAllowMethods); + } + } + } + } + + /** + * When used in the context of CORS support, it sets the URI an origin server allows for the + * requested resource. Use "*" as a wildcard character.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Origin" header. + * + * @param accessControlAllowOrigin The origin allowed by the requested resource. + */ + public void setAccessControlAllowOrigin(String accessControlAllowOrigin) { + // TODO Add some input validation here. + this.accessControlAllowOrigin = accessControlAllowOrigin; + } + + /** + * Sets the list of headers an origin server allows for the requested resource.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Expose-Headers" header. + * + * @param accessControlExposeHeaders The set of headers an origin server allows for the + * requested resource. + */ + public void setAccessControlExposeHeaders(Set accessControlExposeHeaders) { + synchronized (getAccessControlExposeHeaders()) { + if (accessControlExposeHeaders != this.accessControlExposeHeaders) { + this.accessControlExposeHeaders.clear(); + + if (accessControlExposeHeaders != null) { + this.accessControlExposeHeaders.addAll(accessControlExposeHeaders); + } + } + } + } + + /** + * Sets the estimated amount of time since a response was generated or revalidated by the origin + * server. Origin servers should leave the 0 default value. Only caches are expected to set this + * property.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Age" header. + * + * @param age The response age. + */ + public void setAge(int age) { + this.age = age; + } + + /** + * Sets the set of methods allowed on the requested resource. The set instance set must be + * thread-safe (use {@link CopyOnWriteArraySet} for example.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Allow" header. + * + * @param allowedMethods The set of methods allowed on the requested resource. + */ + public void setAllowedMethods(Set allowedMethods) { + synchronized (getAllowedMethods()) { + if (allowedMethods != this.allowedMethods) { + this.allowedMethods.clear(); + + if (allowedMethods != null) { + this.allowedMethods.addAll(allowedMethods); + } + } + } + } + + /** + * Sets the authentication information sent by an origin server to a client after a successful + * authentication attempt.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Authentication-Info" + * header. + * + * @param authenticationInfo The data returned by the server in response to successful + * authentication. + */ + public void setAuthenticationInfo(AuthenticationInfo authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } + + /** + * Indicates if the response should be automatically committed. + * + * @param autoCommitting True if the response should be automatically committed + */ + public void setAutoCommitting(boolean autoCommitting) { + this.autoCommitting = autoCommitting; + } + + /** + * Sets the list of authentication requests sent by an origin server to a client. Note that when + * used with HTTP connectors, this property maps to the "WWW-Authenticate" header. This method + * clears the current list and adds all entries in the parameter list. + * + * @param challengeRequests A list of authentication requests sent by an origin server to a + * client. + */ + public void setChallengeRequests(List challengeRequests) { + synchronized (getChallengeRequests()) { + if (challengeRequests != getChallengeRequests()) { + getChallengeRequests().clear(); + + if (challengeRequests != null) { + getChallengeRequests().addAll(challengeRequests); + } + } + } + } + + /** + * Indicates if the response has already been committed. + * + * @param committed True if the response has already been committed. + */ + public void setCommitted(boolean committed) { + this.committed = committed; + } + + /** + * Sets the modifiable series of cookie settings provided by the server. Note that when used + * with HTTP connectors, this property maps to the "Set-Cookie" and "Set-Cookie2" headers. This + * method clears the current series and adds all entries in the parameter series. + * + * @param cookieSettings A series of cookie settings provided by the server. + */ + public void setCookieSettings(Series cookieSettings) { + synchronized (getCookieSettings()) { + if (cookieSettings != getCookieSettings()) { + getCookieSettings().clear(); + + if (cookieSettings != null) { + getCookieSettings().addAll(cookieSettings); + } + } + } + } + + /** + * Sets the set of dimensions on which the response entity may vary. Note that when used with + * HTTP connectors, this property maps to the "Vary" header. This method clears the current set + * and adds all entries in the parameter set. + * + * @param dimensions The set of dimensions on which the response entity may vary. + */ + public void setDimensions(Set dimensions) { + synchronized (getDimensions()) { + if (dimensions != getDimensions()) { + getDimensions().clear(); + + if (dimensions != null) { + getDimensions().addAll(dimensions); + } + } + } + } + + /** + * Sets the reference that the client should follow for redirections or resource creations. Note + * that when used with HTTP connectors, this property maps to the "Location" header. + * + * @param locationRef The reference to set. + */ + public void setLocationRef(Reference locationRef) { + this.locationRef = locationRef; + } + + /** + * Sets the reference that the client should follow for redirections or resource creations. If + * you pass a relative location URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Location" header. + * + * @param locationUri The URI to set. + * @see #setLocationRef(Reference) + */ + public void setLocationRef(String locationUri) { + Reference baseRef = null; + + if (getRequest().getResourceRef() != null) { + if (getRequest().getResourceRef().getBaseRef() != null) { + baseRef = getRequest().getResourceRef().getBaseRef(); + } else { + baseRef = getRequest().getResourceRef(); + } + } + + setLocationRef(new Reference(baseRef, locationUri).getTargetRef()); + } + + /** + * Sets the modifiable list of authentication requests sent by a proxy to a client. The list + * instance set must be thread-safe (use {@link CopyOnWriteArrayList} for example. Note that + * when used with HTTP connectors, this property maps to the "Proxy-Authenticate" header. This + * method clears the current list and adds all entries in the parameter list. + * + * @param proxyChallengeRequests A list of authentication requests sent by a proxy to a client. + */ + public void setProxyChallengeRequests(List proxyChallengeRequests) { + synchronized (getProxyChallengeRequests()) { + if (proxyChallengeRequests != getProxyChallengeRequests()) { + getProxyChallengeRequests().clear(); + + if (proxyChallengeRequests != null) { + getProxyChallengeRequests().addAll(proxyChallengeRequests); + } + } + } + } + + /** + * Sets the associated request. + * + * @param request The associated request + */ + public void setRequest(Request request) { + this.request = request; + } + + /** + * Indicates how long the service is expected to be unavailable to the requesting client. + * Default value is null.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Retry-After" header. + * + * @param retryAfter Date after with a retry attempt could occur. + */ + public void setRetryAfter(Date retryAfter) { + this.retryAfter = retryAfter; + } + + /** + * Sets the server-specific information. + * + * @param serverInfo The server-specific information. + */ + public void setServerInfo(ServerInfo serverInfo) { + this.serverInfo = serverInfo; + } + + /** + * Sets the status. + * + * @param status The status to set. + */ + public void setStatus(Status status) { + this.status = status; + } + + /** + * Sets the status. + * + * @param status The status to set (code and reason phrase). + * @param description The longer status description. + */ + public void setStatus(Status status, String description) { + setStatus(new Status(status, description)); + } + + /** + * Sets the status. + * + * @param status The status to set. + * @param throwable The related error or exception. + */ + public void setStatus(Status status, Throwable throwable) { + setStatus(new Status(status, throwable)); + } + + /** + * Sets the status. + * + * @param status The status to set. + * @param throwable The related error or exception. + * @param message The status message. + */ + public void setStatus(Status status, Throwable throwable, String message) { + setStatus(new Status(status, throwable, message)); + } + + /** + * Displays a synthesis of the response like an HTTP status line. + * + * @return A synthesis of the response like an HTTP status line. + */ + public String toString() { + return ((getRequest() == null) ? "?" : getRequest().getProtocol()) + " - " + getStatus(); + } } diff --git a/org.restlet/src/main/java/org/restlet/Restlet.java b/org.restlet/src/main/java/org/restlet/Restlet.java index 038dfedfc5..98c2598a78 100644 --- a/org.restlet/src/main/java/org/restlet/Restlet.java +++ b/org.restlet/src/main/java/org/restlet/Restlet.java @@ -1,427 +1,415 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; -import org.restlet.data.Status; -import org.restlet.engine.Engine; - import java.util.logging.Level; import java.util.logging.Logger; +import org.restlet.data.Status; +import org.restlet.engine.Engine; /** - * Uniform class that provides a context and life cycle support. It has many - * subclasses that focus on specific ways to process calls. The context property - * is typically provided by a parent Component as a way to encapsulate access to - * shared features such as logging and client connectors.
+ * Uniform class that provides a context and life cycle support. It has many subclasses that focus + * on specific ways to process calls. The context property is typically provided by a parent + * Component as a way to encapsulate access to shared features such as logging and client + * connectors.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public abstract class Restlet implements Uniform { - /** Error message. */ - private static final String UNABLE_TO_START = "Unable to start the Restlet"; - - /** - * Indicates that a Restlet's context has changed. - * - * @param restlet The Restlet with a changed context. - * @param context The new context. - */ - private static void fireContextChanged(Restlet restlet, Context context) { - if (context != null) { - if (context instanceof org.restlet.engine.util.ChildContext childContext) { - - if (childContext.getChild() == null) { - childContext.setChild(restlet); - } - } else if (!(restlet instanceof Component) - && (context instanceof org.restlet.engine.component.ComponentContext)) { - context.getLogger().severe( - "For security reasons, don't pass the component context to child Restlets anymore. Use the Context#createChildContext() method instead. " - + restlet.getClass()); - } - } - } - - /** The author(s). */ - private volatile String author; - - /** The context. */ - private volatile Context context; - - /** The description. */ - private volatile String description; - - /** Finder class to instantiate. */ - private volatile Class finderClass; - - /** The display name. */ - private volatile String name; - - /** The owner(s). */ - private volatile String owner; - - /** Indicates if the Restlet was started. */ - private volatile boolean started; - - /** - * Constructor with null context. - */ - public Restlet() { - this(null); - } - - /** - * Constructor with the Restlet's context which can be the parent's application - * context, but shouldn't be the parent Component's context for security - * reasons. - * - * @see Context#createChildContext() - * - * @param context The context of the Restlet. - * - */ - public Restlet(Context context) { - this.context = context; - this.started = false; - this.name = toString(); - this.description = null; - this.author = null; - this.owner = null; - - this.finderClass = null; - if (Engine.getInstance() == null) { - Context.getCurrentLogger().severe("Unable to fully initialize the Restlet. No Restlet engine available."); - throw new RuntimeException("Unable to fully initialize the Restlet. No Restlet engine available."); - } - - fireContextChanged(this, context); - } - - /** - * Creates a new finder instance based on the "targetClass" property. If none is - * define, the {@link Application#createFinder(Class)} method is invoked if - * available, otherwise the - * {@link org.restlet.resource.Finder#createFinder(Class, Class, Context, Logger)} - * method is called with the {@link org.restlet.resource.Finder} class as - * parameter. - * - * @param resourceClass The target {@link org.restlet.resource.ServerResource} - * class to find. - * @return The new finder instance. - * @see org.restlet.resource.Finder#createFinder(Class, Class, Context, Logger) - */ - public org.restlet.resource.Finder createFinder( - Class resourceClass) { - org.restlet.resource.Finder result = null; - - if (getFinderClass() != null) { - result = org.restlet.resource.Finder.createFinder(resourceClass, getFinderClass(), getContext(), - getLogger()); - } else if ((getApplication() != null) && (getApplication() != this)) { - result = getApplication().createFinder(resourceClass); - } else { - result = org.restlet.resource.Finder.createFinder(resourceClass, org.restlet.resource.Finder.class, - getContext(), getLogger()); - } - - return result; - } - - /** - * Attempts to {@link #stop()} the Restlet if it is still started. - */ - @Override - protected void finalize() throws Throwable { - if (isStarted()) { - stop(); - } - super.finalize(); - } - - /** - * Returns the parent application if it exists, or null. - * - * @return The parent application if it exists, or null. - */ - public Application getApplication() { - return Application.getCurrent(); - } - - /** - * Returns the author(s). - * - * @return The author(s). - */ - public String getAuthor() { - return this.author; - } - - /** - * Returns the context. - * - * @return The context. - */ - public Context getContext() { - return this.context; - } - - /** - * Returns the description. - * - * @return The description - */ - public String getDescription() { - return this.description; - } - - /** - * Returns the finder class used to instantiate resource classes. By default, it - * returns the {@link org.restlet.resource.Finder} class. This property is - * leveraged by {@link Application#setOutboundRoot(Class)} and - * {@link Application#setInboundRoot(Class)} methods. - * - * @return the finder class to instantiate. - */ - public Class getFinderClass() { - return finderClass; - } - - /** - * Returns the context's logger. - * - * @return The context's logger. - */ - public Logger getLogger() { - Logger result = null; - Context context = getContext(); - - if (context == null) { - context = Context.getCurrent(); - } - - if (context != null) { - result = context.getLogger(); - } - - if (result == null) { - result = Engine.getLogger(this, "org.restlet"); - } - - return result; - } - - /** - * Returns the display name. - * - * @return The display name. - */ - public String getName() { - return this.name; - } - - /** - * Returns the owner(s). - * - * @return The owner(s). - */ - public String getOwner() { - return this.owner; - } - - /** - * Handles a call. Creates an empty {@link Response} object and then invokes - * {@link #handle(Request, Response)}. - * - * @param request The request to handle. - * @return The returned response. - */ - public final Response handle(Request request) { - Response response = new Response(request); - handle(request, response); - return response; - } - - /** - * Handles a call. The default behavior is to initialize the Restlet by setting - * the current context using the {@link Context#setCurrent(Context)} method and - * by attempting to start it, unless it was already started. If an exception is - * thrown during the start action, then the response status is set to - * {@link Status#SERVER_ERROR_INTERNAL}. - *

- * Subclasses overriding this method should make sure that they call - * super.handle(request, response) before adding their own logic. - * - * @param request The request to handle. - * @param response The response to update. - */ - public void handle(Request request, Response response) { - // Associate the response to the current thread - Response.setCurrent(response); - - // Associate the context to the current thread - if (getContext() != null) { - Context.setCurrent(getContext()); - } - - // Check if the Restlet was started - if (isStopped()) { - try { - start(); - } catch (Exception e) { - // Occurred while starting the Restlet - if (getContext() != null) { - getContext().getLogger().log(Level.WARNING, UNABLE_TO_START, e); - } else { - Context.getCurrentLogger().log(Level.WARNING, UNABLE_TO_START, e); - } - - response.setStatus(Status.SERVER_ERROR_INTERNAL); - } - - if (!isStarted()) { - // No exception raised but the Restlet somehow couldn't be - // started - getContext().getLogger().log(Level.WARNING, UNABLE_TO_START); - response.setStatus(Status.SERVER_ERROR_INTERNAL); - } - } - } - - /** - * Handles a call. - * - * @param request The request to handle. - * @param response The response to update. - * @param onResponseCallback The callback invoked upon response reception. - */ - public final void handle(Request request, Response response, Uniform onResponseCallback) { - request.setOnResponse(onResponseCallback); - handle(request, response); - } - - /** - * Handles a call. - * - * @param request The request to handle. - * @param onReceivedCallback The callback invoked upon request reception. - */ - public final void handle(Request request, Uniform onReceivedCallback) { - Response response = new Response(request); - handle(request, response, onReceivedCallback); - } - - /** - * Indicates if the Restlet is started. - * - * @return True if the Restlet is started. - */ - public boolean isStarted() { - return this.started; - } - - /** - * Indicates if the Restlet is stopped. - * - * @return True if the Restlet is stopped. - */ - public boolean isStopped() { - return !this.started; - } - - /** - * Sets the author(s). - * - * @param author The author(s). - */ - public void setAuthor(String author) { - this.author = author; - } - - /** - * Sets the context. - * - * @param context The context. - */ - public void setContext(Context context) { - this.context = context; - fireContextChanged(this, context); - } - - /** - * Sets the description. - * - * @param description The description. - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Sets the finder class to instantiate. This property is leveraged by - * {@link Application#setOutboundRoot(Class)} and - * {@link Application#setInboundRoot(Class)} methods. - * - * @param finderClass The finder class to instantiate. - */ - public void setFinderClass(Class finderClass) { - this.finderClass = finderClass; - } - - /** - * Sets the display name. - * - * @param name The display name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Sets the owner(s). - * - * @param owner The owner(s). - */ - public void setOwner(String owner) { - this.owner = owner; - } - - /** - * Starts the Restlet. By default its only sets "started" internal property to - * true. - * - * WARNING: this method must be called at the end of the starting process by - * subclasses otherwise concurrent threads could enter into the call handling - * logic too early. - * - * @throws Exception - */ - public synchronized void start() throws Exception { - this.started = true; - } - - /** - * Stops the Restlet. By default its only sets "started" internal property to - * false. - * - * WARNING: this method must be called at the beginning of the stopping process - * by subclasses otherwise concurrent threads could continue to (improperly) - * handle calls. - * - * @throws Exception - */ - public synchronized void stop() throws Exception { - this.started = false; - } - + /** Error message. */ + private static final String UNABLE_TO_START = "Unable to start the Restlet"; + + /** + * Indicates that a Restlet's context has changed. + * + * @param restlet The Restlet with a changed context. + * @param context The new context. + */ + private static void fireContextChanged(Restlet restlet, Context context) { + if (context != null) { + if (context instanceof org.restlet.engine.util.ChildContext childContext) { + + if (childContext.getChild() == null) { + childContext.setChild(restlet); + } + } else if (!(restlet instanceof Component) + && (context instanceof org.restlet.engine.component.ComponentContext)) { + context.getLogger() + .severe( + "For security reasons, don't pass the component context to child Restlets anymore. Use the Context#createChildContext() method instead. " + + restlet.getClass()); + } + } + } + + /** The author(s). */ + private volatile String author; + + /** The context. */ + private volatile Context context; + + /** The description. */ + private volatile String description; + + /** Finder class to instantiate. */ + private volatile Class finderClass; + + /** The display name. */ + private volatile String name; + + /** The owner(s). */ + private volatile String owner; + + /** Indicates if the Restlet was started. */ + private volatile boolean started; + + /** Constructor with null context. */ + public Restlet() { + this(null); + } + + /** + * Constructor with the Restlet's context which can be the parent's application context, but + * shouldn't be the parent Component's context for security reasons. + * + * @see Context#createChildContext() + * @param context The context of the Restlet. + */ + public Restlet(Context context) { + this.context = context; + this.started = false; + this.name = toString(); + this.description = null; + this.author = null; + this.owner = null; + + this.finderClass = null; + if (Engine.getInstance() == null) { + Context.getCurrentLogger() + .severe("Unable to fully initialize the Restlet. No Restlet engine available."); + throw new RuntimeException( + "Unable to fully initialize the Restlet. No Restlet engine available."); + } + + fireContextChanged(this, context); + } + + /** + * Creates a new finder instance based on the "targetClass" property. If none is define, the + * {@link Application#createFinder(Class)} method is invoked if available, otherwise the {@link + * org.restlet.resource.Finder#createFinder(Class, Class, Context, Logger)} method is called + * with the {@link org.restlet.resource.Finder} class as parameter. + * + * @param resourceClass The target {@link org.restlet.resource.ServerResource} class to find. + * @return The new finder instance. + * @see org.restlet.resource.Finder#createFinder(Class, Class, Context, Logger) + */ + public org.restlet.resource.Finder createFinder( + Class resourceClass) { + org.restlet.resource.Finder result = null; + + if (getFinderClass() != null) { + result = + org.restlet.resource.Finder.createFinder( + resourceClass, getFinderClass(), getContext(), getLogger()); + } else if ((getApplication() != null) && (getApplication() != this)) { + result = getApplication().createFinder(resourceClass); + } else { + result = + org.restlet.resource.Finder.createFinder( + resourceClass, + org.restlet.resource.Finder.class, + getContext(), + getLogger()); + } + + return result; + } + + /** Attempts to {@link #stop()} the Restlet if it is still started. */ + @Override + protected void finalize() throws Throwable { + if (isStarted()) { + stop(); + } + super.finalize(); + } + + /** + * Returns the parent application if it exists, or null. + * + * @return The parent application if it exists, or null. + */ + public Application getApplication() { + return Application.getCurrent(); + } + + /** + * Returns the author(s). + * + * @return The author(s). + */ + public String getAuthor() { + return this.author; + } + + /** + * Returns the context. + * + * @return The context. + */ + public Context getContext() { + return this.context; + } + + /** + * Returns the description. + * + * @return The description + */ + public String getDescription() { + return this.description; + } + + /** + * Returns the finder class used to instantiate resource classes. By default, it returns the + * {@link org.restlet.resource.Finder} class. This property is leveraged by {@link + * Application#setOutboundRoot(Class)} and {@link Application#setInboundRoot(Class)} methods. + * + * @return the finder class to instantiate. + */ + public Class getFinderClass() { + return finderClass; + } + + /** + * Returns the context's logger. + * + * @return The context's logger. + */ + public Logger getLogger() { + Logger result = null; + Context context = getContext(); + + if (context == null) { + context = Context.getCurrent(); + } + + if (context != null) { + result = context.getLogger(); + } + + if (result == null) { + result = Engine.getLogger(this, "org.restlet"); + } + + return result; + } + + /** + * Returns the display name. + * + * @return The display name. + */ + public String getName() { + return this.name; + } + + /** + * Returns the owner(s). + * + * @return The owner(s). + */ + public String getOwner() { + return this.owner; + } + + /** + * Handles a call. Creates an empty {@link Response} object and then invokes {@link + * #handle(Request, Response)}. + * + * @param request The request to handle. + * @return The returned response. + */ + public final Response handle(Request request) { + Response response = new Response(request); + handle(request, response); + return response; + } + + /** + * Handles a call. The default behavior is to initialize the Restlet by setting the current + * context using the {@link Context#setCurrent(Context)} method and by attempting to start it, + * unless it was already started. If an exception is thrown during the start action, then the + * response status is set to {@link Status#SERVER_ERROR_INTERNAL}. + * + *

Subclasses overriding this method should make sure that they call super.handle(request, + * response) before adding their own logic. + * + * @param request The request to handle. + * @param response The response to update. + */ + public void handle(Request request, Response response) { + // Associate the response to the current thread + Response.setCurrent(response); + + // Associate the context to the current thread + if (getContext() != null) { + Context.setCurrent(getContext()); + } + + // Check if the Restlet was started + if (isStopped()) { + try { + start(); + } catch (Exception e) { + // Occurred while starting the Restlet + if (getContext() != null) { + getContext().getLogger().log(Level.WARNING, UNABLE_TO_START, e); + } else { + Context.getCurrentLogger().log(Level.WARNING, UNABLE_TO_START, e); + } + + response.setStatus(Status.SERVER_ERROR_INTERNAL); + } + + if (!isStarted()) { + // No exception raised but the Restlet somehow couldn't be + // started + getContext().getLogger().log(Level.WARNING, UNABLE_TO_START); + response.setStatus(Status.SERVER_ERROR_INTERNAL); + } + } + } + + /** + * Handles a call. + * + * @param request The request to handle. + * @param response The response to update. + * @param onResponseCallback The callback invoked upon response reception. + */ + public final void handle(Request request, Response response, Uniform onResponseCallback) { + request.setOnResponse(onResponseCallback); + handle(request, response); + } + + /** + * Handles a call. + * + * @param request The request to handle. + * @param onReceivedCallback The callback invoked upon request reception. + */ + public final void handle(Request request, Uniform onReceivedCallback) { + Response response = new Response(request); + handle(request, response, onReceivedCallback); + } + + /** + * Indicates if the Restlet is started. + * + * @return True if the Restlet is started. + */ + public boolean isStarted() { + return this.started; + } + + /** + * Indicates if the Restlet is stopped. + * + * @return True if the Restlet is stopped. + */ + public boolean isStopped() { + return !this.started; + } + + /** + * Sets the author(s). + * + * @param author The author(s). + */ + public void setAuthor(String author) { + this.author = author; + } + + /** + * Sets the context. + * + * @param context The context. + */ + public void setContext(Context context) { + this.context = context; + fireContextChanged(this, context); + } + + /** + * Sets the description. + * + * @param description The description. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Sets the finder class to instantiate. This property is leveraged by {@link + * Application#setOutboundRoot(Class)} and {@link Application#setInboundRoot(Class)} methods. + * + * @param finderClass The finder class to instantiate. + */ + public void setFinderClass(Class finderClass) { + this.finderClass = finderClass; + } + + /** + * Sets the display name. + * + * @param name The display name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the owner(s). + * + * @param owner The owner(s). + */ + public void setOwner(String owner) { + this.owner = owner; + } + + /** + * Starts the Restlet. By default, it only sets the "started" internal property to true. + * + *

WARNING: this method must be called at the end of the starting process by subclasses, + * otherwise concurrent threads could enter into the call handling logic too early. + * + * @throws Exception + */ + public synchronized void start() throws Exception { + this.started = true; + } + + /** + * Stops the Restlet. By default, it only sets the "started" internal property to false. + * + *

WARNING: this method must be called at the beginning of the stopping process by subclasses, + * otherwise concurrent threads could continue to (improperly) handle calls. + * + * @throws Exception + */ + public synchronized void stop() throws Exception { + this.started = false; + } } diff --git a/org.restlet/src/main/java/org/restlet/Server.java b/org.restlet/src/main/java/org/restlet/Server.java index 0bdd923aac..055fc952a2 100644 --- a/org.restlet/src/main/java/org/restlet/Server.java +++ b/org.restlet/src/main/java/org/restlet/Server.java @@ -1,494 +1,489 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; +import java.util.Arrays; +import java.util.List; import org.restlet.data.Protocol; import org.restlet.engine.Engine; import org.restlet.engine.RestletHelper; import org.restlet.resource.ServerResource; -import java.util.Arrays; -import java.util.List; - /** - * Connector acting as a generic server. It internally uses one of the available - * connector helpers registered with the Restlet engine.
+ * Connector acting as a generic server. It internally uses one of the available connector helpers + * registered with the Restlet engine.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables.
+ * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables.
*
- * For advanced cases, it is possible to obtained the wrapped - * {@link RestletHelper} instance that is used by this client to handle the - * calls via the "org.restlet.engine.helper" attribute stored in the - * {@link Context} object. - * + * For advanced cases, it is possible to obtain the wrapped {@link RestletHelper} instance that is + * used by this client to handle the calls via the "org.restlet.engine.helper" attribute stored in + * the {@link Context} object. + * * @author Jerome Louvel */ public class Server extends Connector { - /** The listening address if specified. */ - private volatile String address; - - /** The helper provided by the implementation. */ - private final RestletHelper helper; - - /** The next Restlet. */ - private volatile Restlet next; - - /** The listening port if specified. */ - private volatile int port; - - /** - * Constructor. - * - * @param context The context. - * @param protocols The connector protocols. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(Context context, List protocols, int port, Restlet next) { - this(context, protocols, null, port, next); - } - - /** - * Constructor. - * - * @param context The context. - * @param protocols The connector protocols. - * @param address The optional listening IP address (useful if multiple IP - * addresses available). You can also use a domain name as an - * alias for the IP address to listen to. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(Context context, List protocols, String address, int port, Restlet next) { - this(context, protocols, address, port, next, null); - } - - /** - * Constructor. - * - * @param context The context. - * @param protocols The connector protocols. - * @param address The optional listening IP address (useful if multiple IP - * addresses available). You can also use a domain name as an - * alias for the IP address to listen to. - * @param port The listening port. - * @param next The next Restlet. - * @param helperClass Optional helper class name. - */ - public Server(Context context, List protocols, String address, int port, Restlet next, - String helperClass) { - super(context, protocols); - this.address = address; - this.port = port; - this.next = next; - - if (Engine.getInstance() != null) { - this.helper = Engine.getInstance().createHelper(this, helperClass); - } else { - this.helper = null; - } - - if (context != null && this.helper != null) { - context.getAttributes().put("org.restlet.engine.helper", this.helper); - } - } - - /** - * Constructor. Note that it uses the protocol's default port. - * - * @param context The parent context. - * @param protocol The connector protocol. - */ - public Server(Context context, Protocol protocol) { - this(context, protocol, (protocol == null) ? -1 : protocol.getDefaultPort()); - } - - /** - * Constructor. - * - * @param context The context. - * @param protocol The connector protocol. - * @param nextClass The next server resource. - */ - public Server(Context context, Protocol protocol, Class nextClass) { - this(context, protocol); - setNext(createFinder(nextClass)); - } - - /** - * Constructor. - * - * @param context The parent context. - * @param protocol The connector protocol. - * @param port The listening port. - */ - public Server(Context context, Protocol protocol, int port) { - this(context, protocol, port, (Restlet) null); - } - - /** - * Constructor. - * - * @param context The context. - * @param protocol The connector protocol. - * @param port The listening port. - * @param nextClass The next server resource. - */ - public Server(Context context, Protocol protocol, int port, Class nextClass) { - this(context, protocol, port); - setNext(createFinder(nextClass)); - } - - /** - * Constructor. - * - * @param context The context. - * @param protocol The connector protocol. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(Context context, Protocol protocol, int port, Restlet next) { - this(context, protocol, null, port, next); - } - - /** - * Constructor using the protocol's default port. - * - * @param context The context. - * @param protocol The connector protocol. - * @param next The next Restlet. - */ - public Server(Context context, Protocol protocol, Restlet next) { - this(context, protocol, null, (protocol == null) ? -1 : protocol.getDefaultPort(), next); - } - - /** - * Constructor. - * - * @param context The context. - * @param protocol The connector protocol. - * @param address The optional listening IP address (useful if multiple IP - * addresses available). You can also use a domain name as an - * alias for the IP address to listen to. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(Context context, Protocol protocol, String address, int port, Restlet next) { - this(context, (protocol == null) ? null : Arrays.asList(protocol), address, port, next); - } - - /** - * Constructor. - * - * @param protocols The connector protocols. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(List protocols, int port, Restlet next) { - this(null, protocols, port, next); - } - - /** - * Constructor. - * - * @param protocols The connector protocols. - * @param address The optional listening IP address (useful if multiple IP - * addresses available). You can also use a domain name as an - * alias for the IP address to listen to. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(List protocols, String address, int port, Restlet next) { - this(null, protocols, address, port, next); - } - - /** - * Constructor. - * - * @param protocol The connector protocol. - */ - public Server(Protocol protocol) { - this(null, protocol, (Restlet) null); - } - - /** - * Constructor using the protocol's default port. - * - * @param protocol The connector protocol. - * @param nextClass The next server resource. - */ - public Server(Protocol protocol, Class nextClass) { - this(null, protocol); - setNext(createFinder(nextClass)); - } - - /** - * Constructor. - * - * @param protocol The connector protocol. - * @param port The listening port. - */ - public Server(Protocol protocol, int port) { - this(null, protocol, port, (Restlet) null); - } - - /** - * Constructor. - * - * @param protocol The connector protocol. - * @param port The listening port. - * @param nextClass The next server resource. - */ - public Server(Protocol protocol, int port, Class nextClass) { - this(protocol, port); - setNext(createFinder(nextClass)); - } - - /** - * Constructor. - * - * @param protocol The connector protocol. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(Protocol protocol, int port, Restlet next) { - this(null, protocol, port, next); - } - - /** - * Constructor using the protocol's default port. - * - * @param protocol The connector protocol. - * @param next The next Restlet. - */ - public Server(Protocol protocol, Restlet next) { - this(null, protocol, next); - } - - /** - * Constructor using the protocol's default port. - * - * @param protocol The connector protocol. - * @param address The listening IP address (useful if multiple IP addresses - * available). You can also use a domain name as an alias for - * the IP address to listen to. - */ - public Server(Protocol protocol, String address) { - this(null, protocol, address, protocol.getDefaultPort(), null); - } - - /** - * Constructor using the protocol's default port. - * - * @param protocol The connector protocol. - * @param address The listening IP address (useful if multiple IP addresses - * available). You can also use a domain name as an alias for - * the IP address to listen to. - * @param nextClass The next server resource. - */ - public Server(Protocol protocol, String address, Class nextClass) { - this(protocol, address); - setNext(createFinder(nextClass)); - } - - /** - * Constructor. - * - * @param protocol The connector protocol. - * @param address The optional listening IP address (useful if multiple IP - * addresses available). You can also use a domain name as an - * alias for the IP address to listen to. - * @param port The listening port. - */ - public Server(Protocol protocol, String address, int port) { - this(null, protocol, address, port, null); - } - - /** - * Constructor. - * - * @param protocol The connector protocol. - * @param address The optional listening IP address (useful if multiple IP - * addresses available). You can also use a domain name as an - * alias for the IP address to listen to. - * @param port The listening port. - * @param next The next Restlet. - */ - public Server(Protocol protocol, String address, int port, Restlet next) { - this(null, protocol, address, port, next); - } - - /** - * Constructor using the protocol's default port. - * - * @param protocol The connector protocol. - * @param address The listening IP address (useful if multiple IP addresses - * available). You can also use a domain name as an alias for - * the IP address to listen to. - * @param next The next Restlet. - */ - public Server(Protocol protocol, String address, Restlet next) { - this(null, protocol, address, protocol.getDefaultPort(), next); - } - - /** - * Returns the actual server port after it has started. If an ephemeral port is - * used it will be returned, otherwise the fixed port will be provided. - * - * @return The actual server port. - */ - public int getActualPort() { - return (getPort() == 0) ? getEphemeralPort() : getPort(); - } - - /** - * Returns the optional listening IP address (local host used if null). - * - * @return The optional listening IP address (local host used if null). - */ - public String getAddress() { - return this.address; - } - - /** - * Returns the actual ephemeral port used when the listening port is set to '0'. - * The default value is '-1' if no ephemeral port is known. See - * InetSocketAddress#InetSocketAddress(int) and ServerSocket#getLocalPort() - * methods for details. - * - * @return The actual ephemeral port used. - */ - public int getEphemeralPort() { - return (Integer) getHelper().getAttributes().get("ephemeralPort"); - } - - /** - * Returns the internal server. - * - * @return The internal server. - */ - private RestletHelper getHelper() { - return this.helper; - } - - /** - * Returns the next Restlet. - * - * @return The next Restlet. - */ - public Restlet getNext() { - return this.next; - } - - /** - * Returns the listening port if specified. - * - * @return The listening port if specified. - */ - public int getPort() { - return this.port; - } - - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - - if (getNext() != null) { - getNext().handle(request, response); - } - } - - /** - * Indicates if a next Restlet is set. - * - * @return True if a next Restlet is set. - */ - public boolean hasNext() { - return this.next != null; - } - - /** - * Indicates the underlying connector helper is available. - * - * @return True if the underlying connector helper is available. - */ - @Override - public boolean isAvailable() { - return getHelper() != null; - } - - /** - * Sets the optional listening IP address (local host used if null). - * - * @param address The optional listening IP address (local host used if null). - */ - public void setAddress(String address) { - this.address = address; - } - - /** - * Sets the next Restlet as a Finder for a given resource class. When the call - * is delegated to the Finder instance, a new instance of the resource class - * will be created and will actually handle the request. - * - * @param nextClass The next resource class to attach. - */ - public void setNext(Class nextClass) { - setNext(createFinder(nextClass)); - } - - /** - * Sets the next Restlet. - * - * @param next The next Restlet. - */ - public void setNext(Restlet next) { - this.next = next; - } - - /** - * Sets the listening port if specified. Note that '0' means that the system - * will pick up an ephemeral port at the binding time. This ephemeral can be - * retrieved once the server is started using the {@link #getEphemeralPort()} - * method. - * - * @param port The listening port if specified. - */ - protected void setPort(int port) { - this.port = port; - } - - @Override - public synchronized void start() throws Exception { - if (isStopped()) { - if (getHelper() != null) { - getHelper().start(); - } - - // Must be invoked as a last step - super.start(); - } - } - - @Override - public synchronized void stop() throws Exception { - if (isStarted()) { - // Must be invoked as a first step - super.stop(); - - if (getHelper() != null) { - getHelper().stop(); - } - } - } - + /** The listening address if specified. */ + private volatile String address; + + /** The helper provided by the implementation. */ + private final RestletHelper helper; + + /** The next Restlet. */ + private volatile Restlet next; + + /** The listening port if specified. */ + private volatile int port; + + /** + * Constructor. + * + * @param context The context. + * @param protocols The connector protocols. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server(Context context, List protocols, int port, Restlet next) { + this(context, protocols, null, port, next); + } + + /** + * Constructor. + * + * @param context The context. + * @param protocols The connector protocols. + * @param address The optional listening IP address (useful if multiple IP addresses available). + * You can also use a domain name as an alias for the IP address to listen to. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server( + Context context, List protocols, String address, int port, Restlet next) { + this(context, protocols, address, port, next, null); + } + + /** + * Constructor. + * + * @param context The context. + * @param protocols The connector protocols. + * @param address The optional listening IP address (useful if multiple IP addresses available). + * You can also use a domain name as an alias for the IP address to listen to. + * @param port The listening port. + * @param next The next Restlet. + * @param helperClass Optional helper class name. + */ + public Server( + Context context, + List protocols, + String address, + int port, + Restlet next, + String helperClass) { + super(context, protocols); + this.address = address; + this.port = port; + this.next = next; + + if (Engine.getInstance() != null) { + this.helper = Engine.getInstance().createHelper(this, helperClass); + } else { + this.helper = null; + } + + if (context != null && this.helper != null) { + context.getAttributes().put("org.restlet.engine.helper", this.helper); + } + } + + /** + * Constructor. Note that it uses the protocol's default port. + * + * @param context The parent context. + * @param protocol The connector protocol. + */ + public Server(Context context, Protocol protocol) { + this(context, protocol, (protocol == null) ? -1 : protocol.getDefaultPort()); + } + + /** + * Constructor. + * + * @param context The context. + * @param protocol The connector protocol. + * @param nextClass The next server resource. + */ + public Server(Context context, Protocol protocol, Class nextClass) { + this(context, protocol); + setNext(createFinder(nextClass)); + } + + /** + * Constructor. + * + * @param context The parent context. + * @param protocol The connector protocol. + * @param port The listening port. + */ + public Server(Context context, Protocol protocol, int port) { + this(context, protocol, port, (Restlet) null); + } + + /** + * Constructor. + * + * @param context The context. + * @param protocol The connector protocol. + * @param port The listening port. + * @param nextClass The next server resource. + */ + public Server( + Context context, + Protocol protocol, + int port, + Class nextClass) { + this(context, protocol, port); + setNext(createFinder(nextClass)); + } + + /** + * Constructor. + * + * @param context The context. + * @param protocol The connector protocol. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server(Context context, Protocol protocol, int port, Restlet next) { + this(context, protocol, null, port, next); + } + + /** + * Constructor using the protocol's default port. + * + * @param context The context. + * @param protocol The connector protocol. + * @param next The next Restlet. + */ + public Server(Context context, Protocol protocol, Restlet next) { + this(context, protocol, null, (protocol == null) ? -1 : protocol.getDefaultPort(), next); + } + + /** + * Constructor. + * + * @param context The context. + * @param protocol The connector protocol. + * @param address The optional listening IP address (useful if multiple IP addresses available). + * You can also use a domain name as an alias for the IP address to listen to. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server(Context context, Protocol protocol, String address, int port, Restlet next) { + this(context, (protocol == null) ? null : Arrays.asList(protocol), address, port, next); + } + + /** + * Constructor. + * + * @param protocols The connector protocols. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server(List protocols, int port, Restlet next) { + this(null, protocols, port, next); + } + + /** + * Constructor. + * + * @param protocols The connector protocols. + * @param address The optional listening IP address (useful if multiple IP addresses available). + * You can also use a domain name as an alias for the IP address to listen to. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server(List protocols, String address, int port, Restlet next) { + this(null, protocols, address, port, next); + } + + /** + * Constructor. + * + * @param protocol The connector protocol. + */ + public Server(Protocol protocol) { + this(null, protocol, (Restlet) null); + } + + /** + * Constructor using the protocol's default port. + * + * @param protocol The connector protocol. + * @param nextClass The next server resource. + */ + public Server(Protocol protocol, Class nextClass) { + this(null, protocol); + setNext(createFinder(nextClass)); + } + + /** + * Constructor. + * + * @param protocol The connector protocol. + * @param port The listening port. + */ + public Server(Protocol protocol, int port) { + this(null, protocol, port, (Restlet) null); + } + + /** + * Constructor. + * + * @param protocol The connector protocol. + * @param port The listening port. + * @param nextClass The next server resource. + */ + public Server(Protocol protocol, int port, Class nextClass) { + this(protocol, port); + setNext(createFinder(nextClass)); + } + + /** + * Constructor. + * + * @param protocol The connector protocol. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server(Protocol protocol, int port, Restlet next) { + this(null, protocol, port, next); + } + + /** + * Constructor using the protocol's default port. + * + * @param protocol The connector protocol. + * @param next The next Restlet. + */ + public Server(Protocol protocol, Restlet next) { + this(null, protocol, next); + } + + /** + * Constructor using the protocol's default port. + * + * @param protocol The connector protocol. + * @param address The listening IP address (useful if multiple IP addresses available). You can + * also use a domain name as an alias for the IP address to listen to. + */ + public Server(Protocol protocol, String address) { + this(null, protocol, address, protocol.getDefaultPort(), null); + } + + /** + * Constructor using the protocol's default port. + * + * @param protocol The connector protocol. + * @param address The listening IP address (useful if multiple IP addresses available). You can + * also use a domain name as an alias for the IP address to listen to. + * @param nextClass The next server resource. + */ + public Server(Protocol protocol, String address, Class nextClass) { + this(protocol, address); + setNext(createFinder(nextClass)); + } + + /** + * Constructor. + * + * @param protocol The connector protocol. + * @param address The optional listening IP address (useful if multiple IP addresses available). + * You can also use a domain name as an alias for the IP address to listen to. + * @param port The listening port. + */ + public Server(Protocol protocol, String address, int port) { + this(null, protocol, address, port, null); + } + + /** + * Constructor. + * + * @param protocol The connector protocol. + * @param address The optional listening IP address (useful if multiple IP addresses available). + * You can also use a domain name as an alias for the IP address to listen to. + * @param port The listening port. + * @param next The next Restlet. + */ + public Server(Protocol protocol, String address, int port, Restlet next) { + this(null, protocol, address, port, next); + } + + /** + * Constructor using the protocol's default port. + * + * @param protocol The connector protocol. + * @param address The listening IP address (useful if multiple IP addresses available). You can + * also use a domain name as an alias for the IP address to listen to. + * @param next The next Restlet. + */ + public Server(Protocol protocol, String address, Restlet next) { + this(null, protocol, address, protocol.getDefaultPort(), next); + } + + /** + * Returns the actual server port after it has started. If an ephemeral port is used it will be + * returned, otherwise the fixed port will be provided. + * + * @return The actual server port. + */ + public int getActualPort() { + return (getPort() == 0) ? getEphemeralPort() : getPort(); + } + + /** + * Returns the optional listening IP address (local host used if null). + * + * @return The optional listening IP address (local host used if null). + */ + public String getAddress() { + return this.address; + } + + /** + * Returns the actual ephemeral port used when the listening port is set to '0'. The default + * value is '-1' if no ephemeral port is known. See InetSocketAddress#InetSocketAddress(int) and + * ServerSocket#getLocalPort() methods for details. + * + * @return The actual ephemeral port used. + */ + public int getEphemeralPort() { + return (Integer) getHelper().getAttributes().get("ephemeralPort"); + } + + /** + * Returns the internal server. + * + * @return The internal server. + */ + private RestletHelper getHelper() { + return this.helper; + } + + /** + * Returns the next Restlet. + * + * @return The next Restlet. + */ + public Restlet getNext() { + return this.next; + } + + /** + * Returns the listening port if specified. + * + * @return The listening port if specified. + */ + public int getPort() { + return this.port; + } + + @Override + public void handle(Request request, Response response) { + super.handle(request, response); + + if (getNext() != null) { + getNext().handle(request, response); + } + } + + /** + * Indicates if the next Restlet is set. + * + * @return True if a next Restlet is set. + */ + public boolean hasNext() { + return this.next != null; + } + + /** + * Indicates the underlying connector helper is available. + * + * @return True if the underlying connector helper is available. + */ + @Override + public boolean isAvailable() { + return getHelper() != null; + } + + /** + * Sets the optional listening IP address (local host used if null). + * + * @param address The optional listening IP address (local host used if null). + */ + public void setAddress(String address) { + this.address = address; + } + + /** + * Sets the next Restlet as a Finder for a given resource class. When the call is delegated to + * the Finder instance, a new instance of the resource class will be created and will actually + * handle the request. + * + * @param nextClass The next resource class to attach. + */ + public void setNext(Class nextClass) { + setNext(createFinder(nextClass)); + } + + /** + * Sets the next Restlet. + * + * @param next The next Restlet. + */ + public void setNext(Restlet next) { + this.next = next; + } + + /** + * Sets the listening port if specified. Note that '0' means that the system will pick up an + * ephemeral port at the binding time. This ephemeral can be retrieved once the server is + * started using the {@link #getEphemeralPort()} method. + * + * @param port The listening port if specified. + */ + protected void setPort(int port) { + this.port = port; + } + + @Override + public synchronized void start() throws Exception { + if (isStopped()) { + if (getHelper() != null) { + getHelper().start(); + } + + // Must be invoked as a last step + super.start(); + } + } + + @Override + public synchronized void stop() throws Exception { + if (isStarted()) { + // Must be invoked as a first step + super.stop(); + + if (getHelper() != null) { + getHelper().stop(); + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/Uniform.java b/org.restlet/src/main/java/org/restlet/Uniform.java index 6552a37b7f..4584aea91f 100644 --- a/org.restlet/src/main/java/org/restlet/Uniform.java +++ b/org.restlet/src/main/java/org/restlet/Uniform.java @@ -1,36 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; /** - * Uniform REST interface. "The central feature that distinguishes the REST - * architectural style from other network-based styles is its emphasis on a - * uniform interface between components. By applying the software engineering - * principle of generality to the component interface, the overall system - * architecture is simplified and the visibility of interactions is improved. - * Implementations are decoupled from the services they provide, which - * encourages independent evolvability." Roy T. Fielding - * - * @see Source - * dissertation + * Uniform REST interface. "The central feature that distinguishes the REST architectural style from + * other network-based styles is its emphasis on a uniform interface between components. By applying + * the software engineering principle of generality to the component interface, the overall system + * architecture is simplified and the visibility of interactions is improved. Implementations are + * decoupled from the services they provide, which encourages independent evolvability." Roy T. + * Fielding + * + * @see Source + * dissertation * @author Jerome Louvel */ public interface Uniform { - /** - * Handles a uniform call. It is important to realize that this interface can be - * used either on the client-side or on the server-side. - * - * @param request The request to handle. - * @param response The associated response. - */ - void handle(Request request, Response response); + /** + * Handles a uniform call. It is important to realize that this interface can be used either on + * the client-side or on the server-side. + * + * @param request The request to handle. + * @param response The associated response. + */ + void handle(Request request, Response response); } diff --git a/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java b/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java index f93f067039..d602849a91 100644 --- a/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java @@ -1,229 +1,234 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import org.restlet.engine.util.SystemUtils; /** - * Preemptive authentication information. Sent by an origin server to a client - * after a successful digest authentication attempt.
+ * Preemptive authentication information. Sent by an origin server to a client after a successful + * digest authentication attempt.
*
- * Note that when used with HTTP connectors, this class maps to the - * "Authentication-Info" header. - * - * @see HTTP - * Authentication - The Authentication-Info Header - * + * Note that when used with HTTP connectors, this class maps to the "Authentication-Info" header. + * + * @see HTTP Authentication - + * The Authentication-Info Header * @author Kelly McLaughlin * @author Jerome Louvel */ public class AuthenticationInfo { - /** The next nonce value. */ - private volatile String nextServerNonce; - - /** The nonce-count value. */ - private volatile int nonceCount; - - /** The client nonce. */ - private volatile String clientNonce; - - /** The quality of protection. */ - private volatile String quality; - - /** The optional response digest for mutual authentication. */ - private volatile String responseDigest; - - /** - * Default constructor. - * - * @param nextNonce The next nonce value. - */ - // public AuthenticationInfo(String nextNonce) { - // this(nextNonce, 0, ); - // } - - /** - * Constructor. - * - * @param nextNonce The next nonce value. - * @param nonceCount The nonce-count value. - * @param cnonce The cnonce value. - * @param quality The quality of protection. - * @param responseDigest The optional response digest for mutual authentication. - */ - public AuthenticationInfo(String nextNonce, int nonceCount, String cnonce, String quality, String responseDigest) { - this.nextServerNonce = nextNonce; - this.nonceCount = nonceCount; - this.clientNonce = cnonce; - this.quality = quality; - this.responseDigest = responseDigest; - } - - /** {@inheritDoc} */ - @Override - public final boolean equals(final Object obj) { - boolean result = (obj == this); - - // if obj == this no need to go further - if (!result) { - // if obj isn't a challenge request or is null don't evaluate - // further - if (obj instanceof AuthenticationInfo) { - final AuthenticationInfo that = (AuthenticationInfo) obj; - if (getNextServerNonce() != null) { - result = getNextServerNonce().equals(that.getNextServerNonce()); - } else { - result = (that.getNextServerNonce() == null); - } - - if (result) { - result = (getNonceCount() == that.getNonceCount()); - } - - if (result) { - if (getClientNonce() != null) { - result = getClientNonce().equals(that.getClientNonce()); - } else { - result = (that.getClientNonce() == null); - } - } - - if (result) { - if (getQuality() != null) { - result = getQuality().equals(that.getQuality()); - } else { - result = (that.getQuality() == null); - } - } - - if (result) { - if (getResponseDigest() != null) { - result = getResponseDigest().equals(that.getResponseDigest()); - } else { - result = (that.getResponseDigest() == null); - } - } - } - } - - return result; - } - - /** - * Returns the client nonce. - * - * @return The client nonce. - */ - public String getClientNonce() { - return this.clientNonce; - } - - /** - * Returns the next server nonce. This is the nonce the server wishes the client - * to use for a future authentication response - * - * @return The next nonce value. - */ - public String getNextServerNonce() { - return this.nextServerNonce; - } - - /** - * Returns the nonce-count value. - * - * @return The nonce-count value. - */ - public int getNonceCount() { - return this.nonceCount; - } - - /** - * Returns the quality of protection. The value can be - * {@link ChallengeMessage#QUALITY_AUTHENTICATION} for authentication or - * {@link ChallengeMessage#QUALITY_AUTHENTICATION_INTEGRITY} for authentication - * with integrity protection. - * - * @return The quality of protection. - */ - public String getQuality() { - return this.quality; - } - - /** - * Returns the optional response digest for mutual authentication. Note that - * when used with HTTP connectors, this property maps to the "response-digest" - * value in the "response-auth" directive of the "Authentication-Info" header. - * - * @return The optional response digest for mutual authentication. - */ - public String getResponseDigest() { - return this.responseDigest; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(getNextServerNonce(), getNonceCount(), getClientNonce(), getQuality(), - getResponseDigest()); - } - - /** - * Sets the client nonce. - * - * @param clientNonce The client nonce. - */ - public void setClientNonce(String clientNonce) { - this.clientNonce = clientNonce; - } - - /** - * Sets the next server nonce. This is the nonce the server wishes the client to - * use for a future authentication response - * - * @param nextNonce The next nonce. - */ - public void setNextServerNonce(String nextNonce) { - this.nextServerNonce = nextNonce; - } - - /** - * Sets the nonce-count value. - * - * @param nonceCount The nonceCount value. - */ - public void setNonceCount(int nonceCount) { - this.nonceCount = nonceCount; - } - - /** - * Sets the quality of protection. The value can be - * {@link ChallengeMessage#QUALITY_AUTHENTICATION} for authentication or - * {@link ChallengeMessage#QUALITY_AUTHENTICATION_INTEGRITY} for authentication - * with integrity protection. - * - * @param qop The quality of protection. - */ - public void setQuality(String qop) { - this.quality = qop; - } - - /** - * Sets the optional response digest for mutual authentication. Note that when - * used with HTTP connectors, this property maps to the "response-digest" value - * in the "response-auth" directive of the "Authentication-Info" header. - * - * @param responseDigest The response digest. - */ - public void setResponseDigest(String responseDigest) { - this.responseDigest = responseDigest; - } + /** The next nonce value. */ + private volatile String nextServerNonce; + + /** The nonce-count value. */ + private volatile int nonceCount; + + /** The client nonce. */ + private volatile String clientNonce; + + /** The quality of protection. */ + private volatile String quality; + + /** The optional response digest for mutual authentication. */ + private volatile String responseDigest; + + /** + * Default constructor. + * + * @param nextNonce The next nonce value. + */ + // public AuthenticationInfo(String nextNonce) { + // this(nextNonce, 0, ); + // } + + /** + * Constructor. + * + * @param nextNonce The next nonce value. + * @param nonceCount The nonce-count value. + * @param cnonce The cnonce value. + * @param quality The quality of protection. + * @param responseDigest The optional response digest for mutual authentication. + */ + public AuthenticationInfo( + String nextNonce, + int nonceCount, + String cnonce, + String quality, + String responseDigest) { + this.nextServerNonce = nextNonce; + this.nonceCount = nonceCount; + this.clientNonce = cnonce; + this.quality = quality; + this.responseDigest = responseDigest; + } + + /** {@inheritDoc} */ + @Override + public final boolean equals(final Object obj) { + boolean result = (obj == this); + + // if obj == this no need to go further + if (!result) { + // if obj isn't a challenge request or is null don't evaluate + // further + if (obj instanceof AuthenticationInfo) { + final AuthenticationInfo that = (AuthenticationInfo) obj; + if (getNextServerNonce() != null) { + result = getNextServerNonce().equals(that.getNextServerNonce()); + } else { + result = (that.getNextServerNonce() == null); + } + + if (result) { + result = (getNonceCount() == that.getNonceCount()); + } + + if (result) { + if (getClientNonce() != null) { + result = getClientNonce().equals(that.getClientNonce()); + } else { + result = (that.getClientNonce() == null); + } + } + + if (result) { + if (getQuality() != null) { + result = getQuality().equals(that.getQuality()); + } else { + result = (that.getQuality() == null); + } + } + + if (result) { + if (getResponseDigest() != null) { + result = getResponseDigest().equals(that.getResponseDigest()); + } else { + result = (that.getResponseDigest() == null); + } + } + } + } + + return result; + } + + /** + * Returns the client nonce. + * + * @return The client nonce. + */ + public String getClientNonce() { + return this.clientNonce; + } + + /** + * Returns the next server nonce. This is the nonce the server wishes the client to use for a + * future authentication response + * + * @return The next nonce value. + */ + public String getNextServerNonce() { + return this.nextServerNonce; + } + + /** + * Returns the nonce-count value. + * + * @return The nonce-count value. + */ + public int getNonceCount() { + return this.nonceCount; + } + + /** + * Returns the quality of protection. The value can be {@link + * ChallengeMessage#QUALITY_AUTHENTICATION} for authentication or {@link + * ChallengeMessage#QUALITY_AUTHENTICATION_INTEGRITY} for authentication with integrity + * protection. + * + * @return The quality of protection. + */ + public String getQuality() { + return this.quality; + } + + /** + * Returns the optional response digest for mutual authentication. Note that when used with HTTP + * connectors, this property maps to the "response-digest" value in the "response-auth" + * directive of the "Authentication-Info" header. + * + * @return The optional response digest for mutual authentication. + */ + public String getResponseDigest() { + return this.responseDigest; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode( + getNextServerNonce(), + getNonceCount(), + getClientNonce(), + getQuality(), + getResponseDigest()); + } + + /** + * Sets the client nonce. + * + * @param clientNonce The client nonce. + */ + public void setClientNonce(String clientNonce) { + this.clientNonce = clientNonce; + } + + /** + * Sets the next server nonce. This is the nonce the server wishes the client to use for a + * future authentication response + * + * @param nextNonce The next nonce. + */ + public void setNextServerNonce(String nextNonce) { + this.nextServerNonce = nextNonce; + } + + /** + * Sets the nonce-count value. + * + * @param nonceCount The nonceCount value. + */ + public void setNonceCount(int nonceCount) { + this.nonceCount = nonceCount; + } + + /** + * Sets the quality of protection. The value can be {@link + * ChallengeMessage#QUALITY_AUTHENTICATION} for authentication or {@link + * ChallengeMessage#QUALITY_AUTHENTICATION_INTEGRITY} for authentication with integrity + * protection. + * + * @param qop The quality of protection. + */ + public void setQuality(String qop) { + this.quality = qop; + } + + /** + * Sets the optional response digest for mutual authentication. Note that when used with HTTP + * connectors, this property maps to the "response-digest" value in the "response-auth" + * directive of the "Authentication-Info" header. + * + * @param responseDigest The response digest. + */ + public void setResponseDigest(String responseDigest) { + this.responseDigest = responseDigest; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/CacheDirective.java b/org.restlet/src/main/java/org/restlet/data/CacheDirective.java index 9ddd8d6622..b1e27ca1e0 100644 --- a/org.restlet/src/main/java/org/restlet/data/CacheDirective.java +++ b/org.restlet/src/main/java/org/restlet/data/CacheDirective.java @@ -1,461 +1,432 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.List; +import java.util.Objects; import org.restlet.engine.header.HeaderConstants; import org.restlet.engine.util.SystemUtils; import org.restlet.util.NamedValue; -import java.util.List; -import java.util.Objects; - /** - * Directive for caching mechanisms along the call chain. This overrides the - * default behavior of those caches and proxies.
+ * Directive for caching mechanisms along the call chain. This overrides the default behavior of + * those caches and proxies.
*
- * Note that when used with HTTP connectors, this class maps to the - * "Cache-Control" header. - * + * Note that when used with HTTP connectors, this class maps to the "Cache-Control" header. + * * @author Jerome Louvel */ public final class CacheDirective implements NamedValue { - /** - * Creates a "max-age" directive. Indicates that the client is willing to accept - * a response whose age is no greater than the specified time in seconds. Unless - * "max-stale" directive is also included, the client is not willing to accept a - * stale response.
- *
- * Note that this directive can be used on requests or responses. - * - * @param maxAge Maximum age in seconds. - * @return A new "max-age" directive. - * @see HTTP - * 1.1 - Modifications of the Basic Expiration Mechanism - * @see HTTP - * 1.1 - Cache Revalidation and Reload Controls - */ - public static CacheDirective maxAge(int maxAge) { - return new CacheDirective(HeaderConstants.CACHE_MAX_AGE, Integer.toString(maxAge), true); - } - - /** - * Creates a "max-stale" directive. Indicates that the client is willing to - * accept a response that has exceeded its expiration time by any amount of - * time.
- *
- * Note that this directive can be used on requests only. - * - * @return A new "max-stale" directive. - * @see HTTP - * 1.1 - Modifications of the Basic Expiration Mechanism - */ - public static CacheDirective maxStale() { - return new CacheDirective(HeaderConstants.CACHE_MAX_STALE); - } - - /** - * Creates a "max-stale" directive. Indicates that the client is willing to - * accept a response that has exceeded its expiration time by a given amount of - * time.
- *
- * Note that this directive can be used on requests only. - * - * @param maxStale Maximum stale age in seconds. - * @return A new "max-stale" directive. - * @see HTTP - * 1.1 - Modifications of the Basic Expiration Mechanism - */ - public static CacheDirective maxStale(int maxStale) { - return new CacheDirective(HeaderConstants.CACHE_MAX_STALE, Integer.toString(maxStale), true); - } - - /** - * Creates a "min-fresh" directive. Indicates that the client is willing to - * accept a response whose freshness lifetime is no less than its current age - * plus the specified time in seconds. That is, the client wants a response that - * will still be fresh for at least the specified number of seconds.
- *
- * Note that this directive can be used on requests only. - * - * @param minFresh Minimum freshness lifetime in seconds. - * @return A new "min-fresh" directive. - * @see HTTP - * 1.1 - Modifications of the Basic Expiration Mechanism - */ - public static CacheDirective minFresh(int minFresh) { - return new CacheDirective(HeaderConstants.CACHE_MIN_FRESH, Integer.toString(minFresh), true); - } - - /** - * Creates a "must-revalidate" directive. Indicates that the origin server - * requires revalidation of a cache entry on any subsequent use.
- *
- * Note that this directive can be used on responses only. - * - * @return A new "must-revalidate" directive. - * @see HTTP - * 1.1 - Cache Revalidation and Reload Controls - */ - public static CacheDirective mustRevalidate() { - return new CacheDirective(HeaderConstants.CACHE_MUST_REVALIDATE); - } - - /** - * Creates a "no-cache" directive. Indicates that a cache must not use the - * response to satisfy subsequent requests without successful revalidation with - * the origin server.
- *
- * Note that this directive can be used on requests or responses. - * - * @return A new "no-cache" directive. - * @see HTTP - * 1.1 - What is Cacheable - */ - public static CacheDirective noCache() { - return new CacheDirective(HeaderConstants.CACHE_NO_CACHE); - } - - /** - * Creates a "no-cache" directive. Indicates that a cache must not use the - * response to satisfy subsequent requests without successful revalidation with - * the origin server.
- *
- * Note that this directive can be used on requests or responses. - * - * @param fieldNames Field names, typically a HTTP header name, that must not be - * sent by caches. - * @return A new "no-cache" directive. - * @see HTTP - * 1.1 - What is Cacheable - */ - public static CacheDirective noCache(List fieldNames) { - StringBuilder sb = new StringBuilder(); - - if (fieldNames != null) { - for (int i = 0; i < fieldNames.size(); i++) { - sb.append("\"").append(fieldNames.get(i)).append("\""); - - if (i < fieldNames.size() - 1) { - sb.append(','); - } - } - } - - return new CacheDirective(HeaderConstants.CACHE_NO_CACHE, sb.toString()); - } - - /** - * Creates a "no-cache" directive. Indicates that a cache must not use the - * response to satisfy subsequent requests without successful revalidation with - * the origin server.
- *
- * Note that this directive can be used on requests or responses. - * - * @param fieldName A field name, typically a HTTP header name, that must not be - * sent by caches. - * @return A new "no-cache" directive. - * @see HTTP - * 1.1 - What is Cacheable - */ - public static CacheDirective noCache(String fieldName) { - return new CacheDirective(HeaderConstants.CACHE_NO_CACHE, "\"" + fieldName + "\""); - } - - /** - * Creates a "no-store" directive. Indicates that a cache must not release or - * retain any information about the call. This applies to both private and - * shared caches.
- *
- * Note that this directive can be used on requests or responses. - * - * @return A new "no-store" directive. - * @see HTTP - * 1.1 - What May be Stored by Caches - */ - public static CacheDirective noStore() { - return new CacheDirective(HeaderConstants.CACHE_NO_STORE); - } - - /** - * Creates a "no-transform" directive. Indicates that a cache or intermediary - * proxy must not transform the response entity.
- *
- * Note that this directive can be used on requests or responses. - * - * @return A new "no-transform" directive. - * @see HTTP - * 1.1 - No-Transform Directive - */ - public static CacheDirective noTransform() { - return new CacheDirective(HeaderConstants.CACHE_NO_TRANSFORM); - } - - /** - * Creates a "onlyIfCached" directive. Indicates that only cached responses - * should be returned to the client.
- *
- * Note that this directive can be used on requests only. - * - * @return A new "only-if-cached" directive. - * @see HTTP - * 1.1 - Cache Revalidation and Reload Controls - */ - public static CacheDirective onlyIfCached() { - return new CacheDirective(HeaderConstants.CACHE_ONLY_IF_CACHED); - } - - /** - * Creates a "private" directive. Indicates that all or part of the response - * message is intended for a single user and must not be cached by a shared - * cache.
- *
- * Note that this directive can be used on responses only. - * - * @return A new "private" directive. - * @see HTTP - * 1.1 - What is Cacheable - */ - public static CacheDirective privateInfo() { - return new CacheDirective(HeaderConstants.CACHE_PRIVATE); - } - - /** - * Creates a "private" directive. Indicates that all or part of the response - * message is intended for a single user and must not be cached by a shared - * cache.
- *
- * Note that this directive can be used on responses only. - * - * @param fieldNames Field names, typically a HTTP header name, that must be - * private. - * @return A new "private" directive. - * @see HTTP - * 1.1 - What is Cacheable - */ - public static CacheDirective privateInfo(List fieldNames) { - StringBuilder sb = new StringBuilder(); - - if (fieldNames != null) { - for (int i = 0; i < fieldNames.size(); i++) { - sb.append("\"").append(fieldNames.get(i)).append("\""); - - if (i < fieldNames.size() - 1) { - sb.append(','); - } - } - } - - return new CacheDirective(HeaderConstants.CACHE_PRIVATE, sb.toString()); - } - - /** - * Creates a "private" directive. Indicates that all or part of the response - * message is intended for a single user and must not be cached by a shared - * cache.
- *
- * Note that this directive can be used on responses only. - * - * @param fieldName A field name, typically a HTTP header name, that is private. - * @return A new "private" directive. - * @see HTTP - * 1.1 - What is Cacheable - */ - public static CacheDirective privateInfo(String fieldName) { - return new CacheDirective(HeaderConstants.CACHE_PRIVATE, "\"" + fieldName + "\""); - } - - /** - * Creates a "proxy-revalidate" directive. Indicates that the origin server - * requires revalidation of a cache entry on any subsequent use, except that it - * does not apply to non-shared user agent caches
- *
- * Note that this directive can be used on responses only. - * - * @return A new "proxy-revalidate" directive. - * @see HTTP - * 1.1 - Cache Revalidation and Reload Controls - */ - public static CacheDirective proxyMustRevalidate() { - return new CacheDirective(HeaderConstants.CACHE_PROXY_MUST_REVALIDATE); - } - - /** - * Creates a "public" directive. Indicates that the response may be cached by - * any cache, even if it would normally be non-cacheable or cacheable only - * within a non-shared cache.
- *
- * Note that this directive can be used on responses only. - * - * @return A new "public" directive. - * @see HTTP - * 1.1 - What is Cacheable - */ - public static CacheDirective publicInfo() { - return new CacheDirective(HeaderConstants.CACHE_PUBLIC); - } - - /** - * Creates a "s-maxage" directive. Indicates that the client is willing to - * accept a response from a shared cache (but not a private cache) whose age is - * no greater than the specified time in seconds.
- *
- * Note that this directive can be used on responses only. - * - * @param sharedMaxAge Maximum age in seconds. - * @return A new "s-maxage" directive. - * @see HTTP - * 1.1 - Modifications of the Basic Expiration Mechanism - */ - public static CacheDirective sharedMaxAge(int sharedMaxAge) { - return new CacheDirective(HeaderConstants.CACHE_SHARED_MAX_AGE, Integer.toString(sharedMaxAge), true); - } - - /** Indicates if the directive is a digit value. */ - private boolean digit; - - /** The name. */ - private volatile String name; - - /** The value. */ - private volatile String value; - - /** - * Constructor for directives with no value. - * - * @param name The directive name. - */ - public CacheDirective(String name) { - this(name, null); - } - - /** - * Constructor for directives with a value. - * - * @param name The directive name. - * @param value The directive value. - */ - public CacheDirective(String name, String value) { - this(name, value, false); - } - - /** - * Constructor for directives with a value. - * - * @param name The directive name. - * @param value The directive value. - * @param digit The kind of value (true for a digit value, false otherwise). - */ - public CacheDirective(String name, String value, boolean digit) { - this.name = name; - this.value = value; - this.digit = digit; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (!(obj instanceof CacheDirective)) { - return false; - } - - CacheDirective that = (CacheDirective) obj; - - return Objects.equals(getName(), that.getName()) && Objects.equals(getValue(), that.getValue()) - && (this.digit == that.digit); - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the value. - * - * @return The value. - */ - public String getValue() { - return value; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(getName(), getValue(), isDigit()); - } - - /** - * Returns true if the directive contains a digit value. - * - * @return True if the directive contains a digit value. - */ - public boolean isDigit() { - return digit; - } - - /** - * Indicates if the directive is a digit value. - * - * @param digit True if the directive contains a digit value. - */ - public void setDigit(boolean digit) { - this.digit = digit; - } - - /** - * Sets the name. - * - * @param name The name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Sets the value. - * - * @param value The value. - */ - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "CacheDirective [digit=" + digit + ", name=" + name + ", value=" + value + "]"; - } + /** + * Creates a "max-age" directive. Indicates that the client is willing to accept a response + * whose age is not greater than the specified time in seconds. Unless the "max-stale" directive + * is also included, the client is not willing to accept a stale response.
+ *
+ * Note that this directive can be used on requests or responses. + * + * @param maxAge Maximum age in seconds. + * @return A new "max-age" directive. + * @see HTTP 1.1 - + * Modifications of the Basic Expiration Mechanism + * @see HTTP 1.1 - + * Cache Revalidation and Reload Controls + */ + public static CacheDirective maxAge(int maxAge) { + return new CacheDirective(HeaderConstants.CACHE_MAX_AGE, Integer.toString(maxAge), true); + } + + /** + * Creates a "max-stale" directive. Indicates that the client is willing to accept a response + * that has exceeded its expiration time by any amount of time.
+ *
+ * Note that this directive can be used on requests only. + * + * @return A new "max-stale" directive. + * @see HTTP 1.1 - + * Modifications of the Basic Expiration Mechanism + */ + public static CacheDirective maxStale() { + return new CacheDirective(HeaderConstants.CACHE_MAX_STALE); + } + + /** + * Creates a "max-stale" directive. Indicates that the client is willing to accept a response + * that has exceeded its expiration time by a given amount of time.
+ *
+ * Note that this directive can be used on requests only. + * + * @param maxStale Maximum stale age in seconds. + * @return A new "max-stale" directive. + * @see HTTP 1.1 - + * Modifications of the Basic Expiration Mechanism + */ + public static CacheDirective maxStale(int maxStale) { + return new CacheDirective( + HeaderConstants.CACHE_MAX_STALE, Integer.toString(maxStale), true); + } + + /** + * Creates a "min-fresh" directive. Indicates that the client is willing to accept a response + * whose freshness lifetime is no less than its current age plus the specified time in seconds. + * That is, the client wants a response that will still be fresh for at least the specified + * number of seconds.
+ *
+ * Note that this directive can be used on requests only. + * + * @param minFresh Minimum freshness lifetime in seconds. + * @return A new "min-fresh" directive. + * @see HTTP 1.1 - + * Modifications of the Basic Expiration Mechanism + */ + public static CacheDirective minFresh(int minFresh) { + return new CacheDirective( + HeaderConstants.CACHE_MIN_FRESH, Integer.toString(minFresh), true); + } + + /** + * Creates a "must-revalidate" directive. Indicates that the origin server requires revalidation + * of a cache entry on any later use.
+ *
+ * Note that this directive can be used on responses only. + * + * @return A new "must-revalidate" directive. + * @see HTTP 1.1 - + * Cache Revalidation and Reload Controls + */ + public static CacheDirective mustRevalidate() { + return new CacheDirective(HeaderConstants.CACHE_MUST_REVALIDATE); + } + + /** + * Creates a "no-cache" directive. Indicates that a cache must not use the response to satisfy + * later requests without successful revalidation with the origin server.
+ *
+ * Note that this directive can be used on requests or responses. + * + * @return A new "no-cache" directive. + * @see HTTP 1.1 - + * What is Cacheable + */ + public static CacheDirective noCache() { + return new CacheDirective(HeaderConstants.CACHE_NO_CACHE); + } + + /** + * Creates a "no-cache" directive. Indicates that a cache must not use the response to satisfy + * later requests without successful revalidation with the origin server.
+ *
+ * Note that this directive can be used on requests or responses. + * + * @param fieldNames Field names, typically an HTTP header name, that must not be sent by + * caches. + * @return A new "no-cache" directive. + * @see HTTP 1.1 - + * What is Cacheable + */ + public static CacheDirective noCache(List fieldNames) { + StringBuilder sb = new StringBuilder(); + + if (fieldNames != null) { + for (int i = 0; i < fieldNames.size(); i++) { + sb.append("\"").append(fieldNames.get(i)).append("\""); + + if (i < fieldNames.size() - 1) { + sb.append(','); + } + } + } + + return new CacheDirective(HeaderConstants.CACHE_NO_CACHE, sb.toString()); + } + + /** + * Creates a "no-cache" directive. Indicates that a cache must not use the response to satisfy + * later requests without successful revalidation with the origin server.
+ *
+ * Note that this directive can be used on requests or responses. + * + * @param fieldName A field name, typically an HTTP header name, that must not be sent by + * caches. + * @return A new "no-cache" directive. + * @see HTTP 1.1 - + * What is Cacheable + */ + public static CacheDirective noCache(String fieldName) { + return new CacheDirective(HeaderConstants.CACHE_NO_CACHE, "\"" + fieldName + "\""); + } + + /** + * Creates a "no-store" directive. Indicates that a cache must not release or retain any + * information about the call. This applies to both private and shared caches.
+ *
+ * Note that this directive can be used on requests or responses. + * + * @return A new "no-store" directive. + * @see HTTP 1.1 - + * What May be Stored by Caches + */ + public static CacheDirective noStore() { + return new CacheDirective(HeaderConstants.CACHE_NO_STORE); + } + + /** + * Creates a "no-transform" directive. Indicates that a cache or intermediary proxy must not + * transform the response entity.
+ *
+ * Note that this directive can be used on requests or responses. + * + * @return A new "no-transform" directive. + * @see HTTP 1.1 - + * No-Transform Directive + */ + public static CacheDirective noTransform() { + return new CacheDirective(HeaderConstants.CACHE_NO_TRANSFORM); + } + + /** + * Creates an "onlyIfCached" directive. Indicates that only cached responses should be returned + * to the client.
+ *
+ * Note that this directive can be used on requests only. + * + * @return A new "only-if-cached" directive. + * @see HTTP 1.1 - + * Cache Revalidation and Reload Controls + */ + public static CacheDirective onlyIfCached() { + return new CacheDirective(HeaderConstants.CACHE_ONLY_IF_CACHED); + } + + /** + * Creates a "private" directive. Indicates that all or part of the response message is intended + * for a single user and must not be cached by a shared cache.
+ *
+ * Note that this directive can be used on responses only. + * + * @return A new "private" directive. + * @see HTTP 1.1 - + * What is Cacheable + */ + public static CacheDirective privateInfo() { + return new CacheDirective(HeaderConstants.CACHE_PRIVATE); + } + + /** + * Creates a "private" directive. Indicates that all or part of the response message is intended + * for a single user and must not be cached by a shared cache.
+ *
+ * Note that this directive can be used on responses only. + * + * @param fieldNames Field names, typically an HTTP header name, that must be private. + * @return A new "private" directive. + * @see HTTP 1.1 - + * What is Cacheable + */ + public static CacheDirective privateInfo(List fieldNames) { + StringBuilder sb = new StringBuilder(); + + if (fieldNames != null) { + for (int i = 0; i < fieldNames.size(); i++) { + sb.append("\"").append(fieldNames.get(i)).append("\""); + + if (i < fieldNames.size() - 1) { + sb.append(','); + } + } + } + + return new CacheDirective(HeaderConstants.CACHE_PRIVATE, sb.toString()); + } + + /** + * Creates a "private" directive. Indicates that all or part of the response message is intended + * for a single user and must not be cached by a shared cache.
+ *
+ * Note that this directive can be used on responses only. + * + * @param fieldName A field name, typically an HTTP header name, that is private. + * @return A new "private" directive. + * @see HTTP 1.1 - + * What is Cacheable + */ + public static CacheDirective privateInfo(String fieldName) { + return new CacheDirective(HeaderConstants.CACHE_PRIVATE, "\"" + fieldName + "\""); + } + + /** + * Creates a "proxy-revalidate" directive. Indicates that the origin server requires + * revalidation of a cache entry on any later use, except that it does not apply to non-shared + * user agent caches
+ *
+ * Note that this directive can be used on responses only. + * + * @return A new "proxy-revalidate" directive. + * @see HTTP 1.1 - + * Cache Revalidation and Reload Controls + */ + public static CacheDirective proxyMustRevalidate() { + return new CacheDirective(HeaderConstants.CACHE_PROXY_MUST_REVALIDATE); + } + + /** + * Creates a "public" directive. Indicates that any cache may cache the response, even if it + * would normally be non-cacheable or cacheable only within a non-shared cache.
+ *
+ * Note that this directive can be used on responses only. + * + * @return A new "public" directive. + * @see HTTP 1.1 - + * What is Cacheable + */ + public static CacheDirective publicInfo() { + return new CacheDirective(HeaderConstants.CACHE_PUBLIC); + } + + /** + * Creates an "s-maxage" directive. Indicates that the client is willing to accept a response + * from a shared cache (but not a private cache) whose age is not greater than the specified + * time in seconds.
+ *
+ * Note that this directive can be used on responses only. + * + * @param sharedMaxAge Maximum age in seconds. + * @return A new "s-maxage" directive. + * @see HTTP 1.1 - + * Modifications of the Basic Expiration Mechanism + */ + public static CacheDirective sharedMaxAge(int sharedMaxAge) { + return new CacheDirective( + HeaderConstants.CACHE_SHARED_MAX_AGE, Integer.toString(sharedMaxAge), true); + } + + /** Indicates if the directive is a digit value. */ + private boolean digit; + + /** The name. */ + private volatile String name; + + /** The value. */ + private volatile String value; + + /** + * Constructor for directives with no value. + * + * @param name The directive name. + */ + public CacheDirective(String name) { + this(name, null); + } + + /** + * Constructor for directives with a value. + * + * @param name The directive name. + * @param value The directive value. + */ + public CacheDirective(String name, String value) { + this(name, value, false); + } + + /** + * Constructor for directives with a value. + * + * @param name The directive name. + * @param value The directive value. + * @param digit The kind of value (true for a digit value, false otherwise). + */ + public CacheDirective(String name, String value, boolean digit) { + this.name = name; + this.value = value; + this.digit = digit; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof CacheDirective)) { + return false; + } + + CacheDirective that = (CacheDirective) obj; + + return Objects.equals(getName(), that.getName()) + && Objects.equals(getValue(), that.getValue()) + && (this.digit == that.digit); + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the value. + * + * @return The value. + */ + public String getValue() { + return value; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode(getName(), getValue(), isDigit()); + } + + /** + * Returns true if the directive contains a digit value. + * + * @return True if the directive contains a digit value. + */ + public boolean isDigit() { + return digit; + } + + /** + * Indicates if the directive is a digit value. + * + * @param digit True if the directive contains a digit value. + */ + public void setDigit(boolean digit) { + this.digit = digit; + } + + /** + * Sets the name. + * + * @param name The name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the value. + * + * @param value The value. + */ + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "CacheDirective [digit=" + digit + ", name=" + name + ", value=" + value + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java index 517d908b11..2f4e57e4d7 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java @@ -1,275 +1,272 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.Objects; import org.restlet.engine.util.SystemUtils; import org.restlet.util.Series; -import java.util.Objects; - /** - * Base authentication challenge message exchanged between an origin server and - * a client. - * + * Base authentication challenge message exchanged between an origin server and a client. + * * @author Jerome Louvel */ public abstract class ChallengeMessage { - /** Authentication quality. */ - public static final String QUALITY_AUTHENTICATION = "auth"; - - /** Authentication and integrity. */ - public static final String QUALITY_AUTHENTICATION_INTEGRITY = "auth-int"; - - /** The raw value for custom challenge schemes. */ - private volatile String rawValue; - - /** The additional scheme parameters. */ - private volatile Series parameters; - - /** The challenge scheme. */ - private volatile ChallengeScheme scheme; - - /** The server nonce. */ - private volatile String serverNonce; - - /** The authentication realm. */ - private volatile String realm; - - /** - * An opaque string of data which should be returned by the client unchanged. - */ - private volatile String opaque; - - /** The digest algorithm. */ - private volatile String digestAlgorithm; - - /** - * Constructor. - * - * @param scheme The challenge scheme. - */ - public ChallengeMessage(ChallengeScheme scheme) { - this(scheme, null, null); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param parameters The additional scheme parameters. - */ - public ChallengeMessage(ChallengeScheme scheme, Series parameters) { - this(scheme, null, null); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param realm The authentication realm. - */ - public ChallengeMessage(ChallengeScheme scheme, String realm) { - this(scheme, realm, null); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param realm The authentication realm. - * @param parameters The additional scheme parameters. - */ - public ChallengeMessage(ChallengeScheme scheme, String realm, Series parameters) { - this(scheme, realm, parameters, Digest.ALGORITHM_MD5, null, null); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param realm The authentication realm. - * @param parameters The additional scheme parameters. - * @param digestAlgorithm The digest algorithm. - * @param opaque An opaque string of data which should be returned by - * the client unchanged. - * @param serverNonce The server nonce. - */ - public ChallengeMessage(ChallengeScheme scheme, String realm, Series parameters, String digestAlgorithm, - String opaque, String serverNonce) { - super(); - this.parameters = parameters; - this.scheme = scheme; - this.serverNonce = serverNonce; - this.realm = realm; - this.opaque = opaque; - this.digestAlgorithm = digestAlgorithm; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof ChallengeMessage)) { - return false; - } - - final ChallengeMessage that = (ChallengeMessage) obj; - - return getParameters().equals(that.getParameters()) && Objects.equals(getRealm(), that.getRealm()) - && Objects.equals(getScheme(), that.getScheme()) - && Objects.equals(getServerNonce(), that.getServerNonce()) - && Objects.equals(getOpaque(), that.getOpaque()) - && Objects.equals(getDigestAlgorithm(), that.getDigestAlgorithm()); - } - - /** - * Returns the digest algorithm. See {@link Digest} class for DIGEST_* - * constants. Default value is {@link Digest#ALGORITHM_MD5}. - * - * @return The digest algorithm. - */ - public String getDigestAlgorithm() { - return digestAlgorithm; - } - - /** - * Returns an opaque string of data which should be returned by the client - * unchanged. - * - * @return An opaque string of data. - */ - public String getOpaque() { - return opaque; - } - - /** - * Returns the modifiable series of scheme parameters. Creates a new instance if - * no one has been set. - * - * @return The modifiable series of scheme parameters. - */ - public Series getParameters() { - if (this.parameters == null) { - this.parameters = new Series(Parameter.class); - } - - return this.parameters; - } - - /** - * Returns the raw challenge value. - * - * @return The raw challenge value. - */ - public String getRawValue() { - return this.rawValue; - } - - /** - * Returns the realm name. - * - * @return The realm name. - */ - public String getRealm() { - return this.realm; - } - - /** - * Returns the scheme used. - * - * @return The scheme used. - */ - public ChallengeScheme getScheme() { - return this.scheme; - } - - /** - * Returns the server nonce. - * - * @return The server nonce. - */ - public String getServerNonce() { - return serverNonce; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(getScheme(), getRealm(), getParameters()); - } - - /** - * Sets the digest algorithm. See {@link Digest} class for ALGORITHM_* - * constants. Default value is {@link Digest#ALGORITHM_MD5}. - * - * @param digestAlgorithm The digest algorithm. - */ - public void setDigestAlgorithm(String digestAlgorithm) { - this.digestAlgorithm = digestAlgorithm; - } - - /** - * Sets an opaque string of data which should be returned by the client - * unchanged. - * - * @param opaque An opaque string of data. - */ - public void setOpaque(String opaque) { - this.opaque = opaque; - } - - /** - * Sets the parameters. - * - * @param parameters The parameters. - */ - public void setParameters(Series parameters) { - this.parameters = parameters; - } - - /** - * Sets the raw value. - * - * @param rawValue The raw value. - */ - public void setRawValue(String rawValue) { - this.rawValue = rawValue; - } - - /** - * Sets the realm name. - * - * @param realm The realm name. - */ - public void setRealm(String realm) { - this.realm = realm; - } - - /** - * Sets the scheme used. - * - * @param scheme The scheme used. - */ - public void setScheme(ChallengeScheme scheme) { - this.scheme = scheme; - } - - /** - * Sets the server nonce. - * - * @param serverNonce The server nonce. - */ - public void setServerNonce(String serverNonce) { - this.serverNonce = serverNonce; - } - + /** Authentication quality. */ + public static final String QUALITY_AUTHENTICATION = "auth"; + + /** Authentication and integrity. */ + public static final String QUALITY_AUTHENTICATION_INTEGRITY = "auth-int"; + + /** The raw value for custom challenge schemes. */ + private volatile String rawValue; + + /** The additional scheme parameters. */ + private volatile Series parameters; + + /** The challenge scheme. */ + private volatile ChallengeScheme scheme; + + /** The server nonce. */ + private volatile String serverNonce; + + /** The authentication realm. */ + private volatile String realm; + + /** An opaque string of data which should be returned by the client unchanged. */ + private volatile String opaque; + + /** The digest algorithm. */ + private volatile String digestAlgorithm; + + /** + * Constructor. + * + * @param scheme The challenge scheme. + */ + public ChallengeMessage(ChallengeScheme scheme) { + this(scheme, null, null); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param parameters The additional scheme parameters. + */ + public ChallengeMessage(ChallengeScheme scheme, Series parameters) { + this(scheme, null, null); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param realm The authentication realm. + */ + public ChallengeMessage(ChallengeScheme scheme, String realm) { + this(scheme, realm, null); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param realm The authentication realm. + * @param parameters The additional scheme parameters. + */ + public ChallengeMessage(ChallengeScheme scheme, String realm, Series parameters) { + this(scheme, realm, parameters, Digest.ALGORITHM_MD5, null, null); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param realm The authentication realm. + * @param parameters The additional scheme parameters. + * @param digestAlgorithm The digest algorithm. + * @param opaque An opaque string of data which should be returned by the client unchanged. + * @param serverNonce The server nonce. + */ + public ChallengeMessage( + ChallengeScheme scheme, + String realm, + Series parameters, + String digestAlgorithm, + String opaque, + String serverNonce) { + super(); + this.parameters = parameters; + this.scheme = scheme; + this.serverNonce = serverNonce; + this.realm = realm; + this.opaque = opaque; + this.digestAlgorithm = digestAlgorithm; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ChallengeMessage)) { + return false; + } + + final ChallengeMessage that = (ChallengeMessage) obj; + + return getParameters().equals(that.getParameters()) + && Objects.equals(getRealm(), that.getRealm()) + && Objects.equals(getScheme(), that.getScheme()) + && Objects.equals(getServerNonce(), that.getServerNonce()) + && Objects.equals(getOpaque(), that.getOpaque()) + && Objects.equals(getDigestAlgorithm(), that.getDigestAlgorithm()); + } + + /** + * Returns the digest algorithm. See {@link Digest} class for DIGEST_* constants. Default value + * is {@link Digest#ALGORITHM_MD5}. + * + * @return The digest algorithm. + */ + public String getDigestAlgorithm() { + return digestAlgorithm; + } + + /** + * Returns an opaque string of data which should be returned by the client unchanged. + * + * @return An opaque string of data. + */ + public String getOpaque() { + return opaque; + } + + /** + * Returns the modifiable series of scheme parameters. Creates a new instance if no one has been + * set. + * + * @return The modifiable series of scheme parameters. + */ + public Series getParameters() { + if (this.parameters == null) { + this.parameters = new Series(Parameter.class); + } + + return this.parameters; + } + + /** + * Returns the raw challenge value. + * + * @return The raw challenge value. + */ + public String getRawValue() { + return this.rawValue; + } + + /** + * Returns the realm name. + * + * @return The realm name. + */ + public String getRealm() { + return this.realm; + } + + /** + * Returns the scheme used. + * + * @return The scheme used. + */ + public ChallengeScheme getScheme() { + return this.scheme; + } + + /** + * Returns the server nonce. + * + * @return The server nonce. + */ + public String getServerNonce() { + return serverNonce; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode(getScheme(), getRealm(), getParameters()); + } + + /** + * Sets the digest algorithm. See {@link Digest} class for ALGORITHM_* constants. Default value + * is {@link Digest#ALGORITHM_MD5}. + * + * @param digestAlgorithm The digest algorithm. + */ + public void setDigestAlgorithm(String digestAlgorithm) { + this.digestAlgorithm = digestAlgorithm; + } + + /** + * Sets an opaque string of data which should be returned by the client unchanged. + * + * @param opaque An opaque string of data. + */ + public void setOpaque(String opaque) { + this.opaque = opaque; + } + + /** + * Sets the parameters. + * + * @param parameters The parameters. + */ + public void setParameters(Series parameters) { + this.parameters = parameters; + } + + /** + * Sets the raw value. + * + * @param rawValue The raw value. + */ + public void setRawValue(String rawValue) { + this.rawValue = rawValue; + } + + /** + * Sets the realm name. + * + * @param realm The realm name. + */ + public void setRealm(String realm) { + this.realm = realm; + } + + /** + * Sets the scheme used. + * + * @param scheme The scheme used. + */ + public void setScheme(ChallengeScheme scheme) { + this.scheme = scheme; + } + + /** + * Sets the server nonce. + * + * @param serverNonce The server nonce. + */ + public void setServerNonce(String serverNonce) { + this.serverNonce = serverNonce; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java index 0185a2a026..c82bcb579b 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java @@ -1,185 +1,177 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.engine.util.SystemUtils; - import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.engine.util.SystemUtils; /** - * Authentication challenge sent by an origin server to a client. Upon reception - * of this request, the client should send a new request with the proper - * {@link ChallengeResponse} set.
+ * Authentication challenge sent by an origin server to a client. Upon reception of this request, + * the client should send a new request with the proper {@link ChallengeResponse} set.
*
- * Note that when used with HTTP connectors, this class maps to the - * "WWW-Authenticate" header. - * + * Note that when used with HTTP connectors, this class maps to the "WWW-Authenticate" header. + * * @author Jerome Louvel */ public final class ChallengeRequest extends ChallengeMessage { - /** The available options for quality of protection. */ - private volatile List qualityOptions; - - /** The URI references that define the protection domains. */ - private volatile List domainRefs; - - /** Indicates if the previous request from the client was stale. */ - private volatile boolean stale; - - /** - * Constructor. - * - * @param scheme The challenge scheme. - */ - public ChallengeRequest(ChallengeScheme scheme) { - this(scheme, null); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param realm The authentication realm. - */ - public ChallengeRequest(ChallengeScheme scheme, String realm) { - super(scheme, realm); - this.domainRefs = null; - this.qualityOptions = null; - this.stale = false; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof ChallengeRequest that)) { - return false; - } - - return getParameters().equals(that.getParameters()) - && Objects.equals(getRealm(), that.getRealm()) - && Objects.equals(getScheme(), that.getScheme()); - } - - /** - * Returns the base URI references that collectively define the protected - * domains for the digest authentication. By default, it returns a list with a - * single "/" URI reference. - * - * @return The base URI references. - */ - public List getDomainRefs() { - // Lazy initialization with double-check. - List r = this.domainRefs; - if (r == null) { - synchronized (this) { - r = this.domainRefs; - if (r == null) { - this.domainRefs = r = new CopyOnWriteArrayList(); - this.domainRefs.add(new Reference("/")); - } - } - } - return r; - } - - /** - * Returns the available options for quality of protection. The default value is - * {@link #QUALITY_AUTHENTICATION}. - * - * @return The available options for quality of protection. - */ - public List getQualityOptions() { - // Lazy initialization with double-check. - List r = this.qualityOptions; - if (r == null) { - synchronized (this) { - r = this.qualityOptions; - if (r == null) { - this.qualityOptions = r = new CopyOnWriteArrayList(); - this.qualityOptions.add(QUALITY_AUTHENTICATION); - } - } - } - return r; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(super.hashCode(), qualityOptions, domainRefs, stale); - } - - /** - * Indicates if the previous request from the client was stale. - * - * @return True if the previous request from the client was stale. - */ - public boolean isStale() { - return stale; - } - - /** - * Sets the URI references that define the protection domains for the digest - * authentication. - * - * @param domainRefs The base URI references. - */ - public void setDomainRefs(List domainRefs) { - this.domainRefs = domainRefs; - } - - /** - * Sets the URI references that define the protection domains for the digest - * authentication. Note that the parameters are copied into a new - * {@link CopyOnWriteArrayList} instance. - * - * @param domainUris The base URI references. - * @see #setDomainRefs(List) - */ - public void setDomainUris(Collection domainUris) { - List domainRefs = null; - - if (domainUris != null) { - domainRefs = new CopyOnWriteArrayList(); - - for (String domainUri : domainUris) { - domainRefs.add(new Reference(domainUri)); - } - } - - setDomainRefs(domainRefs); - } - - /** - * Sets the available options for quality of protection. The default value is - * {@link #QUALITY_AUTHENTICATION}. - * - * @param qualityOptions The available options for quality of protection. - */ - public void setQualityOptions(List qualityOptions) { - this.qualityOptions = qualityOptions; - } - - /** - * Indicates if the previous request from the client was stale. - * - * @param stale True if the previous request from the client was stale. - */ - public void setStale(boolean stale) { - this.stale = stale; - } - + /** The available options for quality of protection. */ + private volatile List qualityOptions; + + /** The URI references that define the protection domains. */ + private volatile List domainRefs; + + /** Indicates if the previous request from the client was stale. */ + private volatile boolean stale; + + /** + * Constructor. + * + * @param scheme The challenge scheme. + */ + public ChallengeRequest(ChallengeScheme scheme) { + this(scheme, null); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param realm The authentication realm. + */ + public ChallengeRequest(ChallengeScheme scheme, String realm) { + super(scheme, realm); + this.domainRefs = null; + this.qualityOptions = null; + this.stale = false; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ChallengeRequest that)) { + return false; + } + + return getParameters().equals(that.getParameters()) + && Objects.equals(getRealm(), that.getRealm()) + && Objects.equals(getScheme(), that.getScheme()); + } + + /** + * Returns the base URI references that collectively define the protected domains for the digest + * authentication. By default, it returns a list with a single "/" URI reference. + * + * @return The base URI references. + */ + public List getDomainRefs() { + // Lazy initialization with double-check. + List r = this.domainRefs; + if (r == null) { + synchronized (this) { + r = this.domainRefs; + if (r == null) { + this.domainRefs = r = new CopyOnWriteArrayList(); + this.domainRefs.add(new Reference("/")); + } + } + } + return r; + } + + /** + * Returns the available options for quality of protection. The default value is {@link + * #QUALITY_AUTHENTICATION}. + * + * @return The available options for quality of protection. + */ + public List getQualityOptions() { + // Lazy initialization with double-check. + List r = this.qualityOptions; + if (r == null) { + synchronized (this) { + r = this.qualityOptions; + if (r == null) { + this.qualityOptions = r = new CopyOnWriteArrayList(); + this.qualityOptions.add(QUALITY_AUTHENTICATION); + } + } + } + return r; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode(super.hashCode(), qualityOptions, domainRefs, stale); + } + + /** + * Indicates if the previous request from the client was stale. + * + * @return True if the previous request from the client was stale. + */ + public boolean isStale() { + return stale; + } + + /** + * Sets the URI references that define the protection domains for the digest authentication. + * + * @param domainRefs The base URI references. + */ + public void setDomainRefs(List domainRefs) { + this.domainRefs = domainRefs; + } + + /** + * Sets the URI references that define the protection domains for the digest authentication. + * Note that the parameters are copied into a new {@link CopyOnWriteArrayList} instance. + * + * @param domainUris The base URI references. + * @see #setDomainRefs(List) + */ + public void setDomainUris(Collection domainUris) { + List domainRefs = null; + + if (domainUris != null) { + domainRefs = new CopyOnWriteArrayList(); + + for (String domainUri : domainUris) { + domainRefs.add(new Reference(domainUri)); + } + } + + setDomainRefs(domainRefs); + } + + /** + * Sets the available options for quality of protection. The default value is {@link + * #QUALITY_AUTHENTICATION}. + * + * @param qualityOptions The available options for quality of protection. + */ + public void setQualityOptions(List qualityOptions) { + this.qualityOptions = qualityOptions; + } + + /** + * Indicates if the previous request from the client was stale. + * + * @param stale True if the previous request from the client was stale. + */ + public void setStale(boolean stale) { + this.stale = stale; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java index d5ac4de161..363485e2e8 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java @@ -1,413 +1,448 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.Arrays; +import java.util.Objects; import org.restlet.Request; import org.restlet.Response; import org.restlet.engine.util.SystemUtils; import org.restlet.util.Series; -import java.util.Arrays; -import java.util.Objects; - /** - * Authentication response sent by client to an origin server. This is typically - * following a {@link ChallengeRequest} sent by the origin server to the - * client.
+ * Authentication response sent by client to an origin server. This is typically following a {@link + * ChallengeRequest} sent by the origin server to the client.
*
- * Sometimes, it might be faster to preemptively issue a challenge response if - * the client knows for sure that the target resource will require - * authentication.
+ * Sometimes, it might be faster to preemptively issue a challenge response if the client knows for + * sure that the target resource will require authentication.
*
- * Note that when used with HTTP connectors, this class maps to the - * "Authorization" header. - * + * Note that when used with HTTP connectors, this class maps to the "Authorization" header. + * * @author Jerome Louvel */ public final class ChallengeResponse extends ChallengeMessage { - /** The client nonce value. */ - private volatile String clientNonce; - - /** - * The {@link Request#getResourceRef()} value duplicated here in case a proxy - * changed it. - */ - private volatile Reference digestRef; - - /** The user identifier, such as a login name or an access key. */ - private volatile String identifier; - - /** The chosen quality of protection. */ - private volatile String quality; - - /** The user secret, such as a password or a secret key. */ - private volatile char[] secret; - - /** The digest algorithm name optionally applied on the user secret. */ - private volatile String secretAlgorithm; - - /** The server nonce count. */ - private volatile int serverNonceCount; - - /** - * The time when the response was issued, as returned by - * {@link System#currentTimeMillis()}. - */ - private volatile long timeIssued; - - /** - * Constructor. It leverages the latest server response and challenge request - * to compute the credentials. - * - * @param challengeRequest The challenge request sent by the origin server. - * @param response The latest server response. - * @param identifier The user identifier, such as a login name or an - * access key. - * @param secret The user secret, such as a password or a secret key, - * with no digest applied. - */ - public ChallengeResponse(ChallengeRequest challengeRequest, Response response, String identifier, char[] secret) { - this(challengeRequest, response, identifier, secret, Digest.ALGORITHM_NONE); - } - - /** - * Constructor. It leverages the latest server response and challenge request in - * order to compute the credentials. - * - * @param challengeRequest The challenge request sent by the origin server. - * @param response The latest server response. - * @param identifier The user identifier, such as a login name or an - * access key. - * @param secret The user secret used to compute the secret, with an - * optional digest applied. - * @param secretAlgorithm The digest algorithm of the user secret (see - * {@link Digest} class). - */ - public ChallengeResponse(ChallengeRequest challengeRequest, Response response, String identifier, char[] secret, - String secretAlgorithm) { - this(challengeRequest.getScheme(), null, identifier, secret, secretAlgorithm, null, null, null, null, null, - null, null, 0, 0L); - org.restlet.engine.security.AuthenticatorUtils.update(this, response.getRequest(), response); - } - - /** - * Constructor. It leverages the latest server response and challenge request - * to compute the credentials. - * - * @param challengeRequest The challenge request sent by the origin server. - * @param response The latest server response. - * @param identifier The user identifier, such as a login name or an - * access key. - * @param secret The user secret, such as a password or a secret key. - */ - public ChallengeResponse(ChallengeRequest challengeRequest, Response response, String identifier, String secret) { - this(challengeRequest, response, identifier, secret.toCharArray(), Digest.ALGORITHM_NONE); - } - - /** - * Constructor with no credentials. - * - * @param scheme The challenge scheme. - */ - public ChallengeResponse(ChallengeScheme scheme) { - this(scheme, null, (char[]) null); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param parameters The additional scheme parameters. - * @param identifier The user identifier, such as a login name or an - * access key. - * @param secret The user secret, such as a password or a secret key. - * @param secretAlgorithm The digest algorithm name optionally applied on the - * user secret. - * @param realm The authentication realm. - * @param quality The chosen quality of protection. - * @param digestRef The {@link Request#getResourceRef()} value - * duplicated here in case a proxy changed it. - * @param digestAlgorithm The digest algorithm. - * @param opaque An opaque string of data which should be returned by - * the client unchanged. - * @param clientNonce The client nonce value. - * @param serverNonce The server nonce. - * @param serverNonceCount The server nonce count. - * @param timeIssued The time when the response was issued, as returned - * by {@link System#currentTimeMillis()}. - */ - public ChallengeResponse(ChallengeScheme scheme, Series parameters, String identifier, char[] secret, - String secretAlgorithm, String realm, String quality, Reference digestRef, String digestAlgorithm, - String opaque, String clientNonce, String serverNonce, int serverNonceCount, long timeIssued) { - super(scheme, realm, parameters, digestAlgorithm, opaque, serverNonce); - this.clientNonce = clientNonce; - this.digestRef = digestRef; - this.identifier = identifier; - this.quality = quality; - this.secret = secret; - this.secretAlgorithm = secretAlgorithm; - this.serverNonceCount = serverNonceCount; - this.timeIssued = timeIssued; - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param identifier The user identifier, such as a login name or an access key. - * @param secret The user secret, such as a password or a secret key. - */ - public ChallengeResponse(ChallengeScheme scheme, String identifier, char[] secret) { - this(scheme, identifier, secret, null); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param identifier The user identifier, such as a login name or an access key. - * @param parameters The additional scheme parameters. - */ - public ChallengeResponse(ChallengeScheme scheme, String identifier, char[] secret, Series parameters) { - this(scheme, parameters, identifier, secret, Digest.ALGORITHM_NONE, null, null, null, null, null, null, null, 0, - 0L); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param identifier The user identifier, such as a login name or an access key. - * @param parameters The additional scheme parameters. - */ - public ChallengeResponse(ChallengeScheme scheme, String identifier, Series parameters) { - this(scheme, identifier, null, parameters); - } - - /** - * Constructor. - * - * @param scheme The challenge scheme. - * @param identifier The user identifier, such as a login name or an access key. - * @param secret The user secret, such as a password or a secret key. - */ - public ChallengeResponse(ChallengeScheme scheme, String identifier, String secret) { - this(scheme, identifier, (secret != null) ? secret.toCharArray() : null); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - // if obj == this no need to go further - if (obj == this) { - return true; - } - - // if obj isn't a challenge request or is null don't evaluate further - if (!(obj instanceof ChallengeResponse that)) { - return false; - } - - if (!Objects.equals(getRawValue(), that.getRawValue()) - || !Objects.equals(getIdentifier(), that.getIdentifier()) - || !Objects.equals(getScheme(), that.getScheme())) { - return false; - } - - return Arrays.equals(getSecret(), that.getSecret()); - } - - /** - * Returns the client nonce. - * - * @return The client nonce. - */ - public String getClientNonce() { - return this.clientNonce; - } - - /** - * Returns the {@link Request#getResourceRef()} value duplicated here in case a - * proxy changed it. - * - * @return The digest URI reference. - */ - public Reference getDigestRef() { - return digestRef; - } - - /** - * Returns the user identifier, such as a login name or an access key. - * - * @return The user identifier, such as a login name or an access key. - */ - public String getIdentifier() { - return this.identifier; - } - - /** - * Gets the principal associated to the identifier property. - * - * @return The principal associated to the identifier property. - */ - public java.security.Principal getPrincipal() { - return this::getIdentifier; - } - - /** - * Returns the chosen quality of protection. - * - * @return The chosen quality of protection. - */ - public String getQuality() { - return quality; - } - - /** - * Returns the user secret, such as a password or a secret key. - * - * It is not recommended to use {@link String#String(char[])} for security - * reasons. - * - * @return The user secret, such as a password or a secret key. - */ - public char[] getSecret() { - return this.secret; - } - - /** - * Returns the digest algorithm name optionally applied on the user secret. - * - * @return The digest algorithm name optionally applied on the user secret. - */ - public String getSecretAlgorithm() { - return secretAlgorithm; - } - - /** - * Returns the server nonce count. - * - * @return The server nonce count. - */ - public int getServerNonceCount() { - return serverNonceCount; - } - - /** - * Returns the server nonce count as a hexadecimal string of eight characters. - * - * @return The server nonce count as a hexadecimal string. - */ - public String getServerNonceCountAsHex() { - return org.restlet.engine.security.AuthenticatorUtils.formatNonceCount(getServerNonceCount()); - } - - /** - * Returns the time when the response was issued, as returned by - * {@link System#currentTimeMillis()}. - * - * @return The time when the response was issued. - */ - public long getTimeIssued() { - return timeIssued; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - // Note that the secret is simply discarded from hash code calculation - // because we don't want it to be materialized as a string - return SystemUtils.hashCode(getScheme(), getIdentifier(), getRawValue()); - } - - /** - * Sets the client nonce. - * - * @param clientNonce The client nonce. - */ - public void setClientNonce(String clientNonce) { - this.clientNonce = clientNonce; - } - - /** - * Sets the digest URI reference. - * - * @param digestRef The digest URI reference. - */ - public void setDigestRef(Reference digestRef) { - this.digestRef = digestRef; - } - - /** - * Sets the user identifier, such as a login name or an access key. - * - * @param identifier The user identifier, such as a login name or an access key. - */ - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - /** - * Sets the chosen quality of protection. - * - * @param quality The chosen quality of protection. - */ - public void setQuality(String quality) { - this.quality = quality; - } - - /** - * Sets the user secret, such as a password or a secret key. - * - * @param secret The user secret, such as a password or a secret key. - */ - public void setSecret(char[] secret) { - this.secret = secret; - } - - /** - * Sets the user secret, such as a password or a secret key. - * - * @param secret The user secret, such as a password or a secret key. - */ - public void setSecret(String secret) { - this.secret = (secret == null) ? null : secret.toCharArray(); - } - - /** - * Sets the digest algorithm name optionally applied on the user secret. - * - * @param secretDigestAlgorithm The digest algorithm name optionally applied on - * the user secret. - */ - public void setSecretAlgorithm(String secretDigestAlgorithm) { - this.secretAlgorithm = secretDigestAlgorithm; - } - - /** - * Sets the server nonce count. - * - * @param serverNonceCount The server nonce count. - */ - public void setServerNonceCount(int serverNonceCount) { - this.serverNonceCount = serverNonceCount; - } - - /** - * Sets the time when the response was issued, as returned by - * {@link System#currentTimeMillis()}. - * - * @param timeIssued The time when the response was issued. - */ - public void setTimeIssued(long timeIssued) { - this.timeIssued = timeIssued; - } + /** The client nonce value. */ + private volatile String clientNonce; + + /** The {@link Request#getResourceRef()} value duplicated here in case a proxy changed it. */ + private volatile Reference digestRef; + + /** The user identifier, such as a login name or an access key. */ + private volatile String identifier; + + /** The chosen quality of protection. */ + private volatile String quality; + + /** The user secret, such as a password or a secret key. */ + private volatile char[] secret; + + /** The digest algorithm name optionally applied on the user secret. */ + private volatile String secretAlgorithm; + + /** The server nonce count. */ + private volatile int serverNonceCount; + + /** The time when the response was issued, as returned by {@link System#currentTimeMillis()}. */ + private volatile long timeIssued; + + /** + * Constructor. It leverages the latest server response and challenge request to compute the + * credentials. + * + * @param challengeRequest The challenge request sent by the origin server. + * @param response The latest server response. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret, such as a password or a secret key, with no digest applied. + */ + public ChallengeResponse( + ChallengeRequest challengeRequest, + Response response, + String identifier, + char[] secret) { + this(challengeRequest, response, identifier, secret, Digest.ALGORITHM_NONE); + } + + /** + * Constructor. It leverages the latest server response and challenge request to compute the + * credentials. + * + * @param challengeRequest The challenge request sent by the origin server. + * @param response The latest server response. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret used to compute the secret, with an optional digest applied. + * @param secretAlgorithm The digest algorithm of the user secret (see {@link Digest} class). + */ + public ChallengeResponse( + ChallengeRequest challengeRequest, + Response response, + String identifier, + char[] secret, + String secretAlgorithm) { + this( + challengeRequest.getScheme(), + null, + identifier, + secret, + secretAlgorithm, + null, + null, + null, + null, + null, + null, + null, + 0, + 0L); + org.restlet.engine.security.AuthenticatorUtils.update( + this, response.getRequest(), response); + } + + /** + * Constructor. It leverages the latest server response and challenge request to compute the + * credentials. + * + * @param challengeRequest The challenge request sent by the origin server. + * @param response The latest server response. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret, such as a password or a secret key. + */ + public ChallengeResponse( + ChallengeRequest challengeRequest, + Response response, + String identifier, + String secret) { + this(challengeRequest, response, identifier, secret.toCharArray(), Digest.ALGORITHM_NONE); + } + + /** + * Constructor with no credentials. + * + * @param scheme The challenge scheme. + */ + public ChallengeResponse(ChallengeScheme scheme) { + this(scheme, null, (char[]) null); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param parameters The additional scheme parameters. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret, such as a password or a secret key. + * @param secretAlgorithm The digest algorithm name optionally applied on the user secret. + * @param realm The authentication realm. + * @param quality The chosen quality of protection. + * @param digestRef The {@link Request#getResourceRef()} value duplicated here in case a proxy + * changed it. + * @param digestAlgorithm The digest algorithm. + * @param opaque An opaque string of data which should be returned by the client unchanged. + * @param clientNonce The client nonce value. + * @param serverNonce The server nonce. + * @param serverNonceCount The server nonce count. + * @param timeIssued The time when the response was issued, as returned by {@link + * System#currentTimeMillis()}. + */ + public ChallengeResponse( + ChallengeScheme scheme, + Series parameters, + String identifier, + char[] secret, + String secretAlgorithm, + String realm, + String quality, + Reference digestRef, + String digestAlgorithm, + String opaque, + String clientNonce, + String serverNonce, + int serverNonceCount, + long timeIssued) { + super(scheme, realm, parameters, digestAlgorithm, opaque, serverNonce); + this.clientNonce = clientNonce; + this.digestRef = digestRef; + this.identifier = identifier; + this.quality = quality; + this.secret = secret; + this.secretAlgorithm = secretAlgorithm; + this.serverNonceCount = serverNonceCount; + this.timeIssued = timeIssued; + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret, such as a password or a secret key. + */ + public ChallengeResponse(ChallengeScheme scheme, String identifier, char[] secret) { + this(scheme, identifier, secret, null); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param identifier The user identifier, such as a login name or an access key. + * @param parameters The additional scheme parameters. + */ + public ChallengeResponse( + ChallengeScheme scheme, + String identifier, + char[] secret, + Series parameters) { + this( + scheme, + parameters, + identifier, + secret, + Digest.ALGORITHM_NONE, + null, + null, + null, + null, + null, + null, + null, + 0, + 0L); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param identifier The user identifier, such as a login name or an access key. + * @param parameters The additional scheme parameters. + */ + public ChallengeResponse( + ChallengeScheme scheme, String identifier, Series parameters) { + this(scheme, identifier, null, parameters); + } + + /** + * Constructor. + * + * @param scheme The challenge scheme. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret, such as a password or a secret key. + */ + public ChallengeResponse(ChallengeScheme scheme, String identifier, String secret) { + this(scheme, identifier, (secret != null) ? secret.toCharArray() : null); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + // if obj == this no need to go further + if (obj == this) { + return true; + } + + // if obj isn't a challenge request or is null don't evaluate further + if (!(obj instanceof ChallengeResponse that)) { + return false; + } + + if (!Objects.equals(getRawValue(), that.getRawValue()) + || !Objects.equals(getIdentifier(), that.getIdentifier()) + || !Objects.equals(getScheme(), that.getScheme())) { + return false; + } + + return Arrays.equals(getSecret(), that.getSecret()); + } + + /** + * Returns the client nonce. + * + * @return The client nonce. + */ + public String getClientNonce() { + return this.clientNonce; + } + + /** + * Returns the {@link Request#getResourceRef()} value duplicated here in case a proxy changed + * it. + * + * @return The digest URI reference. + */ + public Reference getDigestRef() { + return digestRef; + } + + /** + * Returns the user identifier, such as a login name or an access key. + * + * @return The user identifier, such as a login name or an access key. + */ + public String getIdentifier() { + return this.identifier; + } + + /** + * Gets the principal associated with the identifier property. + * + * @return The principal associated with the identifier property. + */ + public java.security.Principal getPrincipal() { + return this::getIdentifier; + } + + /** + * Returns the chosen quality of protection. + * + * @return The chosen quality of protection. + */ + public String getQuality() { + return quality; + } + + /** + * Returns the user secret, such as a password or a secret key. + * + *

It is not recommended to use {@link String#String(char[])} for security reasons. + * + * @return The user secret, such as a password or a secret key. + */ + public char[] getSecret() { + return this.secret; + } + + /** + * Returns the digest algorithm name optionally applied on the user secret. + * + * @return The digest algorithm name optionally applied on the user secret. + */ + public String getSecretAlgorithm() { + return secretAlgorithm; + } + + /** + * Returns the server nonce count. + * + * @return The server nonce count. + */ + public int getServerNonceCount() { + return serverNonceCount; + } + + /** + * Returns the server nonce count as a hexadecimal string of eight characters. + * + * @return The server nonce count as a hexadecimal string. + */ + public String getServerNonceCountAsHex() { + return org.restlet.engine.security.AuthenticatorUtils.formatNonceCount( + getServerNonceCount()); + } + + /** + * Returns the time when the response was issued, as returned by {@link + * System#currentTimeMillis()}. + * + * @return The time when the response was issued. + */ + public long getTimeIssued() { + return timeIssued; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + // Note that the secret is simply discarded from hash code calculation + // because we don't want it to be materialized as a string + return SystemUtils.hashCode(getScheme(), getIdentifier(), getRawValue()); + } + + /** + * Sets the client nonce. + * + * @param clientNonce The client nonce. + */ + public void setClientNonce(String clientNonce) { + this.clientNonce = clientNonce; + } + + /** + * Sets the digest URI reference. + * + * @param digestRef The digest URI reference. + */ + public void setDigestRef(Reference digestRef) { + this.digestRef = digestRef; + } + + /** + * Sets the user identifier, such as a login name or an access key. + * + * @param identifier The user identifier, such as a login name or an access key. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Sets the chosen quality of protection. + * + * @param quality The chosen quality of protection. + */ + public void setQuality(String quality) { + this.quality = quality; + } + + /** + * Sets the user secret, such as a password or a secret key. + * + * @param secret The user secret, such as a password or a secret key. + */ + public void setSecret(char[] secret) { + this.secret = secret; + } + + /** + * Sets the user secret, such as a password or a secret key. + * + * @param secret The user secret, such as a password or a secret key. + */ + public void setSecret(String secret) { + this.secret = (secret == null) ? null : secret.toCharArray(); + } + + /** + * Sets the digest algorithm name optionally applied on the user secret. + * + * @param secretDigestAlgorithm The digest algorithm name optionally applied on the user secret. + */ + public void setSecretAlgorithm(String secretDigestAlgorithm) { + this.secretAlgorithm = secretDigestAlgorithm; + } + + /** + * Sets the server nonce count. + * + * @param serverNonceCount The server nonce count. + */ + public void setServerNonceCount(int serverNonceCount) { + this.serverNonceCount = serverNonceCount; + } + + /** + * Sets the time when the response was issued, as returned by {@link + * System#currentTimeMillis()}. + * + * @param timeIssued The time when the response was issued. + */ + public void setTimeIssued(long timeIssued) { + this.timeIssued = timeIssued; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java b/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java index 49f636a926..a3acd32dde 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import java.util.Collections; @@ -15,214 +14,221 @@ /** * Challenge scheme used to authenticate remote clients. - * + * * @author Jerome Louvel */ public final class ChallengeScheme { - /** Custom scheme based on IP address or cookies or query parameters, etc. */ - public static final ChallengeScheme CUSTOM = new ChallengeScheme("CUSTOM", "Custom", "Custom authentication"); - - /** Plain FTP scheme. */ - public static final ChallengeScheme FTP_PLAIN = new ChallengeScheme("FTP_PLAIN", "PLAIN", - "Plain FTP authentication"); - - /** Amazon Query String HTTP scheme. */ - public static final ChallengeScheme HTTP_AWS_IAM = new ChallengeScheme("HTTP_AWS_IAM", "AWS3", - "Amazon IAM-based authentication"); - - /** Amazon Query String HTTP scheme. */ - public static final ChallengeScheme HTTP_AWS_QUERY = new ChallengeScheme("HTTP_AWS_QUERY", "AWS_QUERY", - "Amazon Query String authentication"); - - /** Amazon S3 HTTP scheme. */ - public static final ChallengeScheme HTTP_AWS_S3 = new ChallengeScheme("HTTP_AWS_S3", "AWS", - "Amazon S3 HTTP authentication"); - - /** - * Microsoft Azure Shared Key scheme. - * - * @see MSDN - * page - */ - public static final ChallengeScheme HTTP_AZURE_SHAREDKEY = new ChallengeScheme("HTTP_AZURE_SHAREDKEY", "SharedKey", - "Microsoft Azure Shared Key authorization (authentication)"); - - /** - * Microsoft Azure Shared Key lite scheme. - * - * @see MSDN - * page - */ - public static final ChallengeScheme HTTP_AZURE_SHAREDKEY_LITE = new ChallengeScheme("HTTP_AZURE_SHAREDKEY_LITE", - "SharedKeyLite", "Microsoft Azure Shared Key lite authorization (authentication)"); - - /** Basic HTTP scheme. */ - public static final ChallengeScheme HTTP_BASIC = new ChallengeScheme("HTTP_BASIC", "Basic", - "Basic HTTP authentication"); - - /** Cookie HTTP scheme. */ - public static final ChallengeScheme HTTP_COOKIE = new ChallengeScheme("HTTP_Cookie", "Cookie", - "Cookie HTTP authentication"); - - /** Digest HTTP scheme. */ - public static final ChallengeScheme HTTP_DIGEST = new ChallengeScheme("HTTP_DIGEST", "Digest", - "Digest HTTP authentication"); - - /** Microsoft NTML HTTP scheme. */ - public static final ChallengeScheme HTTP_NTLM = new ChallengeScheme("HTTP_NTLM", "NTLM", - "Microsoft NTLM HTTP authentication"); - - /** - * OAuth 1.0 HTTP scheme. Removed in later drafts and final OAuth 2.0 - * specification. - */ - public static final ChallengeScheme HTTP_OAUTH = new ChallengeScheme("HTTP_OAuth", "OAuth", - "OAuth 1.0 authentication"); - - /** OAuth Bearer HTTP scheme. */ - public static final ChallengeScheme HTTP_OAUTH_BEARER = new ChallengeScheme("HTTP_Bearer", "Bearer", - "OAuth 2.0 bearer token authentication"); - - /** OAuth MAC HTTP scheme. */ - public static final ChallengeScheme HTTP_OAUTH_MAC = new ChallengeScheme("HTTP_MAC", "Mac", - "OAuth 2.0 message authentication code authentication"); - - /** Private list of schemes for optimization purpose. */ - private static Map SCHEMES; - - static { - Map schemes = new HashMap(); - - schemes.put(CUSTOM.getName().toLowerCase(), CUSTOM); - schemes.put(FTP_PLAIN.getName().toLowerCase(), FTP_PLAIN); - schemes.put(HTTP_AWS_IAM.getName().toLowerCase(), HTTP_AWS_S3); - schemes.put(HTTP_AWS_QUERY.getName().toLowerCase(), HTTP_AWS_S3); - schemes.put(HTTP_AWS_S3.getName().toLowerCase(), HTTP_AWS_S3); - schemes.put(HTTP_AZURE_SHAREDKEY.getName().toLowerCase(), HTTP_AZURE_SHAREDKEY); - schemes.put(HTTP_AZURE_SHAREDKEY_LITE.getName().toLowerCase(), HTTP_AZURE_SHAREDKEY_LITE); - schemes.put(HTTP_BASIC.getName().toLowerCase(), HTTP_BASIC); - schemes.put(HTTP_COOKIE.getName().toLowerCase(), HTTP_COOKIE); - schemes.put(HTTP_DIGEST.getName().toLowerCase(), HTTP_DIGEST); - schemes.put(HTTP_NTLM.getName().toLowerCase(), HTTP_NTLM); - schemes.put(HTTP_OAUTH.getName().toLowerCase(), HTTP_OAUTH); - schemes.put(HTTP_OAUTH_BEARER.getName().toLowerCase(), HTTP_OAUTH); - schemes.put(HTTP_OAUTH_MAC.getName().toLowerCase(), HTTP_OAUTH); - - ChallengeScheme.SCHEMES = Collections.unmodifiableMap(schemes); - } - - /** - * Returns the challenge scheme associated to a scheme name. If an existing - * constant exists then it is returned, otherwise a new instance is created. - * - * @param name The scheme name. - * @return The associated challenge scheme. - */ - public static ChallengeScheme valueOf(final String name) { - if (name == null) { - throw new IllegalArgumentException("ChallengeScheme.valueOf(name) name must not be null"); - } - - ChallengeScheme result = SCHEMES.get(name.toLowerCase()); - - if (result == null) { - result = new ChallengeScheme(name, null, null); - } - - return result; - } - - /** The description. */ - private final String description; - - /** The name. */ - private final String name; - - /** The technical name. */ - private volatile String technicalName; - - /** - * Constructor. - * - * @param name The unique name. - * @param technicalName The technical name. - */ - public ChallengeScheme(final String name, final String technicalName) { - this(name, technicalName, null); - } - - /** - * Constructor. - * - * @param name The unique name. - * @param technicalName The technical name. - * @param description The description. - */ - public ChallengeScheme(final String name, final String technicalName, final String description) { - this.name = name; - this.description = description; - this.technicalName = technicalName; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(final Object object) { - return (object instanceof ChallengeScheme) && ((ChallengeScheme) object).getName().equalsIgnoreCase(getName()); - } - - /** - * Returns the description. - * - * @return The description. - */ - public String getDescription() { - return this.description; - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the technical name (ex: BASIC). - * - * @return The technical name (ex: BASIC). - */ - public String getTechnicalName() { - return this.technicalName; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); - } - - /** - * Sets the technical name (ex: BASIC). - * - * @param technicalName The technical name (ex: BASIC). - */ - @SuppressWarnings("unused") - private void setTechnicalName(String technicalName) { - this.technicalName = technicalName; - } - - /** - * Returns the name. - * - * @return The name. - */ - @Override - public String toString() { - return getName(); - } - + /** Custom scheme based on IP address or cookies or query parameters, etc. */ + public static final ChallengeScheme CUSTOM = + new ChallengeScheme("CUSTOM", "Custom", "Custom authentication"); + + /** Plain FTP scheme. */ + public static final ChallengeScheme FTP_PLAIN = + new ChallengeScheme("FTP_PLAIN", "PLAIN", "Plain FTP authentication"); + + /** Amazon Query String HTTP scheme. */ + public static final ChallengeScheme HTTP_AWS_IAM = + new ChallengeScheme("HTTP_AWS_IAM", "AWS3", "Amazon IAM-based authentication"); + + /** Amazon Query String HTTP scheme. */ + public static final ChallengeScheme HTTP_AWS_QUERY = + new ChallengeScheme( + "HTTP_AWS_QUERY", "AWS_QUERY", "Amazon Query String authentication"); + + /** Amazon S3 HTTP scheme. */ + public static final ChallengeScheme HTTP_AWS_S3 = + new ChallengeScheme("HTTP_AWS_S3", "AWS", "Amazon S3 HTTP authentication"); + + /** + * Microsoft Azure Shared Key scheme. + * + * @see MSDN + * page + */ + public static final ChallengeScheme HTTP_AZURE_SHAREDKEY = + new ChallengeScheme( + "HTTP_AZURE_SHAREDKEY", + "SharedKey", + "Microsoft Azure Shared Key authorization (authentication)"); + + /** + * Microsoft Azure Shared Key lite scheme. + * + * @see MSDN + * page + */ + public static final ChallengeScheme HTTP_AZURE_SHAREDKEY_LITE = + new ChallengeScheme( + "HTTP_AZURE_SHAREDKEY_LITE", + "SharedKeyLite", + "Microsoft Azure Shared Key lite authorization (authentication)"); + + /** Basic HTTP scheme. */ + public static final ChallengeScheme HTTP_BASIC = + new ChallengeScheme("HTTP_BASIC", "Basic", "Basic HTTP authentication"); + + /** Cookie HTTP scheme. */ + public static final ChallengeScheme HTTP_COOKIE = + new ChallengeScheme("HTTP_Cookie", "Cookie", "Cookie HTTP authentication"); + + /** Digest HTTP scheme. */ + public static final ChallengeScheme HTTP_DIGEST = + new ChallengeScheme("HTTP_DIGEST", "Digest", "Digest HTTP authentication"); + + /** Microsoft NTML HTTP scheme. */ + public static final ChallengeScheme HTTP_NTLM = + new ChallengeScheme("HTTP_NTLM", "NTLM", "Microsoft NTLM HTTP authentication"); + + /** OAuth 1.0 HTTP scheme. Removed in later drafts and final OAuth 2.0 specification. */ + public static final ChallengeScheme HTTP_OAUTH = + new ChallengeScheme("HTTP_OAuth", "OAuth", "OAuth 1.0 authentication"); + + /** OAuth Bearer HTTP scheme. */ + public static final ChallengeScheme HTTP_OAUTH_BEARER = + new ChallengeScheme("HTTP_Bearer", "Bearer", "OAuth 2.0 bearer token authentication"); + + /** OAuth MAC HTTP scheme. */ + public static final ChallengeScheme HTTP_OAUTH_MAC = + new ChallengeScheme( + "HTTP_MAC", "Mac", "OAuth 2.0 message authentication code authentication"); + + /** Private list of schemes for optimization purpose. */ + private static Map SCHEMES; + + static { + Map schemes = new HashMap(); + + schemes.put(CUSTOM.getName().toLowerCase(), CUSTOM); + schemes.put(FTP_PLAIN.getName().toLowerCase(), FTP_PLAIN); + schemes.put(HTTP_AWS_IAM.getName().toLowerCase(), HTTP_AWS_S3); + schemes.put(HTTP_AWS_QUERY.getName().toLowerCase(), HTTP_AWS_S3); + schemes.put(HTTP_AWS_S3.getName().toLowerCase(), HTTP_AWS_S3); + schemes.put(HTTP_AZURE_SHAREDKEY.getName().toLowerCase(), HTTP_AZURE_SHAREDKEY); + schemes.put(HTTP_AZURE_SHAREDKEY_LITE.getName().toLowerCase(), HTTP_AZURE_SHAREDKEY_LITE); + schemes.put(HTTP_BASIC.getName().toLowerCase(), HTTP_BASIC); + schemes.put(HTTP_COOKIE.getName().toLowerCase(), HTTP_COOKIE); + schemes.put(HTTP_DIGEST.getName().toLowerCase(), HTTP_DIGEST); + schemes.put(HTTP_NTLM.getName().toLowerCase(), HTTP_NTLM); + schemes.put(HTTP_OAUTH.getName().toLowerCase(), HTTP_OAUTH); + schemes.put(HTTP_OAUTH_BEARER.getName().toLowerCase(), HTTP_OAUTH); + schemes.put(HTTP_OAUTH_MAC.getName().toLowerCase(), HTTP_OAUTH); + + ChallengeScheme.SCHEMES = Collections.unmodifiableMap(schemes); + } + + /** + * Returns the challenge scheme associated with a scheme name. If an existing constant exists, + * then it is returned; otherwise a new instance is created. + * + * @param name The scheme name. + * @return The associated challenge scheme. + */ + public static ChallengeScheme valueOf(final String name) { + if (name == null) { + throw new IllegalArgumentException( + "ChallengeScheme.valueOf(name) name must not be null"); + } + + ChallengeScheme result = SCHEMES.get(name.toLowerCase()); + + if (result == null) { + result = new ChallengeScheme(name, null, null); + } + + return result; + } + + /** The description. */ + private final String description; + + /** The name. */ + private final String name; + + /** The technical name. */ + private volatile String technicalName; + + /** + * Constructor. + * + * @param name The unique name. + * @param technicalName The technical name. + */ + public ChallengeScheme(final String name, final String technicalName) { + this(name, technicalName, null); + } + + /** + * Constructor. + * + * @param name The unique name. + * @param technicalName The technical name. + * @param description The description. + */ + public ChallengeScheme( + final String name, final String technicalName, final String description) { + this.name = name; + this.description = description; + this.technicalName = technicalName; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object object) { + return (object instanceof ChallengeScheme) + && ((ChallengeScheme) object).getName().equalsIgnoreCase(getName()); + } + + /** + * Returns the description. + * + * @return The description. + */ + public String getDescription() { + return this.description; + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the technical name (ex: BASIC). + * + * @return The technical name (ex: BASIC). + */ + public String getTechnicalName() { + return this.technicalName; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); + } + + /** + * Sets the technical name (ex: BASIC). + * + * @param technicalName The technical name (ex: BASIC). + */ + @SuppressWarnings("unused") + private void setTechnicalName(String technicalName) { + this.technicalName = technicalName; + } + + /** + * Returns the name. + * + * @return The name. + */ + @Override + public String toString() { + return getName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java index 20c70e0bdc..4677831492 100644 --- a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java +++ b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java @@ -1,293 +1,296 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; /** * Metadata used to specify the character set of textual representations. - * + * * @author Jerome Louvel */ public final class CharacterSet extends Metadata { - /** All character sets acceptable. */ - public static final CharacterSet ALL = new CharacterSet("*", "All character sets"); - - /** - * The ISO/IEC 8859-1 (Latin 1) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_1 = new CharacterSet("ISO-8859-1", - "ISO/IEC 8859-1 or Latin 1 character set"); - - /** - * The ISO/IEC 8859-2 (Latin 2) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_2 = new CharacterSet("ISO-8859-2", - "ISO/IEC 8859-2 or Latin 2 character set"); - - /** - * The ISO/IEC 8859-3 (Latin 3) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_3 = new CharacterSet("ISO-8859-3", - "ISO/IEC 8859-3 or Latin 3 character set"); - - /** - * The ISO/IEC 8859-4 (Latin 4) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_4 = new CharacterSet("ISO-8859-4", - "ISO/IEC 8859-4 or Latin 4 character set"); - - /** - * The ISO/IEC 8859-5 (Cyrillic) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_5 = new CharacterSet("ISO-8859-5", - "ISO/IEC 8859-5 or Cyrillic character set"); - - /** - * The ISO/IEC 8859-6 (Arabic) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_6 = new CharacterSet("ISO-8859-6", - "ISO/IEC 8859-6 or Arabic character set"); - - /** - * The ISO/IEC 8859-7 (Greek) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_7 = new CharacterSet("ISO-8859-7", - "ISO/IEC 8859-7 or Greek character set"); - - /** - * The ISO/IEC 8859-8 (Hebrew) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_8 = new CharacterSet("ISO-8859-8", - "ISO/IEC 8859-8 or Hebrew character set"); - - /** - * The ISO/IEC 8859-9 (Latin 5) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_9 = new CharacterSet("ISO-8859-9", - "ISO/IEC 8859-9 or Latin 5 character set"); - - /** - * The ISO/IEC 8859-10 (Latin 6) character set. - * - * @see Wikipedia page - */ - public static final CharacterSet ISO_8859_10 = new CharacterSet("ISO-8859-10", - "ISO/IEC 8859-10 or Latin 6 character set"); - - /** - * The Macintosh ("Mac OS Roman") character set. - * - * @see Wikipedia page - */ - public static final CharacterSet MACINTOSH = new CharacterSet("macintosh", "Mac OS Roman character set"); - - /** - * The US-ASCII character set. - * - * @see Wikipedia page - */ - public static final CharacterSet US_ASCII = new CharacterSet("US-ASCII", "US ASCII character set"); - - /** - * The UTF-16 character set. - * - * @see Wikipedia page - */ - public static final CharacterSet UTF_16 = new CharacterSet("UTF-16", "UTF 16 character set"); - - /** - * The UTF-8 character set. - * - * @see Wikipedia page - */ - public static final CharacterSet UTF_8 = new CharacterSet("UTF-8", "UTF 8 character set"); - - /** - * The Windows-1252 ('ANSI') character set. - * - * @see Wikipedia page - * - */ - public static final CharacterSet WINDOWS_1252 = new CharacterSet("windows-1252", "Windows 1232 character set"); - - /** - * The default character set of the JVM. - * - * @see java.nio.charset.Charset#defaultCharset() - */ - public static final CharacterSet DEFAULT = new CharacterSet(java.nio.charset.Charset.defaultCharset()); - - /** - * Handles mapping between Java character set names and IANA preferred name. For - * example, "MACROMAN" is not an official IANA name and "ISO-8859-6" is - * preferred over "arabic". - * - * @param name The character set name. - * @return The IANA character set name. - */ - private static String getIanaName(String name) { - if (name != null) { - name = name.toUpperCase(); - - if (name.equalsIgnoreCase("MACROMAN")) { - name = MACINTOSH.getName(); - } else if (name.equalsIgnoreCase("ASCII")) { - name = US_ASCII.getName(); - } else if (name.equalsIgnoreCase("latin1")) { - name = ISO_8859_1.getName(); - } else if (name.equalsIgnoreCase("latin2")) { - name = ISO_8859_2.getName(); - } else if (name.equalsIgnoreCase("latin3")) { - name = ISO_8859_3.getName(); - } else if (name.equalsIgnoreCase("latin4")) { - name = ISO_8859_4.getName(); - } else if (name.equalsIgnoreCase("cyrillic")) { - name = ISO_8859_5.getName(); - } else if (name.equalsIgnoreCase("arabic")) { - name = ISO_8859_6.getName(); - } else if (name.equalsIgnoreCase("greek")) { - name = ISO_8859_7.getName(); - } else if (name.equalsIgnoreCase("hebrew")) { - name = ISO_8859_8.getName(); - } else if (name.equalsIgnoreCase("latin5")) { - name = ISO_8859_9.getName(); - } else if (name.equalsIgnoreCase("latin6")) { - name = ISO_8859_10.getName(); - } - } - - return name; - } - - /** - * Returns the character set associated to a name. If an existing constant - * exists then it is returned, otherwise a new instance is created. - * - * @param name The name. - * @return The associated character set. - */ - public static CharacterSet valueOf(String name) { - CharacterSet result = null; - name = getIanaName(name); - - if ((name != null) && !name.isEmpty()) { - if (name.equalsIgnoreCase(ALL.getName())) { - result = ALL; - } else if (name.equalsIgnoreCase(ISO_8859_1.getName())) { - result = ISO_8859_1; - } else if (name.equalsIgnoreCase(US_ASCII.getName())) { - result = US_ASCII; - } else if (name.equalsIgnoreCase(UTF_8.getName())) { - result = UTF_8; - } else if (name.equalsIgnoreCase(UTF_16.getName())) { - result = UTF_16; - } else if (name.equalsIgnoreCase(WINDOWS_1252.getName())) { - result = WINDOWS_1252; - } else if (name.equalsIgnoreCase(MACINTOSH.getName())) { - result = MACINTOSH; - } else { - result = new CharacterSet(name); - } - } - - return result; - } - - /** - * Constructor. - * - * @param charset The character set. - */ - public CharacterSet(final java.nio.charset.Charset charset) { - this(charset.name(), charset.displayName()); - } - - /** - * Constructor. - * - * @param name The name. - */ - public CharacterSet(String name) { - this(name == null ? null : name.toUpperCase(), "Character set or range of character sets"); - } - - /** - * Constructor. - * - * @param name The name. - * @param description The description. - */ - public CharacterSet(String name, String description) { - super(getIanaName(name), description); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object object) { - return (object instanceof CharacterSet) && getName().equalsIgnoreCase(((CharacterSet) object).getName()); - } - - @Override - public Metadata getParent() { - return equals(ALL) ? null : ALL; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); - } - - /** - * Indicates if a given character set is included in the current one. The test - * is true if both character sets are equal or if the given character set is - * within the range of the current one. For example, ALL includes all character - * sets. A null character set is considered as included into the current one. - *

- * Examples: - *

    - *
  • ALL.includes(UTF_16) returns true
  • - *
  • UTF_16.includes(ALL) returns false
  • - *
- * - * @param included The character set to test for inclusion. - * @return True if the given character set is included in the current one. - * @see #isCompatible(Metadata) - */ - public boolean includes(Metadata included) { - return equals(ALL) || (included == null) || equals(included); - } - - /** - * Returns the NIO charset matching the character set name. - * - * @return The NIO charset. - */ - public java.nio.charset.Charset toCharset() { - return java.nio.charset.Charset.forName(getName()); - } + /** All character sets acceptable. */ + public static final CharacterSet ALL = new CharacterSet("*", "All character sets"); + + /** + * The ISO/IEC 8859-1 (Latin 1) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_1 = + new CharacterSet("ISO-8859-1", "ISO/IEC 8859-1 or Latin 1 character set"); + + /** + * The ISO/IEC 8859-2 (Latin 2) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_2 = + new CharacterSet("ISO-8859-2", "ISO/IEC 8859-2 or Latin 2 character set"); + + /** + * The ISO/IEC 8859-3 (Latin 3) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_3 = + new CharacterSet("ISO-8859-3", "ISO/IEC 8859-3 or Latin 3 character set"); + + /** + * The ISO/IEC 8859-4 (Latin 4) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_4 = + new CharacterSet("ISO-8859-4", "ISO/IEC 8859-4 or Latin 4 character set"); + + /** + * The ISO/IEC 8859-5 (Cyrillic) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_5 = + new CharacterSet("ISO-8859-5", "ISO/IEC 8859-5 or Cyrillic character set"); + + /** + * The ISO/IEC 8859-6 (Arabic) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_6 = + new CharacterSet("ISO-8859-6", "ISO/IEC 8859-6 or Arabic character set"); + + /** + * The ISO/IEC 8859-7 (Greek) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_7 = + new CharacterSet("ISO-8859-7", "ISO/IEC 8859-7 or Greek character set"); + + /** + * The ISO/IEC 8859-8 (Hebrew) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_8 = + new CharacterSet("ISO-8859-8", "ISO/IEC 8859-8 or Hebrew character set"); + + /** + * The ISO/IEC 8859-9 (Latin 5) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_9 = + new CharacterSet("ISO-8859-9", "ISO/IEC 8859-9 or Latin 5 character set"); + + /** + * The ISO/IEC 8859-10 (Latin 6) character set. + * + * @see Wikipedia page + */ + public static final CharacterSet ISO_8859_10 = + new CharacterSet("ISO-8859-10", "ISO/IEC 8859-10 or Latin 6 character set"); + + /** + * The Macintosh ("Mac OS Roman") character set. + * + * @see Wikipedia page + */ + public static final CharacterSet MACINTOSH = + new CharacterSet("macintosh", "Mac OS Roman character set"); + + /** + * The US-ASCII character set. + * + * @see Wikipedia page + */ + public static final CharacterSet US_ASCII = + new CharacterSet("US-ASCII", "US ASCII character set"); + + /** + * The UTF-16 character set. + * + * @see Wikipedia page + */ + public static final CharacterSet UTF_16 = new CharacterSet("UTF-16", "UTF 16 character set"); + + /** + * The UTF-8 character set. + * + * @see Wikipedia page + */ + public static final CharacterSet UTF_8 = new CharacterSet("UTF-8", "UTF 8 character set"); + + /** + * The Windows-1252 ('ANSI') character set. + * + * @see Wikipedia page + */ + public static final CharacterSet WINDOWS_1252 = + new CharacterSet("windows-1252", "Windows 1232 character set"); + + /** + * The default character set of the JVM. + * + * @see java.nio.charset.Charset#defaultCharset() + */ + public static final CharacterSet DEFAULT = + new CharacterSet(java.nio.charset.Charset.defaultCharset()); + + /** + * Handles mapping between Java character set names and IANA preferred name. For example, + * "MACROMAN" is not an official IANA name and "ISO-8859-6" is preferred over "arabic". + * + * @param name The character set name. + * @return The IANA character set name. + */ + private static String getIanaName(String name) { + if (name != null) { + name = name.toUpperCase(); + + if (name.equalsIgnoreCase("MACROMAN")) { + name = MACINTOSH.getName(); + } else if (name.equalsIgnoreCase("ASCII")) { + name = US_ASCII.getName(); + } else if (name.equalsIgnoreCase("latin1")) { + name = ISO_8859_1.getName(); + } else if (name.equalsIgnoreCase("latin2")) { + name = ISO_8859_2.getName(); + } else if (name.equalsIgnoreCase("latin3")) { + name = ISO_8859_3.getName(); + } else if (name.equalsIgnoreCase("latin4")) { + name = ISO_8859_4.getName(); + } else if (name.equalsIgnoreCase("cyrillic")) { + name = ISO_8859_5.getName(); + } else if (name.equalsIgnoreCase("arabic")) { + name = ISO_8859_6.getName(); + } else if (name.equalsIgnoreCase("greek")) { + name = ISO_8859_7.getName(); + } else if (name.equalsIgnoreCase("hebrew")) { + name = ISO_8859_8.getName(); + } else if (name.equalsIgnoreCase("latin5")) { + name = ISO_8859_9.getName(); + } else if (name.equalsIgnoreCase("latin6")) { + name = ISO_8859_10.getName(); + } + } + + return name; + } + + /** + * Returns the character set associated with a name. If an existing constant exists, then it is + * returned; otherwise a new instance is created. + * + * @param name The name. + * @return The associated character set. + */ + public static CharacterSet valueOf(String name) { + CharacterSet result = null; + name = getIanaName(name); + + if ((name != null) && !name.isEmpty()) { + if (name.equalsIgnoreCase(ALL.getName())) { + result = ALL; + } else if (name.equalsIgnoreCase(ISO_8859_1.getName())) { + result = ISO_8859_1; + } else if (name.equalsIgnoreCase(US_ASCII.getName())) { + result = US_ASCII; + } else if (name.equalsIgnoreCase(UTF_8.getName())) { + result = UTF_8; + } else if (name.equalsIgnoreCase(UTF_16.getName())) { + result = UTF_16; + } else if (name.equalsIgnoreCase(WINDOWS_1252.getName())) { + result = WINDOWS_1252; + } else if (name.equalsIgnoreCase(MACINTOSH.getName())) { + result = MACINTOSH; + } else { + result = new CharacterSet(name); + } + } + + return result; + } + + /** + * Constructor. + * + * @param charset The character set. + */ + public CharacterSet(final java.nio.charset.Charset charset) { + this(charset.name(), charset.displayName()); + } + + /** + * Constructor. + * + * @param name The name. + */ + public CharacterSet(String name) { + this(name == null ? null : name.toUpperCase(), "Character set or range of character sets"); + } + + /** + * Constructor. + * + * @param name The name. + * @param description The description. + */ + public CharacterSet(String name, String description) { + super(getIanaName(name), description); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object object) { + return (object instanceof CharacterSet) + && getName().equalsIgnoreCase(((CharacterSet) object).getName()); + } + + @Override + public Metadata getParent() { + return equals(ALL) ? null : ALL; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); + } + + /** + * Indicates if a given character set is included in the current one. The test is true if both + * character sets are equal or if the given character set is within the range of the current + * one. For example, ALL includes all character sets. A null character set is considered as + * included in the current one. + * + *

Examples: + * + *

    + *
  • ALL.includes(UTF_16) returns true + *
  • UTF_16.includes(ALL) returns false + *
+ * + * @param included The character set to test for inclusion. + * @return True if the given character set is included in the current one. + * @see #isCompatible(Metadata) + */ + public boolean includes(Metadata included) { + return equals(ALL) || (included == null) || equals(included); + } + + /** + * Returns the NIO charset matching the character set name. + * + * @return The NIO charset. + */ + public java.nio.charset.Charset toCharset() { + return java.nio.charset.Charset.forName(getName()); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java index 28d369e35e..18ed80a6c7 100644 --- a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java @@ -1,69 +1,63 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.Context; -import org.restlet.engine.Engine; -import org.restlet.engine.io.IoUtils; - import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.Context; +import org.restlet.engine.Engine; +import org.restlet.engine.io.IoUtils; /** - * Client specific data related to a call. When extracted from a request, most - * of these data are directly taken from the underlying headers. There are some - * exceptions: agentAttributes and mainAgentProduct which are taken from the - * agent name (for example the "user-agent" header for HTTP requests).
+ * Client-specific data related to a call. When extracted from a request, most of these data are + * directly taken from the underlying headers. There are some exceptions: agentAttributes and + * mainAgentProduct which are taken from the agent name (for example, the "user-agent" header for + * HTTP requests).
*
- * As described by the HTTP specification, the "user-agent" can be seen as a - * ordered list of products name (ie a name and a version) and/or comments.
+ * As described by the HTTP specification, the "user-agent" can be seen as an ordered list of + * products name (i.e., a name and a version) and/or comments.
*
- * Each HTTP client (mainly browsers and web crawlers) defines its own - * "user-agent" header which can be seen as the "signature" of the client. - * Unfortunately, there is no rule to identify clearly a kind a client and its - * version (let's say Firefox 2.x, Internet Explorer IE 7.0, Opera, etc) - * according to its signature. Each signature follow its own rules which may - * vary according to the version of the client.
+ * Each HTTP client (mainly browsers and web crawlers) defines its own "user-agent" header which can + * be seen as the "signature" of the client. Unfortunately, there is no rule to identify clearly a + * kind a client and its version (let's say Firefox 2.x, Internet Explorer IE 7.0, Opera, etc.) + * according to its signature. Each signature follows its own rules, which may vary according to the + * version of the client.
*
- * In order to help retrieving interesting data such as product name (Firefox, - * IE, etc), version, operating system, Restlet users has the ability to define - * their own way to extract data from the "user-agent" header. It is based on a - * list of templates declared in a file called "agent.properties" and located in - * the classpath in the sub directory "org/restlet/data". Each template - * describes a typical user-agent string and allows to use predefined variables - * that help to retrieve the content of the agent name, version, operating - * system.
+ * To help retrieving interesting data such as product name (Firefox, IE, etc.), version, operating + * system, Restlet users can define their own way to extract data from the "user-agent" header. It + * is based on a list of templates declared in a file called "agent.properties" and located in the + * classpath in the subdirectory "org/restlet/data". Each template describes a typical user-agent + * string and allows using predefined variables that help to retrieve the content of the agent name, + * version, operating system.
*
- * The "user-agent" string is confronted to the each template from the beginning - * of the property file to the end. The loop stops at the first matched - * template.
+ * The "user-agent" string is confronted to each template from the beginning of the property file to + * the end. The loop stops at the first matched template.
*
- * Here is a sample of such template:
- * + * Here is a sample of such a template:
+ * *

  * #Firefox for Windows
  *  Mozilla/{mozillaVersion} (Windows; U; {agentOs}; {osData}; rv:{releaseVersion}) Gecko/{geckoReleaseDate} {agentName}/{agentVersion}
  * 
- * - * This template matches the "user-agent" string of the Firefox client for - * windows: - * + * + * This template matches the "user-agent" string of the Firefox client for windows: + * *
  *  Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20060918 Firefox/2.0
  * 
- * + * * At this time, six predefined variables are used:
+ * * * * @@ -96,968 +90,974 @@ * * *
list of predefined variables
A sequence of characters that can be empty
+ * *
*
- * These variables are used to generate a {@link Product} instance with the main - * data (name, version, comment). This instance is accessible via the - * {@link ClientInfo#getMainAgentProduct()} method. All other variables used in - * the template aims at catching a sequence of characters and are accessible via - * the {@link ClientInfo#getAgentAttributes()} method. - * + * These variables are used to generate a {@link Product} instance with the main data (name, + * version, comment). This instance is accessible via the {@link ClientInfo#getMainAgentProduct()} + * method. All other variables used in the template aim at catching a sequence of characters and are + * accessible via the {@link ClientInfo#getAgentAttributes()} method. + * * @author Jerome Louvel */ public final class ClientInfo { - /** - * List of user-agent templates defined in "agent.properties" file.
- * - * @see ClientInfo#getAgentAttributes() - */ - private static volatile List userAgentTemplates = null; - - /** - * Returns the preferred metadata taking into account both metadata supported by - * the server and client preferences. - * - * @param supported The metadata supported by the server. - * @param preferences The client preferences. - * @return The preferred metadata. - */ - public static T getPreferredMetadata(List supported, List> preferences) { - T result = null; - float maxQuality = 0; - - if (supported != null) { - for (Preference pref : preferences) { - for (T metadata : supported) { - if (pref.getMetadata().isCompatible(metadata) && (pref.getQuality() > maxQuality)) { - result = metadata; - maxQuality = pref.getQuality(); - } - } - } - } - - return result; - } - - /** - * Returns the list of user-agent templates defined in "agent.properties" file. - * - * @return The list of user-agent templates defined in "agent.properties" file. - * @see ClientInfo#getAgentAttributes() - */ - private static List getUserAgentTemplates() { - // Lazy initialization with double-check. - List u = ClientInfo.userAgentTemplates; - if (u == null) { - synchronized (ClientInfo.class) { - u = ClientInfo.userAgentTemplates; - if (u == null) { - // Load from the "agent.properties" file - java.net.URL userAgentPropertiesUrl = Engine.getResource("org/restlet/data/agent.properties"); - if (userAgentPropertiesUrl != null) { - java.io.BufferedReader reader; - try { - reader = new java.io.BufferedReader( - new InputStreamReader(userAgentPropertiesUrl.openStream(), - CharacterSet.UTF_8.getName()), - IoUtils.BUFFER_SIZE); - String line = reader.readLine(); - for (; line != null; line = reader.readLine()) { - if ((line.trim().length() > 0) && !line.trim().startsWith("#")) { - if (u == null) { - u = new CopyOnWriteArrayList(); - } - u.add(line); - } - } - reader.close(); - } catch (IOException e) { - if (Context.getCurrent() != null) { - Context.getCurrent().getLogger().warning("Cannot read '" - + userAgentPropertiesUrl.toString() + "' due to: " + e.getMessage()); - } - } - } - ClientInfo.userAgentTemplates = u; - } - } - } - return u; - } - - /** The character set preferences. */ - private volatile List> acceptedCharacterSets; - - /** The encoding preferences. */ - private volatile List> acceptedEncodings; - - /** The language preferences. */ - private volatile List> acceptedLanguages; - - /** The media preferences. */ - private volatile List> acceptedMediaTypes; - - /** The patch preferences. */ - private volatile List> acceptedPatches; - - /** The immediate IP addresses. */ - private volatile String address; - - /** The agent name. */ - private volatile String agent; - - /** The attributes data taken from the agent name. */ - private volatile Map agentAttributes; - - /** The main product data taken from the agent name. */ - private volatile Product agentMainProduct; - - /** The list of product tokens taken from the agent name. */ - private volatile List agentProducts; - - /** - * Indicates if the subject has been authenticated. The application is - * responsible for updating this property, relying on - * {@link org.restlet.security.Authenticator} or manually. - */ - private volatile boolean authenticated; - - /** List of client certificates. */ - private volatile List certificates; - - /** The SSL Cipher Suite, if available and accessible. */ - private volatile String cipherSuite; - - /** List of expectations. */ - private volatile List expectations; - - /** The forwarded IP addresses. */ - private volatile List forwardedAddresses; - - /** The email address of the human user controlling the user agent. */ - private volatile String from; - - /** The port number. */ - private volatile int port; - - /** List of additional client principals. */ - private volatile List principals; - - /** List of user roles. */ - private volatile List roles; - - /** Authenticated user. */ - private volatile org.restlet.security.User user; - - /** - * Constructor. - */ - public ClientInfo() { - this.address = null; - this.agent = null; - this.port = -1; - this.acceptedCharacterSets = null; - this.acceptedEncodings = null; - this.acceptedLanguages = null; - this.acceptedMediaTypes = null; - this.acceptedPatches = null; - this.forwardedAddresses = null; - this.from = null; - this.agentProducts = null; - this.principals = null; - this.user = null; - this.roles = null; - this.expectations = null; - } - - /** - * Constructor from a list of variants. Note that only media types are taken - * into account. - * - * @param variants The variants corresponding to the accepted media types. - */ - public ClientInfo(List variants) { - if (variants != null) { - for (org.restlet.representation.Variant variant : variants) { - getAcceptedMediaTypes().add(new Preference(variant.getMediaType())); - } - } - } - - /** - * Constructor from a media type. - * - * @param mediaType The preferred media type. - */ - public ClientInfo(MediaType mediaType) { - getAcceptedMediaTypes().add(new Preference(mediaType)); - } - - /** - * Updates the client preferences to accept the given metadata (media types, - * character sets, etc.) with a 1.0 quality in addition to existing ones. - * - * @param metadata The metadata to accept. - */ - public void accept(Metadata... metadata) { - if (metadata != null) { - for (Metadata md : metadata) { - accept(md, 1.0F); - } - } - } - - /** - * Updates the client preferences to accept the given metadata (media types, - * character sets, etc.) with a given quality in addition to existing ones. - * - * @param metadata The metadata to accept. - * @param quality The quality to set. - */ - public void accept(Metadata metadata, float quality) { - if (metadata instanceof MediaType) { - getAcceptedMediaTypes().add(new Preference((MediaType) metadata, quality)); - } else if (metadata instanceof Language) { - getAcceptedLanguages().add(new Preference((Language) metadata, quality)); - } else if (metadata instanceof Encoding) { - getAcceptedEncodings().add(new Preference((Encoding) metadata, quality)); - } else { - getAcceptedCharacterSets().add(new Preference((CharacterSet) metadata, quality)); - } - } - - /** - * Returns the modifiable list of character set preferences. Creates a new - * instance if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Accept-Charset" header. - * - * @return The character set preferences. - */ - public List> getAcceptedCharacterSets() { - // Lazy initialization with double-check. - List> a = this.acceptedCharacterSets; - if (a == null) { - synchronized (this) { - a = this.acceptedCharacterSets; - if (a == null) { - this.acceptedCharacterSets = a = new CopyOnWriteArrayList>(); - } - } - } - return a; - } - - /** - * Returns the modifiable list of encoding preferences. Creates a new instance - * if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Accept-Encoding" header. - * - * @return The encoding preferences. - */ - public List> getAcceptedEncodings() { - // Lazy initialization with double-check. - List> a = this.acceptedEncodings; - if (a == null) { - synchronized (this) { - a = this.acceptedEncodings; - if (a == null) { - this.acceptedEncodings = a = new CopyOnWriteArrayList>(); - } - } - } - return a; - } - - /** - * Returns the modifiable list of language preferences. Creates a new instance - * if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Accept-Language" header. - * - * @return The language preferences. - */ - public List> getAcceptedLanguages() { - // Lazy initialization with double-check. - List> a = this.acceptedLanguages; - if (a == null) { - synchronized (this) { - a = this.acceptedLanguages; - if (a == null) { - this.acceptedLanguages = a = new CopyOnWriteArrayList>(); - } - } - } - return a; - } - - /** - * Returns the modifiable list of media type preferences. Creates a new instance - * if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the "Accept" - * header. - * - * @return The media type preferences. - */ - public List> getAcceptedMediaTypes() { - // Lazy initialization with double-check. - List> a = this.acceptedMediaTypes; - if (a == null) { - synchronized (this) { - a = this.acceptedMediaTypes; - if (a == null) { - this.acceptedMediaTypes = a = new CopyOnWriteArrayList>(); - } - } - } - return a; - } - - /** - * Returns the modifiable list of patch preferences. Creates a new instance if - * no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Accept-Patch" header. - * - * @return The patch preferences. - */ - public List> getAcceptedPatches() { - // Lazy initialization with double-check. - List> a = this.acceptedPatches; - if (a == null) { - synchronized (this) { - a = this.acceptedPatches; - if (a == null) { - this.acceptedPatches = a = new CopyOnWriteArrayList>(); - } - } - } - return a; - } - - /** - * Returns the immediate client's IP address. If the real client is separated - * from the server by a proxy server, this will return the IP address of the - * proxy. - * - * @return The immediate client's IP address. - * @see #getUpstreamAddress() - * @see #getForwardedAddresses() - */ - public String getAddress() { - return this.address; - } - - /** - * Returns the agent name (ex: "Restlet-Framework/2.0"). Note that when used - * with HTTP connectors, this property maps to the "User-Agent" header. - * - * @return The agent name. - */ - public String getAgent() { - return this.agent; - } - - /** - * Returns a list of attributes taken from the name of the user agent. - * - * @return A list of attributes taken from the name of the user agent. - * @see #getAgent() - */ - public Map getAgentAttributes() { - if (this.agentAttributes == null) { - this.agentAttributes = new ConcurrentHashMap(); - Map map = new ConcurrentHashMap(); - - // Loop on a list of user-agent templates until a template match - // the current user-agent string. The list of templates is - // located in a file named "agent.properties" available on - // the classpath. - // Some defined variables are used in order to catch the name, - // version and optional comment. Respectively, these - // variables are called "agentName", "agentVersion" and - // "agentComment". - org.restlet.routing.Template template = null; - // Predefined variables. - org.restlet.routing.Variable agentName = new org.restlet.routing.Variable( - org.restlet.routing.Variable.TYPE_TOKEN); - org.restlet.routing.Variable agentVersion = new org.restlet.routing.Variable( - org.restlet.routing.Variable.TYPE_TOKEN); - org.restlet.routing.Variable agentComment = new org.restlet.routing.Variable( - org.restlet.routing.Variable.TYPE_COMMENT); - org.restlet.routing.Variable agentCommentAttribute = new org.restlet.routing.Variable( - org.restlet.routing.Variable.TYPE_COMMENT_ATTRIBUTE); - org.restlet.routing.Variable facultativeData = new org.restlet.routing.Variable( - org.restlet.routing.Variable.TYPE_ALL, null, false, false); - - if (ClientInfo.getUserAgentTemplates() != null) { - for (String string : ClientInfo.getUserAgentTemplates()) { - template = new org.restlet.routing.Template(string, org.restlet.routing.Template.MODE_EQUALS); - - // Update the predefined variables. - template.getVariables().put("agentName", agentName); - template.getVariables().put("agentVersion", agentVersion); - template.getVariables().put("agentComment", agentComment); - template.getVariables().put("agentOs", agentCommentAttribute); - template.getVariables().put("commentAttribute", agentCommentAttribute); - template.getVariables().put("facultativeData", facultativeData); - - // Parse the template - if (template.parse(getAgent(), map) > -1) { - for (String key : map.keySet()) { - this.agentAttributes.put(key, (String) map.get(key)); - } - break; - } - } - } - } - - return this.agentAttributes; - } - - /** - * Returns the name of the user agent. - * - * @return The name of the user agent. - * @see #getAgent() - */ - public String getAgentName() { - final Product product = getMainAgentProduct(); - if (product != null) { - return product.getName(); - } - - return null; - } - - /** - * Returns the list of product tokens from the user agent name. - * - * @return The list of product tokens from the user agent name. - * @see #getAgent() - */ - public List getAgentProducts() { - if (this.agentProducts == null) { - this.agentProducts = org.restlet.engine.header.ProductReader.read(getAgent()); - } - return this.agentProducts; - } - - /** - * Returns the version of the user agent. - * - * @return The version of the user agent. - * @see #getAgent() - */ - public String getAgentVersion() { - final Product product = getMainAgentProduct(); - if (product != null) { - return product.getVersion(); - } - return null; - - } - - /** - * Returns the client certificates. Those certificates are available when a - * request is received via an HTTPS connection, corresponding to the SSL/TLS - * certificates. - * - * @return The client certificates. - * @see javax.net.ssl.SSLSession#getPeerCertificates() - */ - public List getCertificates() { - // Lazy initialization with double-check. - List a = this.certificates; - if (a == null) { - synchronized (this) { - a = this.certificates; - if (a == null) { - this.certificates = a = new CopyOnWriteArrayList(); - } - } - } - return a; - } - - /** - * Returns the SSL Cipher Suite, if available and accessible. - * - * @return The SSL Cipher Suite, if available and accessible. - * @see javax.net.ssl.SSLSession#getCipherSuite() - */ - public String getCipherSuite() { - return this.cipherSuite; - } - - /** - * Returns the client expectations. - * - * @return The client expectations. - */ - public List getExpectations() { - // Lazy initialization with double-check. - List a = this.expectations; - if (a == null) { - synchronized (this) { - a = this.expectations; - if (a == null) { - this.expectations = a = new CopyOnWriteArrayList(); - } - } - } - return a; - } - - /** - * Returns the list of forwarded IP addresses. This is useful when the user - * agent is separated from the origin server by a chain of intermediary - * components. Creates a new instance if no one has been set.
- *
- * The first address is the one of the immediate client component and the last - * address should correspond to the origin client (frequently a user agent).
- *
- * This information is only safe for intermediary components within your local - * network. Other addresses could easily be changed by setting a fake header and - * should not be trusted for serious security checks.
- *
- * Note that your HTTP server connectors need to have a special - * "useForwardedForHeader" parameter explicitly set to "true" in order to - * activate this feature, due to potential security issues. - * - * @return The list of forwarded IP addresses. - * @see #getUpstreamAddress() - * @see Wikipedia page - * for the "X-Forwarded-For" HTTP header - */ - public List getForwardedAddresses() { - // Lazy initialization with double-check. - List a = this.forwardedAddresses; - if (a == null) { - synchronized (this) { - a = this.forwardedAddresses; - if (a == null) { - this.forwardedAddresses = a = new CopyOnWriteArrayList(); - } - } - } - return a; - } - - /** - * Returns the email address of the human user controlling the user agent. - * Default value is null. - * - * @return The email address of the human user controlling the user agent. - */ - public String getFrom() { - return from; - } - - /** - * Returns a Product object based on the name of the user agent. - * - * @return A Product object based on name of the user agent. - */ - public Product getMainAgentProduct() { - if (this.agentMainProduct == null) { - if (getAgentAttributes() != null) { - this.agentMainProduct = new Product(getAgentAttributes().get("agentName"), - getAgentAttributes().get("agentVersion"), getAgentAttributes().get("agentComment")); - } - } - - return this.agentMainProduct; - } - - /** - * Returns the port number which sent the call. If no port is specified, -1 is - * returned. - * - * @return The port number which sent the call. - */ - public int getPort() { - return this.port; - } - - /** - * Returns the preferred character set among a list of supported ones, based on - * the client preferences. - * - * @param supported The supported character sets. - * @return The preferred character set. - */ - public CharacterSet getPreferredCharacterSet(List supported) { - return getPreferredMetadata(supported, getAcceptedCharacterSets()); - } - - /** - * Returns the preferred encoding among a list of supported ones, based on the - * client preferences. - * - * @param supported The supported encodings. - * @return The preferred encoding. - */ - public Encoding getPreferredEncoding(List supported) { - return getPreferredMetadata(supported, getAcceptedEncodings()); - } - - /** - * Returns the preferred language among a list of supported ones, based on the - * client preferences. - * - * @param supported The supported languages. - * @return The preferred language. - */ - public Language getPreferredLanguage(List supported) { - return getPreferredMetadata(supported, getAcceptedLanguages()); - } - - /** - * Returns the preferred media type among a list of supported ones, based on the - * client preferences. - * - * @param supported The supported media types. - * @return The preferred media type. - */ - public MediaType getPreferredMediaType(List supported) { - return getPreferredMetadata(supported, getAcceptedMediaTypes()); - } - - /** - * Returns the preferred patch among a list of supported ones, based on the - * client preferences. - * - * @param supported The supported patches. - * @return The preferred patch. - */ - public MediaType getPreferredPatch(List supported) { - return getPreferredMetadata(supported, getAcceptedPatches()); - } - - /** - * Returns the additional client principals. Note that {@link #getUser()} and - * {@link #getRoles()} methods already return user and role principals. - * - * @return The additional client principals. - */ - public List getPrincipals() { - // Lazy initialization with double-check. - List a = this.principals; - if (a == null) { - synchronized (this) { - a = this.principals; - if (a == null) { - this.principals = a = new CopyOnWriteArrayList(); - } - } - } - return a; - } - - /** - * Returns the authenticated user roles. - * - * @return The authenticated user roles. - */ - public List getRoles() { - // Lazy initialization with double-check. - List a = this.roles; - if (a == null) { - synchronized (this) { - a = this.roles; - if (a == null) { - this.roles = a = new CopyOnWriteArrayList(); - } - } - } - return a; - } - - /** - * Returns the IP address of the upstream client component. In general this will - * correspond the the user agent IP address. This is useful if there are - * intermediary components like proxies and load balancers. - * - * If the supporting {@link #getForwardedAddresses()} method returns a non empty - * list, the IP address will be the first element. Otherwise, the value of - * {@link #getAddress()} will be returned.
- *
- * Note that your HTTP server connectors need to have a special - * "useForwardedForHeader" parameter explicitly set to "true" in order to - * activate this feature, due to potential security issues. - * - * @return The most upstream IP address. - * @see #getAddress() - * @see #getForwardedAddresses() - */ - public String getUpstreamAddress() { - if (this.forwardedAddresses == null || this.forwardedAddresses.isEmpty()) { - return getAddress(); - } - - return this.forwardedAddresses.get(0); - } - - /** - * Returns the authenticated user. - * - * @return The authenticated user. - */ - public org.restlet.security.User getUser() { - return user; - } - - /** - * Indicates if the identifier or principal has been authenticated. The - * application is responsible for updating this property, relying on a - * {@link org.restlet.security.Authenticator} or manually. - * - * @return True if the identifier or principal has been authenticated. - */ - public boolean isAuthenticated() { - return this.authenticated; - } - - /** - * Sets the character set preferences. Note that when used with HTTP connectors, - * this property maps to the "Accept-Charset" header. - * - * @param acceptedCharacterSets The character set preferences. - */ - public void setAcceptedCharacterSets(List> acceptedCharacterSets) { - synchronized (this) { - List> ac = getAcceptedCharacterSets(); - ac.clear(); - ac.addAll(acceptedCharacterSets); - } - } - - /** - * Sets the encoding preferences. Note that when used with HTTP connectors, this - * property maps to the "Accept-Encoding" header. - * - * @param acceptedEncodings The encoding preferences. - */ - public void setAcceptedEncodings(List> acceptedEncodings) { - synchronized (this) { - List> ac = getAcceptedEncodings(); - ac.clear(); - ac.addAll(acceptedEncodings); - } - } - - /** - * Sets the language preferences. Note that when used with HTTP connectors, this - * property maps to the "Accept-Language" header. - * - * @param acceptedLanguages The language preferences. - */ - public void setAcceptedLanguages(List> acceptedLanguages) { - synchronized (this) { - List> ac = getAcceptedLanguages(); - ac.clear(); - ac.addAll(acceptedLanguages); - } - } - - /** - * Sets the media type preferences. Note that when used with HTTP connectors, - * this property maps to the "Accept" header. - * - * @param acceptedMediaTypes The media type preferences. - */ - public void setAcceptedMediaTypes(List> acceptedMediaTypes) { - synchronized (this) { - List> ac = getAcceptedMediaTypes(); - ac.clear(); - ac.addAll(acceptedMediaTypes); - } - } - - /** - * Sets the patch preferences. Note that when used with HTTP connectors, this - * property maps to the "Accept-Patch" header. - * - * @param acceptedPatches The media type preferences. - */ - public void setAcceptedPatches(List> acceptedPatches) { - synchronized (this) { - List> ac = getAcceptedPatches(); - ac.clear(); - ac.addAll(acceptedPatches); - } - } - - /** - * Sets the client's IP address. - * - * @param address The client's IP address. - */ - public void setAddress(String address) { - this.address = address; - } - - /** - * Sets the agent name (ex: "Restlet-Framework/2.0"). Note that when used with - * HTTP connectors, this property maps to the "User-Agent" header. - * - * @param agent The agent name. - */ - public void setAgent(String agent) { - this.agent = agent; - } - - /** - * Sets a list of attributes taken from the name of the user agent. - * - * @param agentAttributes A list of attributes taken from the name of the user - * agent. - */ - public void setAgentAttributes(Map agentAttributes) { - synchronized (this) { - Map aa = getAgentAttributes(); - aa.clear(); - aa.putAll(agentAttributes); - } - } - - /** - * Sets the list of product tokens from the user agent name. - * - * @param agentProducts The list of product tokens from the user agent name. - */ - public void setAgentProducts(List agentProducts) { - synchronized (this) { - List ap = getAgentProducts(); - ap.clear(); - ap.addAll(agentProducts); - } - } - - /** - * Indicates if the identifier or principal has been authenticated. The - * application is responsible for updating this property, relying on a - * {@link org.restlet.security.Authenticator} or manually. - * - * @param authenticated True if the identifier or principal has been - * authenticated. - */ - public void setAuthenticated(boolean authenticated) { - this.authenticated = authenticated; - } - - /** - * Sets the new client certificates. - * - * @param certificates The client certificates. - * @see #getCertificates() - */ - public void setCertificates(List certificates) { - synchronized (this) { - List fa = getCertificates(); - fa.clear(); - fa.addAll(certificates); - } - } - - /** - * Sets the SSL Cipher Suite, if available and accessible. - * - * @param cipherSuite The SSL Cipher Suite, if available and accessible. - */ - public void setCipherSuite(String cipherSuite) { - this.cipherSuite = cipherSuite; - } - - /** - * Sets the client expectations. - * - * @param expectations The client expectations. - */ - public void setExpectations(List expectations) { - synchronized (this) { - List e = getExpectations(); - e.clear(); - e.addAll(expectations); - } - } - - /** - * Sets the list of forwarded IP addresses. - * - * @param forwardedAddresses The list of forwarded IP addresses. - * @see #getForwardedAddresses() - */ - public void setForwardedAddresses(List forwardedAddresses) { - synchronized (this) { - List fa = getForwardedAddresses(); - fa.clear(); - fa.addAll(forwardedAddresses); - } - } - - /** - * Sets the email address of the human user controlling the user agent. - * - * @param from The email address of the human user controlling the user agent. - */ - public void setFrom(String from) { - this.from = from; - } - - /** - * Sets the port number which sent the call. - * - * @param port The port number which sent the call. - */ - public void setPort(int port) { - this.port = port; - } - - /** - * Sets the additional client principals. - * - * @param principals The additional client principals. - * @see #getPrincipals() - */ - public void setPrincipals(List principals) { - synchronized (this) { - List fa = getPrincipals(); - fa.clear(); - fa.addAll(principals); - } - } - - /** - * Sets the authenticated user roles. - * - * @param roles The authenticated user roles. - */ - public void setRoles(List roles) { - synchronized (this) { - List r = getRoles(); - r.clear(); - r.addAll(roles); - } - } - - /** - * Sets the authenticated user. - * - * @param user The authenticated user. - */ - public void setUser(org.restlet.security.User user) { - this.user = user; - } - + /** + * List of user-agent templates defined in the "agent.properties" file.
+ * + * @see ClientInfo#getAgentAttributes() + */ + private static volatile List userAgentTemplates = null; + + /** + * Returns the preferred metadata taking into account both metadata supported by the server and + * client preferences. + * + * @param supported The metadata supported by the server. + * @param preferences The client preferences. + * @return The preferred metadata. + */ + public static T getPreferredMetadata( + List supported, List> preferences) { + T result = null; + float maxQuality = 0; + + if (supported != null) { + for (Preference pref : preferences) { + for (T metadata : supported) { + if (pref.getMetadata().isCompatible(metadata) + && (pref.getQuality() > maxQuality)) { + result = metadata; + maxQuality = pref.getQuality(); + } + } + } + } + + return result; + } + + /** + * Returns the list of user-agent templates defined in the "agent.properties" file. + * + * @return The list of user-agent templates defined in the "agent.properties" file. + * @see ClientInfo#getAgentAttributes() + */ + private static List getUserAgentTemplates() { + // Lazy initialization with double-check. + List u = ClientInfo.userAgentTemplates; + if (u == null) { + synchronized (ClientInfo.class) { + u = ClientInfo.userAgentTemplates; + if (u == null) { + // Load from the "agent.properties" file + java.net.URL userAgentPropertiesUrl = + Engine.getResource("org/restlet/data/agent.properties"); + if (userAgentPropertiesUrl != null) { + java.io.BufferedReader reader; + try { + reader = + new java.io.BufferedReader( + new InputStreamReader( + userAgentPropertiesUrl.openStream(), + CharacterSet.UTF_8.getName()), + IoUtils.BUFFER_SIZE); + String line = reader.readLine(); + for (; line != null; line = reader.readLine()) { + final String trim = line.trim(); + if ((!trim.isEmpty()) && !trim.startsWith("#")) { + if (u == null) { + u = new CopyOnWriteArrayList<>(); + } + u.add(line); + } + } + reader.close(); + } catch (IOException e) { + if (Context.getCurrent() != null) { + Context.getCurrent() + .getLogger() + .warning( + "Cannot read '" + + userAgentPropertiesUrl.toString() + + "' due to: " + + e.getMessage()); + } + } + } + ClientInfo.userAgentTemplates = u; + } + } + } + return u; + } + + /** The character set preferences. */ + private volatile List> acceptedCharacterSets; + + /** The encoding preferences. */ + private volatile List> acceptedEncodings; + + /** The language preferences. */ + private volatile List> acceptedLanguages; + + /** The media preferences. */ + private volatile List> acceptedMediaTypes; + + /** The patch preferences. */ + private volatile List> acceptedPatches; + + /** The immediate IP addresses. */ + private volatile String address; + + /** The agent name. */ + private volatile String agent; + + /** The attributes data taken from the agent name. */ + private volatile Map agentAttributes; + + /** The main product data taken from the agent name. */ + private volatile Product agentMainProduct; + + /** The list of product tokens taken from the agent name. */ + private volatile List agentProducts; + + /** + * Indicates if the subject has been authenticated. The application is responsible for updating + * this property, relying on {@link org.restlet.security.Authenticator} or manually. + */ + private volatile boolean authenticated; + + /** List of client certificates. */ + private volatile List certificates; + + /** The SSL Cipher Suite, if available and accessible. */ + private volatile String cipherSuite; + + /** List of expectations. */ + private volatile List expectations; + + /** The forwarded IP addresses. */ + private volatile List forwardedAddresses; + + /** The email address of the human user controlling the user agent. */ + private volatile String from; + + /** The port number. */ + private volatile int port; + + /** List of additional client principals. */ + private volatile List principals; + + /** List of user roles. */ + private volatile List roles; + + /** Authenticated user. */ + private volatile org.restlet.security.User user; + + /** Constructor. */ + public ClientInfo() { + this.address = null; + this.agent = null; + this.port = -1; + this.acceptedCharacterSets = null; + this.acceptedEncodings = null; + this.acceptedLanguages = null; + this.acceptedMediaTypes = null; + this.acceptedPatches = null; + this.forwardedAddresses = null; + this.from = null; + this.agentProducts = null; + this.principals = null; + this.user = null; + this.roles = null; + this.expectations = null; + } + + /** + * Constructor from a list of variants. Note that only media types are taken into account. + * + * @param variants The variants corresponding to the accepted media types. + */ + public ClientInfo(List variants) { + if (variants != null) { + for (org.restlet.representation.Variant variant : variants) { + getAcceptedMediaTypes().add(new Preference(variant.getMediaType())); + } + } + } + + /** + * Constructor from a media type. + * + * @param mediaType The preferred media type. + */ + public ClientInfo(MediaType mediaType) { + getAcceptedMediaTypes().add(new Preference(mediaType)); + } + + /** + * Updates the client preferences to accept the given metadata (media types, character sets, + * etc.) with a 1.0 quality in addition to existing ones. + * + * @param metadata The metadata to accept. + */ + public void accept(Metadata... metadata) { + if (metadata != null) { + for (Metadata md : metadata) { + accept(md, 1.0F); + } + } + } + + /** + * Updates the client preferences to accept the given metadata (media types, character sets, + * etc.) with a given quality in addition to existing ones. + * + * @param metadata The metadata to accept. + * @param quality The quality to set. + */ + public void accept(Metadata metadata, float quality) { + switch (metadata) { + case MediaType mediaType -> + getAcceptedMediaTypes().add(new Preference<>(mediaType, quality)); + case Language language -> + getAcceptedLanguages().add(new Preference<>(language, quality)); + case Encoding encoding -> + getAcceptedEncodings().add(new Preference<>(encoding, quality)); + case null, default -> + getAcceptedCharacterSets() + .add(new Preference<>((CharacterSet) metadata, quality)); + } + } + + /** + * Returns the modifiable list of character set preferences. Creates a new instance if no one + * has been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Accept-Charset" header. + * + * @return The character set preferences. + */ + public List> getAcceptedCharacterSets() { + // Lazy initialization with double-check. + List> a = this.acceptedCharacterSets; + if (a == null) { + synchronized (this) { + a = this.acceptedCharacterSets; + if (a == null) { + this.acceptedCharacterSets = + a = new CopyOnWriteArrayList>(); + } + } + } + return a; + } + + /** + * Returns the modifiable list of encoding preferences. Creates a new instance if no one has + * been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Accept-Encoding" header. + * + * @return The encoding preferences. + */ + public List> getAcceptedEncodings() { + // Lazy initialization with double-check. + List> a = this.acceptedEncodings; + if (a == null) { + synchronized (this) { + a = this.acceptedEncodings; + if (a == null) { + this.acceptedEncodings = a = new CopyOnWriteArrayList>(); + } + } + } + return a; + } + + /** + * Returns the modifiable list of language preferences. Creates a new instance if no one has + * been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Accept-Language" header. + * + * @return The language preferences. + */ + public List> getAcceptedLanguages() { + // Lazy initialization with double-check. + List> a = this.acceptedLanguages; + if (a == null) { + synchronized (this) { + a = this.acceptedLanguages; + if (a == null) { + this.acceptedLanguages = a = new CopyOnWriteArrayList>(); + } + } + } + return a; + } + + /** + * Returns the modifiable list of media type preferences. Creates a new instance if no one has + * been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Accept" header. + * + * @return The media type preferences. + */ + public List> getAcceptedMediaTypes() { + // Lazy initialization with double-check. + List> a = this.acceptedMediaTypes; + if (a == null) { + synchronized (this) { + a = this.acceptedMediaTypes; + if (a == null) { + this.acceptedMediaTypes = a = new CopyOnWriteArrayList>(); + } + } + } + return a; + } + + /** + * Returns the modifiable list of patch preferences. Creates a new instance if no one has been + * set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Accept-Patch" header. + * + * @return The patch preferences. + */ + public List> getAcceptedPatches() { + // Lazy initialization with double-check. + List> a = this.acceptedPatches; + if (a == null) { + synchronized (this) { + a = this.acceptedPatches; + if (a == null) { + this.acceptedPatches = a = new CopyOnWriteArrayList>(); + } + } + } + return a; + } + + /** + * Returns the immediate client's IP address. If the real client is separated from the server by + * a proxy server, this will return the IP address of the proxy. + * + * @return The immediate client's IP address. + * @see #getUpstreamAddress() + * @see #getForwardedAddresses() + */ + public String getAddress() { + return this.address; + } + + /** + * Returns the agent name (ex: "Restlet-Framework/2.0"). Note that when used with HTTP + * connectors, this property maps to the "User-Agent" header. + * + * @return The agent name. + */ + public String getAgent() { + return this.agent; + } + + /** + * Returns a list of attributes taken from the name of the user agent. + * + * @return A list of attributes taken from the name of the user agent. + * @see #getAgent() + */ + public Map getAgentAttributes() { + if (this.agentAttributes == null) { + this.agentAttributes = new ConcurrentHashMap(); + Map map = new ConcurrentHashMap(); + + // Loop on a list of user-agent templates until a template match + // the current user-agent string. The list of templates is + // located in a file named "agent.properties" available on + // the classpath. + // Some defined variables are used to catch the name, + // version and optional comment. Respectively, these + // variables are called "agentName", "agentVersion" and + // "agentComment". + org.restlet.routing.Template template = null; + // Predefined variables. + org.restlet.routing.Variable agentName = + new org.restlet.routing.Variable(org.restlet.routing.Variable.TYPE_TOKEN); + org.restlet.routing.Variable agentVersion = + new org.restlet.routing.Variable(org.restlet.routing.Variable.TYPE_TOKEN); + org.restlet.routing.Variable agentComment = + new org.restlet.routing.Variable(org.restlet.routing.Variable.TYPE_COMMENT); + org.restlet.routing.Variable agentCommentAttribute = + new org.restlet.routing.Variable( + org.restlet.routing.Variable.TYPE_COMMENT_ATTRIBUTE); + org.restlet.routing.Variable facultativeData = + new org.restlet.routing.Variable( + org.restlet.routing.Variable.TYPE_ALL, null, false, false); + + if (ClientInfo.getUserAgentTemplates() != null) { + for (String string : ClientInfo.getUserAgentTemplates()) { + template = + new org.restlet.routing.Template( + string, org.restlet.routing.Template.MODE_EQUALS); + + // Update the predefined variables. + template.getVariables().put("agentName", agentName); + template.getVariables().put("agentVersion", agentVersion); + template.getVariables().put("agentComment", agentComment); + template.getVariables().put("agentOs", agentCommentAttribute); + template.getVariables().put("commentAttribute", agentCommentAttribute); + template.getVariables().put("facultativeData", facultativeData); + + // Parse the template + if (template.parse(getAgent(), map) > -1) { + for (String key : map.keySet()) { + this.agentAttributes.put(key, (String) map.get(key)); + } + break; + } + } + } + } + + return this.agentAttributes; + } + + /** + * Returns the name of the user agent. + * + * @return The name of the user agent. + * @see #getAgent() + */ + public String getAgentName() { + final Product product = getMainAgentProduct(); + if (product != null) { + return product.getName(); + } + + return null; + } + + /** + * Returns the list of product tokens from the user agent name. + * + * @return The list of product tokens from the user agent name. + * @see #getAgent() + */ + public List getAgentProducts() { + if (this.agentProducts == null) { + this.agentProducts = org.restlet.engine.header.ProductReader.read(getAgent()); + } + return this.agentProducts; + } + + /** + * Returns the version of the user agent. + * + * @return The version of the user agent. + * @see #getAgent() + */ + public String getAgentVersion() { + final Product product = getMainAgentProduct(); + if (product != null) { + return product.getVersion(); + } + return null; + } + + /** + * Returns the client certificates. Those certificates are available when a request is received + * via an HTTPS connection, corresponding to the SSL/TLS certificates. + * + * @return The client certificates. + * @see javax.net.ssl.SSLSession#getPeerCertificates() + */ + public List getCertificates() { + // Lazy initialization with double-check. + List a = this.certificates; + if (a == null) { + synchronized (this) { + a = this.certificates; + if (a == null) { + this.certificates = + a = new CopyOnWriteArrayList(); + } + } + } + return a; + } + + /** + * Returns the SSL Cipher Suite, if available and accessible. + * + * @return The SSL Cipher Suite, if available and accessible. + * @see javax.net.ssl.SSLSession#getCipherSuite() + */ + public String getCipherSuite() { + return this.cipherSuite; + } + + /** + * Returns the client expectations. + * + * @return The client expectations. + */ + public List getExpectations() { + // Lazy initialization with double-check. + List a = this.expectations; + if (a == null) { + synchronized (this) { + a = this.expectations; + if (a == null) { + this.expectations = + a = new CopyOnWriteArrayList(); + } + } + } + return a; + } + + /** + * Returns the list of forwarded IP addresses. This is useful when the user agent is separated + * from the origin server by a chain of intermediary components. Creates a new instance if no + * one has been set.
+ *
+ * The first address is the one of the immediate client component, and the last address should + * correspond to the origin client (frequently a user agent).
+ *
+ * This information is only safe for intermediary components within your local network. Other + * addresses could easily be changed by setting a fake header and should not be trusted for + * serious security checks.
+ *
+ * Note that your HTTP server connectors need to have a special "useForwardedForHeader" + * parameter explicitly set to "true" to activate this feature, due to potential security + * issues. + * + * @return The list of forwarded IP addresses. + * @see #getUpstreamAddress() + * @see Wikipedia page for the + * "X-Forwarded-For" HTTP header + */ + public List getForwardedAddresses() { + // Lazy initialization with double-check. + List a = this.forwardedAddresses; + if (a == null) { + synchronized (this) { + a = this.forwardedAddresses; + if (a == null) { + this.forwardedAddresses = a = new CopyOnWriteArrayList(); + } + } + } + return a; + } + + /** + * Returns the email address of the human user controlling the user agent. The default value is + * null. + * + * @return The email address of the human user controlling the user agent. + */ + public String getFrom() { + return from; + } + + /** + * Returns a Product object based on the name of the user agent. + * + * @return A Product object based on the name of the user agent. + */ + public Product getMainAgentProduct() { + if (this.agentMainProduct == null) { + if (getAgentAttributes() != null) { + this.agentMainProduct = + new Product( + getAgentAttributes().get("agentName"), + getAgentAttributes().get("agentVersion"), + getAgentAttributes().get("agentComment")); + } + } + + return this.agentMainProduct; + } + + /** + * Returns the port number which sent the call. If no port is specified, -1 is returned. + * + * @return The port number which sent the call. + */ + public int getPort() { + return this.port; + } + + /** + * Returns the preferred character set among a list of supported ones, based on the client + * preferences. + * + * @param supported The supported character sets. + * @return The preferred character set. + */ + public CharacterSet getPreferredCharacterSet(List supported) { + return getPreferredMetadata(supported, getAcceptedCharacterSets()); + } + + /** + * Returns the preferred encoding among a list of supported ones, based on the client + * preferences. + * + * @param supported The supported encodings. + * @return The preferred encoding. + */ + public Encoding getPreferredEncoding(List supported) { + return getPreferredMetadata(supported, getAcceptedEncodings()); + } + + /** + * Returns the preferred language among a list of supported ones, based on the client + * preferences. + * + * @param supported The supported languages. + * @return The preferred language. + */ + public Language getPreferredLanguage(List supported) { + return getPreferredMetadata(supported, getAcceptedLanguages()); + } + + /** + * Returns the preferred media type among a list of supported ones, based on the client + * preferences. + * + * @param supported The supported media types. + * @return The preferred media type. + */ + public MediaType getPreferredMediaType(List supported) { + return getPreferredMetadata(supported, getAcceptedMediaTypes()); + } + + /** + * Returns the preferred patch among a list of supported ones, based on the client preferences. + * + * @param supported The supported patches. + * @return The preferred patch. + */ + public MediaType getPreferredPatch(List supported) { + return getPreferredMetadata(supported, getAcceptedPatches()); + } + + /** + * Returns the additional client principals. Note that {@link #getUser()} and {@link + * #getRoles()} methods already return user and role principals. + * + * @return The additional client principals. + */ + public List getPrincipals() { + // Lazy initialization with double-check. + List a = this.principals; + if (a == null) { + synchronized (this) { + a = this.principals; + if (a == null) { + this.principals = a = new CopyOnWriteArrayList(); + } + } + } + return a; + } + + /** + * Returns the authenticated user roles. + * + * @return The authenticated user roles. + */ + public List getRoles() { + // Lazy initialization with double-check. + List a = this.roles; + if (a == null) { + synchronized (this) { + a = this.roles; + if (a == null) { + this.roles = a = new CopyOnWriteArrayList(); + } + } + } + return a; + } + + /** + * Returns the IP address of the upstream client component. In general, this will correspond the + * the user agent IP address. This is useful if there are intermediary components like proxies + * and load balancers. + * + *

If the supporting {@link #getForwardedAddresses()} method returns a non-empty list, the IP + * address will be the first element. Otherwise, the value of {@link #getAddress()} will be + * returned.
+ *
+ * Note that your HTTP server connectors need to have a special "useForwardedForHeader" + * parameter explicitly set to "true" to activate this feature, due to potential security + * issues. + * + * @return The most upstream IP address. + * @see #getAddress() + * @see #getForwardedAddresses() + */ + public String getUpstreamAddress() { + if (this.forwardedAddresses == null || this.forwardedAddresses.isEmpty()) { + return getAddress(); + } + + return this.forwardedAddresses.getFirst(); + } + + /** + * Returns the authenticated user. + * + * @return The authenticated user. + */ + public org.restlet.security.User getUser() { + return user; + } + + /** + * Indicates if the identifier or principal has been authenticated. The application is + * responsible for updating this property, relying on a {@link + * org.restlet.security.Authenticator} or manually. + * + * @return True if the identifier or principal has been authenticated. + */ + public boolean isAuthenticated() { + return this.authenticated; + } + + /** + * Sets the character set preferences. Note that when used with HTTP connectors, this property + * maps to the "Accept-Charset" header. + * + * @param acceptedCharacterSets The character set preferences. + */ + public void setAcceptedCharacterSets(List> acceptedCharacterSets) { + synchronized (this) { + List> ac = getAcceptedCharacterSets(); + ac.clear(); + ac.addAll(acceptedCharacterSets); + } + } + + /** + * Sets the encoding preferences. Note that when used with HTTP connectors, this property maps + * to the "Accept-Encoding" header. + * + * @param acceptedEncodings The encoding preferences. + */ + public void setAcceptedEncodings(List> acceptedEncodings) { + synchronized (this) { + List> ac = getAcceptedEncodings(); + ac.clear(); + ac.addAll(acceptedEncodings); + } + } + + /** + * Sets the language preferences. Note that when used with HTTP connectors, this property maps + * to the "Accept-Language" header. + * + * @param acceptedLanguages The language preferences. + */ + public void setAcceptedLanguages(List> acceptedLanguages) { + synchronized (this) { + List> ac = getAcceptedLanguages(); + ac.clear(); + ac.addAll(acceptedLanguages); + } + } + + /** + * Sets the media type preferences. Note that when used with HTTP connectors, this property maps + * to the "Accept" header. + * + * @param acceptedMediaTypes The media type preferences. + */ + public void setAcceptedMediaTypes(List> acceptedMediaTypes) { + synchronized (this) { + List> ac = getAcceptedMediaTypes(); + ac.clear(); + ac.addAll(acceptedMediaTypes); + } + } + + /** + * Sets the patch preferences. Note that when used with HTTP connectors, this property maps to + * the "Accept-Patch" header. + * + * @param acceptedPatches The media type preferences. + */ + public void setAcceptedPatches(List> acceptedPatches) { + synchronized (this) { + List> ac = getAcceptedPatches(); + ac.clear(); + ac.addAll(acceptedPatches); + } + } + + /** + * Sets the client's IP address. + * + * @param address The client's IP address. + */ + public void setAddress(String address) { + this.address = address; + } + + /** + * Sets the agent name (ex: "Restlet-Framework/2.0"). Note that when used with HTTP connectors, + * this property maps to the "User-Agent" header. + * + * @param agent The agent name. + */ + public void setAgent(String agent) { + this.agent = agent; + } + + /** + * Sets a list of attributes taken from the name of the user agent. + * + * @param agentAttributes A list of attributes taken from the name of the user agent. + */ + public void setAgentAttributes(Map agentAttributes) { + synchronized (this) { + Map aa = getAgentAttributes(); + aa.clear(); + aa.putAll(agentAttributes); + } + } + + /** + * Sets the list of product tokens from the user agent name. + * + * @param agentProducts The list of product tokens from the user agent name. + */ + public void setAgentProducts(List agentProducts) { + synchronized (this) { + List ap = getAgentProducts(); + ap.clear(); + ap.addAll(agentProducts); + } + } + + /** + * Indicates if the identifier or principal has been authenticated. The application is + * responsible for updating this property, relying on a {@link + * org.restlet.security.Authenticator} or manually. + * + * @param authenticated True if the identifier or principal has been authenticated. + */ + public void setAuthenticated(boolean authenticated) { + this.authenticated = authenticated; + } + + /** + * Sets the new client certificates. + * + * @param certificates The client certificates. + * @see #getCertificates() + */ + public void setCertificates(List certificates) { + synchronized (this) { + List fa = getCertificates(); + fa.clear(); + fa.addAll(certificates); + } + } + + /** + * Sets the SSL Cipher Suite, if available and accessible. + * + * @param cipherSuite The SSL Cipher Suite, if available and accessible. + */ + public void setCipherSuite(String cipherSuite) { + this.cipherSuite = cipherSuite; + } + + /** + * Sets the client expectations. + * + * @param expectations The client expectations. + */ + public void setExpectations(List expectations) { + synchronized (this) { + List e = getExpectations(); + e.clear(); + e.addAll(expectations); + } + } + + /** + * Sets the list of forwarded IP addresses. + * + * @param forwardedAddresses The list of forwarded IP addresses. + * @see #getForwardedAddresses() + */ + public void setForwardedAddresses(List forwardedAddresses) { + synchronized (this) { + List fa = getForwardedAddresses(); + fa.clear(); + fa.addAll(forwardedAddresses); + } + } + + /** + * Sets the email address of the human user controlling the user agent. + * + * @param from The email address of the human user controlling the user agent. + */ + public void setFrom(String from) { + this.from = from; + } + + /** + * Sets the port number which sent the call. + * + * @param port The port number which sent the call. + */ + public void setPort(int port) { + this.port = port; + } + + /** + * Sets the additional client principals. + * + * @param principals The additional client principals. + * @see #getPrincipals() + */ + public void setPrincipals(List principals) { + synchronized (this) { + List fa = getPrincipals(); + fa.clear(); + fa.addAll(principals); + } + } + + /** + * Sets the authenticated user roles. + * + * @param roles The authenticated user roles. + */ + public void setRoles(List roles) { + synchronized (this) { + List r = getRoles(); + r.clear(); + r.addAll(roles); + } + } + + /** + * Sets the authenticated user. + * + * @param user The authenticated user. + */ + public void setUser(org.restlet.security.User user) { + this.user = user; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Conditions.java b/org.restlet/src/main/java/org/restlet/data/Conditions.java index 690ffb7c89..cd551a8b8a 100644 --- a/org.restlet/src/main/java/org/restlet/data/Conditions.java +++ b/org.restlet/src/main/java/org/restlet/data/Conditions.java @@ -1,439 +1,424 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.engine.util.DateUtils; -import org.restlet.representation.RepresentationInfo; - import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; +import org.restlet.engine.util.DateUtils; +import org.restlet.representation.RepresentationInfo; /** - * Set of conditions applying to a request. This is equivalent to the HTTP - * conditional headers. - * - * @see If-Match - * @see If-Modified-Since - * @see If-None-Match - * @see If-Range - * @see If-Unmodified-Since - * + * Set of conditions applying to a request. This is an equivalent to the HTTP conditional headers. + * + * @see If-Match + * @see If-Modified-Since + * @see If-None-Match + * @see If-Range + * @see If-Unmodified-Since * @author Jerome Louvel */ public final class Conditions { - /** The "if-match" condition. */ - private volatile List match; - - /** The "if-modified-since" condition. */ - private volatile Date modifiedSince; - - /** The "if-none-match" condition. */ - private volatile List noneMatch; - - /** The "if-range" condition as a Date. */ - private volatile Date rangeDate; - - /** The "if-range" condition as an entity tag. */ - private volatile Tag rangeTag; - - /** The "if-unmodified-since" condition */ - private volatile Date unmodifiedSince; - - /** - * Constructor. - */ - public Conditions() { - } - - /** - * Returns the modifiable list of tags that must be matched. Creates a new - * instance if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Match" header. - * - * @return The "if-match" condition. - */ - public List getMatch() { - // Lazy initialization with double-check. - List m = this.match; - if (m == null) { - synchronized (this) { - m = this.match; - if (m == null) { - this.match = m = new ArrayList(); - } - } - } - return m; - } - - /** - * Returns the condition based on the modification date of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Modified-Since" header. - * - * @return The condition date. - */ - public Date getModifiedSince() { - return this.modifiedSince; - } - - /** - * Returns the modifiable list of tags that mustn't match. Creates a new - * instance if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-None-Match" header. - * - * @return The list of tags that mustn't match. - */ - public List getNoneMatch() { - // Lazy initialization with double-check. - List n = this.noneMatch; - if (n == null) { - synchronized (this) { - n = this.noneMatch; - if (n == null) { - this.noneMatch = n = new ArrayList(); - } - } - } - return n; - } - - /** - * Returns the range condition based on the modification date of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Range" header. - * - * @return The range condition date. - */ - public Date getRangeDate() { - return rangeDate; - } - - /** - * Returns the range conditional status of an entity. - * - * @param representationInfo The representation information that will be tested. - * @return the status of the response. - */ - public Status getRangeStatus(RepresentationInfo representationInfo) { - return getRangeStatus((representationInfo == null) ? null : representationInfo.getTag(), - (representationInfo == null) ? null : representationInfo.getModificationDate()); - } - - /** - * Returns the range conditional status of an entity. - * - * @param tag The tag of the entity. - * @param modificationDate The modification date of the entity. - * @return The status of the response. - */ - public Status getRangeStatus(Tag tag, Date modificationDate) { - Status result = Status.CLIENT_ERROR_PRECONDITION_FAILED; - if (getRangeTag() != null) { - boolean all = getRangeTag().equals(Tag.ALL); - - // If a tag exists - if (tag != null) { - if (all || getRangeTag().equals(tag)) { - result = Status.SUCCESS_OK; - } - } - } else if (getRangeDate() != null) { - // If a modification date exists - if (getRangeDate().equals(modificationDate)) { - result = Status.SUCCESS_OK; - } - } - - return result; - } - - /** - * Returns the range condition based on the entity tag of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Range" header. - * - * @return The range entity tag. - */ - public Tag getRangeTag() { - return rangeTag; - } - - /** - * Returns the conditional status of a variant using a given method. - * - * @param method The request method. - * @param entityExists Indicates if the entity exists. - * @param tag The tag. - * @param modificationDate The modification date. - * @return Null if the requested method can be performed, the status of the - * response otherwise. - */ - public Status getStatus(Method method, boolean entityExists, Tag tag, Date modificationDate) { - Status result = null; - - // Is the "if-Match" rule followed or not? - if ((this.match != null) && !this.match.isEmpty()) { - boolean matched = false; - boolean failed = false; - boolean all = (getMatch().size() > 0) && getMatch().get(0).equals(Tag.ALL); - String statusMessage = null; - - if (entityExists) { - // If a tag exists - if (!all && (tag != null)) { - // Check if it matches one of the representations already - // cached by the client - Tag matchTag; - - for (Iterator iter = getMatch().iterator(); !matched && iter.hasNext();) { - matchTag = iter.next(); - matched = matchTag.equals(tag, false); - } - } else { - matched = all; - } - } else { - // See - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24 - // If none of the entity tags match, or if "*" is given and no - // current entity exists, the server MUST NOT perform the - // requested method - failed = all; - statusMessage = "A non existing resource can't match any tag."; - } - - failed = failed || !matched; - - if (failed) { - result = Status.CLIENT_ERROR_PRECONDITION_FAILED; - if (statusMessage != null) { - result = new Status(result, statusMessage); - } - } - } - - // Is the "if-None-Match" rule followed or not? - if ((result == null) && (this.noneMatch != null) && !this.noneMatch.isEmpty()) { - boolean matched = false; - - if (entityExists) { - // If a tag exists - if (tag != null) { - // Check if it matches one of the representations - // already cached by the client - Tag noneMatchTag; - - for (Iterator iter = getNoneMatch().iterator(); !matched && iter.hasNext();) { - noneMatchTag = iter.next(); - matched = noneMatchTag.equals(tag, (Method.GET.equals(method) || Method.HEAD.equals(method))); - } - - // The current representation matches one of those already - // cached by the client - if (matched) { - // Check if the current representation has been updated - // since the "if-modified-since" date. In this case, the - // rule is followed. - Date modifiedSince = getModifiedSince(); - boolean isModifiedSince = (modifiedSince != null) && (DateUtils.after(new Date(), modifiedSince) - || (modificationDate == null) || DateUtils.after(modifiedSince, modificationDate)); - matched = !isModifiedSince; - } - } else { - matched = (getNoneMatch().size() > 0) && Tag.ALL.equals(getNoneMatch().get(0)); - } - } - - if (matched) { - if (Method.GET.equals(method) || Method.HEAD.equals(method)) { - result = Status.REDIRECTION_NOT_MODIFIED; - } else { - result = Status.CLIENT_ERROR_PRECONDITION_FAILED; - } - } - } - - // Is the "if-Modified-Since" rule followed or not? - if ((result == null) && (getModifiedSince() != null)) { - Date modifiedSince = getModifiedSince(); - boolean isModifiedSince = (DateUtils.after(new Date(), modifiedSince) || (modificationDate == null) - || DateUtils.after(modifiedSince, modificationDate)); - - if (!isModifiedSince) { - if (Method.GET.equals(method) || Method.HEAD.equals(method)) { - result = Status.REDIRECTION_NOT_MODIFIED; - } else { - result = Status.CLIENT_ERROR_PRECONDITION_FAILED; - } - } - } - - // Is the "if-Unmodified-Since" rule followed or not? - if ((result == null) && (getUnmodifiedSince() != null)) { - Date unModifiedSince = getUnmodifiedSince(); - boolean isUnModifiedSince = ((unModifiedSince == null) || (modificationDate == null) - || !DateUtils.before(modificationDate, unModifiedSince)); - - if (!isUnModifiedSince) { - result = Status.CLIENT_ERROR_PRECONDITION_FAILED; - } - } - - return result; - } - - /** - * Returns the conditional status of a variant using a given method. - * - * @param method The request method. - * @param representationInfo The representation information that will be tested. - * @return Null if the requested method can be performed, the status of the - * response otherwise. - */ - public Status getStatus(Method method, RepresentationInfo representationInfo) { - return getStatus(method, representationInfo != null, - (representationInfo == null) ? null : representationInfo.getTag(), - (representationInfo == null) ? null : representationInfo.getModificationDate()); - } - - /** - * Returns the condition based on the modification date of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Unmodified-Since" header. - * - * @return The condition date. - */ - public Date getUnmodifiedSince() { - return this.unmodifiedSince; - } - - /** - * Indicates if there are some conditions set. - * - * @return True if there are some conditions set. - */ - public boolean hasSome() { - return (((this.match != null) && !this.match.isEmpty()) - || ((this.noneMatch != null) && !this.noneMatch.isEmpty()) || (getModifiedSince() != null) - || (getUnmodifiedSince() != null)); - } - - /** - * Indicates if there are some range conditions set. - * - * @return True if there are some range conditions set. - */ - public boolean hasSomeRange() { - return getRangeTag() != null || getRangeDate() != null; - } - - /** - * Sets the modifiable list of tags that must be matched.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Match" header. - * - * @param tags The "if-match" condition. - */ - public void setMatch(List tags) { - this.match = tags; - } - - /** - * Sets the condition based on the modification date of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Modified-Since" header. - * - * @param date The modification date. - */ - public void setModifiedSince(Date date) { - this.modifiedSince = DateUtils.unmodifiable(date); - } - - /** - * Sets the modifiable list of tags that mustn't match. Creates a new instance - * if no one has been set.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-None-Match" header. - * - * @param tags The list of tags that mustn't match. - */ - public void setNoneMatch(List tags) { - this.noneMatch = tags; - } - - /** - * Sets the range condition based on the modification date of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Range" header. - * - * @param rangeDate The date of the range condition. - */ - public void setRangeDate(Date rangeDate) { - this.rangeDate = rangeDate; - } - - /** - * Sets the range condition based on the entity tag of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Range" header. - * - * @param rangeTag The entity tag of the range condition. - */ - public void setRangeTag(Tag rangeTag) { - this.rangeTag = rangeTag; - } - - /** - * Sets the condition based on the modification date of the requested - * variant.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "If-Unmodified-Since" header. - * - * @param date The condition date. - */ - public void setUnmodifiedSince(Date date) { - this.unmodifiedSince = DateUtils.unmodifiable(date); - } - + /** The "if-match" condition. */ + private volatile List match; + + /** The "if-modified-since" condition. */ + private volatile Date modifiedSince; + + /** The "if-none-match" condition. */ + private volatile List noneMatch; + + /** The "if-range" condition as a Date. */ + private volatile Date rangeDate; + + /** The "if-range" condition as an entity tag. */ + private volatile Tag rangeTag; + + /** The "if-unmodified-since" condition */ + private volatile Date unmodifiedSince; + + /** Constructor. */ + public Conditions() {} + + /** + * Returns the modifiable list of tags that must be matched. Creates a new instance if no one + * has been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Match" header. + * + * @return The "if-match" condition. + */ + public List getMatch() { + // Lazy initialization with double-check. + List m = this.match; + if (m == null) { + synchronized (this) { + m = this.match; + if (m == null) { + this.match = m = new ArrayList(); + } + } + } + return m; + } + + /** + * Returns the condition based on the modification date of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Modified-Since" + * header. + * + * @return The condition date. + */ + public Date getModifiedSince() { + return this.modifiedSince; + } + + /** + * Returns the modifiable list of tags that mustn't match. Creates a new instance if no one has + * been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-None-Match" header. + * + * @return The list of tags that mustn't match. + */ + public List getNoneMatch() { + // Lazy initialization with double-check. + List n = this.noneMatch; + if (n == null) { + synchronized (this) { + n = this.noneMatch; + if (n == null) { + this.noneMatch = n = new ArrayList(); + } + } + } + return n; + } + + /** + * Returns the range condition based on the modification date of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Range" header. + * + * @return The range condition date. + */ + public Date getRangeDate() { + return rangeDate; + } + + /** + * Returns the range conditional status of an entity. + * + * @param representationInfo The representation information that will be tested. + * @return the status of the response. + */ + public Status getRangeStatus(RepresentationInfo representationInfo) { + return getRangeStatus( + (representationInfo == null) ? null : representationInfo.getTag(), + (representationInfo == null) ? null : representationInfo.getModificationDate()); + } + + /** + * Returns the range conditional status of an entity. + * + * @param tag The tag of the entity. + * @param modificationDate The modification date of the entity. + * @return The status of the response. + */ + public Status getRangeStatus(Tag tag, Date modificationDate) { + Status result = Status.CLIENT_ERROR_PRECONDITION_FAILED; + if (getRangeTag() != null) { + boolean all = getRangeTag().equals(Tag.ALL); + + // If a tag exists + if (tag != null) { + if (all || getRangeTag().equals(tag)) { + result = Status.SUCCESS_OK; + } + } + } else if (getRangeDate() != null) { + // If a modification date exists + if (getRangeDate().equals(modificationDate)) { + result = Status.SUCCESS_OK; + } + } + + return result; + } + + /** + * Returns the range condition based on the entity tag of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Range" header. + * + * @return The range entity tag. + */ + public Tag getRangeTag() { + return rangeTag; + } + + /** + * Returns the conditional status of a variant using a given method. + * + * @param method The request method. + * @param entityExists Indicates if the entity exists. + * @param tag The tag. + * @param modificationDate The modification date. + * @return Null if the requested method can be performed, the status of the response otherwise. + */ + public Status getStatus(Method method, boolean entityExists, Tag tag, Date modificationDate) { + Status result = null; + + // Is the "if-Match" rule followed or not? + if ((this.match != null) && !this.match.isEmpty()) { + boolean matched = false; + boolean failed = false; + boolean all = (!getMatch().isEmpty()) && getMatch().getFirst().equals(Tag.ALL); + String statusMessage = null; + + if (entityExists) { + // If a tag exists + if (!all && (tag != null)) { + // Check if it matches one of the representations already + // cached by the client + Tag matchTag; + + for (Iterator iter = getMatch().iterator(); !matched && iter.hasNext(); ) { + matchTag = iter.next(); + matched = matchTag.equals(tag, false); + } + } else { + matched = all; + } + } else { + // See + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24 + // If none of the entity tags match, or if "*" is given and no + // current entity exists, the server MUST NOT perform the + // requested method + failed = all; + statusMessage = "A non existing resource can't match any tag."; + } + + failed = failed || !matched; + + if (failed) { + result = Status.CLIENT_ERROR_PRECONDITION_FAILED; + if (statusMessage != null) { + result = new Status(result, statusMessage); + } + } + } + + // Is the "if-None-Match" rule followed or not? + if ((result == null) && (this.noneMatch != null) && !this.noneMatch.isEmpty()) { + boolean matched = false; + + if (entityExists) { + // If a tag exists + if (tag != null) { + // Check if it matches one of the representations + // already cached by the client + Tag noneMatchTag; + + for (Iterator iter = getNoneMatch().iterator(); + !matched && iter.hasNext(); ) { + noneMatchTag = iter.next(); + matched = + noneMatchTag.equals( + tag, + (Method.GET.equals(method) || Method.HEAD.equals(method))); + } + + // The current representation matches one of those already + // cached by the client + if (matched) { + // Check if the current representation has been updated + // since the "if-modified-since" date. In this case, the + // rule is followed. + Date modifiedSince = getModifiedSince(); + boolean isModifiedSince = + (modifiedSince != null) + && (DateUtils.after(new Date(), modifiedSince) + || (modificationDate == null) + || DateUtils.after( + modifiedSince, modificationDate)); + matched = !isModifiedSince; + } + } else { + matched = + (!getNoneMatch().isEmpty()) + && Tag.ALL.equals(getNoneMatch().getFirst()); + } + } + + if (matched) { + if (Method.GET.equals(method) || Method.HEAD.equals(method)) { + result = Status.REDIRECTION_NOT_MODIFIED; + } else { + result = Status.CLIENT_ERROR_PRECONDITION_FAILED; + } + } + } + + // Is the "if-Modified-Since" rule followed or not? + if ((result == null) && (getModifiedSince() != null)) { + Date modifiedSince = getModifiedSince(); + boolean isModifiedSince = + (DateUtils.after(new Date(), modifiedSince) + || (modificationDate == null) + || DateUtils.after(modifiedSince, modificationDate)); + + if (!isModifiedSince) { + if (Method.GET.equals(method) || Method.HEAD.equals(method)) { + result = Status.REDIRECTION_NOT_MODIFIED; + } else { + result = Status.CLIENT_ERROR_PRECONDITION_FAILED; + } + } + } + + // Is the "if-Unmodified-Since" rule followed or not? + if ((result == null) && (getUnmodifiedSince() != null)) { + Date unModifiedSince = getUnmodifiedSince(); + boolean isUnModifiedSince = + ((unModifiedSince == null) + || (modificationDate == null) + || !DateUtils.before(modificationDate, unModifiedSince)); + + if (!isUnModifiedSince) { + result = Status.CLIENT_ERROR_PRECONDITION_FAILED; + } + } + + return result; + } + + /** + * Returns the conditional status of a variant using a given method. + * + * @param method The request method. + * @param representationInfo The representation information that will be tested. + * @return Null if the requested method can be performed, the status of the response otherwise. + */ + public Status getStatus(Method method, RepresentationInfo representationInfo) { + return getStatus( + method, + representationInfo != null, + (representationInfo == null) ? null : representationInfo.getTag(), + (representationInfo == null) ? null : representationInfo.getModificationDate()); + } + + /** + * Returns the condition based on the modification date of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Unmodified-Since" + * header. + * + * @return The condition date. + */ + public Date getUnmodifiedSince() { + return this.unmodifiedSince; + } + + /** + * Indicates if there are some conditions set. + * + * @return True if there are some conditions set. + */ + public boolean hasSome() { + return (((this.match != null) && !this.match.isEmpty()) + || ((this.noneMatch != null) && !this.noneMatch.isEmpty()) + || (getModifiedSince() != null) + || (getUnmodifiedSince() != null)); + } + + /** + * Indicates if there are some range conditions set. + * + * @return True if there are some range conditions set. + */ + public boolean hasSomeRange() { + return getRangeTag() != null || getRangeDate() != null; + } + + /** + * Sets the modifiable list of tags that must be matched.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Match" header. + * + * @param tags The "if-match" condition. + */ + public void setMatch(List tags) { + this.match = tags; + } + + /** + * Sets the condition based on the modification date of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Modified-Since" + * header. + * + * @param date The modification date. + */ + public void setModifiedSince(Date date) { + this.modifiedSince = DateUtils.unmodifiable(date); + } + + /** + * Sets the modifiable list of tags that mustn't match. Creates a new instance if no one has + * been set.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-None-Match" header. + * + * @param tags The list of tags that mustn't match. + */ + public void setNoneMatch(List tags) { + this.noneMatch = tags; + } + + /** + * Sets the range condition based on the modification date of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Range" header. + * + * @param rangeDate The date of the range condition. + */ + public void setRangeDate(Date rangeDate) { + this.rangeDate = rangeDate; + } + + /** + * Sets the range condition based on the entity tag of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Range" header. + * + * @param rangeTag The entity tag of the range condition. + */ + public void setRangeTag(Tag rangeTag) { + this.rangeTag = rangeTag; + } + + /** + * Sets the condition based on the modification date of the requested variant.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "If-Unmodified-Since" + * header. + * + * @param date The condition date. + */ + public void setUnmodifiedSince(Date date) { + this.unmodifiedSince = DateUtils.unmodifiable(date); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Cookie.java b/org.restlet/src/main/java/org/restlet/data/Cookie.java index 4e0e1be791..4037ddd8c0 100644 --- a/org.restlet/src/main/java/org/restlet/data/Cookie.java +++ b/org.restlet/src/main/java/org/restlet/data/Cookie.java @@ -1,213 +1,217 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.Objects; import org.restlet.Request; import org.restlet.engine.util.SystemUtils; import org.restlet.util.NamedValue; -import java.util.Objects; - /** - * Cookie provided by a client. To get the list of all cookies sent by a client, - * you can use the {@link Request#getCookies()} method.
+ * Cookie provided by a client. To get the list of all cookies sent by a client, you can use the + * {@link Request#getCookies()} method.
*
- * Note that if you are on the server side and want to set a cookie on the - * client, you should use the {@link CookieSetting} class instead.
+ * Note that if you are on the server side and want to set a cookie on the client, you should use + * the {@link CookieSetting} class instead.
*
- * Note that when used with HTTP connectors, this class maps to the "Cookie" - * header. - * + * Note that when used with HTTP connectors, this class maps to the "Cookie" header. + * * @see Request#getCookies() * @author Jerome Louvel */ public class Cookie implements NamedValue { - /** The domain name. */ - private volatile String domain; - - /** The name. */ - private volatile String name; - - /** The validity path. */ - private volatile String path; - - /** The value. */ - private volatile String value; - - /** The version number. */ - private volatile int version; - - /** - * Default constructor. - */ - public Cookie() { - this(0, null, null, null, null); - } - - /** - * Constructor. - * - * @param version The version number. - * @param name The name. - * @param value The value. - */ - public Cookie(int version, String name, String value) { - this(version, name, value, null, null); - } - - /** - * Constructor. - * - * @param version The version number. - * @param name The name. - * @param value The value. - * @param path The validity path. - * @param domain The domain name. - */ - public Cookie(int version, String name, String value, String path, String domain) { - this.version = version; - this.name = name; - this.value = value; - this.path = path; - this.domain = domain; - } - - /** - * Constructor. - * - * @param name The name. - * @param value The value. - */ - public Cookie(String name, String value) { - this(0, name, value, null, null); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (!(obj instanceof Cookie that)) { - // if obj isn't a cookie or is null don't evaluate further - return false; - } - - return Objects.equals(getName(), that.getName()) - && Objects.equals(getValue(), that.getValue()) - && (this.version == that.version) - && Objects.equals(getDomain(), that.getDomain()) - && Objects.equals(getPath(), that.getPath()); - } - - /** - * Returns the domain name. - * - * @return The domain name. - */ - public String getDomain() { - return this.domain; - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the validity path. - * - * @return The validity path. - */ - public String getPath() { - return this.path; - } - - /** - * Returns the value. - * - * @return The value. - */ - public String getValue() { - return value; - } - - /** - * Returns the cookie specification version. - * - * @return The cookie specification version. - */ - public int getVersion() { - return this.version; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(getName(), getValue(), getVersion(), getPath(), getDomain()); - } - - /** - * Sets the domain name. - * - * @param domain The domain name. - */ - public void setDomain(String domain) { - this.domain = domain; - } - - /** - * Sets the name. - * - * @param name The name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Sets the validity path. - * - * @param path The validity path. - */ - public void setPath(String path) { - this.path = path; - } - - /** - * Sets the value. - * - * @param value The value. - */ - public void setValue(String value) { - this.value = value; - } - - /** - * Sets the cookie specification version. - * - * @param version The cookie specification version. - */ - public void setVersion(int version) { - this.version = version; - } - - @Override - public String toString() { - return "Cookie [domain=" + domain + ", name=" + name + ", path=" + path + ", value=" + value + ", version=" - + version + "]"; - } + /** The domain name. */ + private volatile String domain; + + /** The name. */ + private volatile String name; + + /** The validity path. */ + private volatile String path; + + /** The value. */ + private volatile String value; + + /** The version number. */ + private volatile int version; + + /** Default constructor. */ + public Cookie() { + this(0, null, null, null, null); + } + + /** + * Constructor. + * + * @param version The version number. + * @param name The name. + * @param value The value. + */ + public Cookie(int version, String name, String value) { + this(version, name, value, null, null); + } + + /** + * Constructor. + * + * @param version The version number. + * @param name The name. + * @param value The value. + * @param path The validity path. + * @param domain The domain name. + */ + public Cookie(int version, String name, String value, String path, String domain) { + this.version = version; + this.name = name; + this.value = value; + this.path = path; + this.domain = domain; + } + + /** + * Constructor. + * + * @param name The name. + * @param value The value. + */ + public Cookie(String name, String value) { + this(0, name, value, null, null); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof Cookie that)) { + // if obj isn't a cookie or is null don't evaluate further + return false; + } + + return Objects.equals(getName(), that.getName()) + && Objects.equals(getValue(), that.getValue()) + && (this.version == that.version) + && Objects.equals(getDomain(), that.getDomain()) + && Objects.equals(getPath(), that.getPath()); + } + + /** + * Returns the domain name. + * + * @return The domain name. + */ + public String getDomain() { + return this.domain; + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the validity path. + * + * @return The validity path. + */ + public String getPath() { + return this.path; + } + + /** + * Returns the value. + * + * @return The value. + */ + public String getValue() { + return value; + } + + /** + * Returns the cookie specification version. + * + * @return The cookie specification version. + */ + public int getVersion() { + return this.version; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode(getName(), getValue(), getVersion(), getPath(), getDomain()); + } + + /** + * Sets the domain name. + * + * @param domain The domain name. + */ + public void setDomain(String domain) { + this.domain = domain; + } + + /** + * Sets the name. + * + * @param name The name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the validity path. + * + * @param path The validity path. + */ + public void setPath(String path) { + this.path = path; + } + + /** + * Sets the value. + * + * @param value The value. + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Sets the cookie specification version. + * + * @param version The cookie specification version. + */ + public void setVersion(int version) { + this.version = version; + } + + @Override + public String toString() { + return "Cookie [domain=" + + domain + + ", name=" + + name + + ", path=" + + path + + ", value=" + + value + + ", version=" + + version + + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java index dacf999fcd..005c751604 100644 --- a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java +++ b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java @@ -1,250 +1,268 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.Objects; import org.restlet.Response; import org.restlet.engine.util.SystemUtils; -import java.util.Objects; - /** - * Cookie setting provided by a server. This allows a server side application to - * add, modify or remove a cookie on the client.
+ * Cookie setting provided by a server. This allows a server side application to add, modify, or + * remove a cookie on the client.
*
- * Note that when used with HTTP connectors, this class maps to the "Set-Cookie" - * and "Set-Cookie2" headers. - * + * Note that when used with HTTP connectors, this class maps to the "Set-Cookie" and "Set-Cookie2" + * headers. + * * @see Response#getCookieSettings() * @author Jerome Louvel */ public final class CookieSetting extends Cookie { - /** - * Indicates whether to restrict cookie access to untrusted parties. Currently, - * this toggles the non-standard but widely supported HttpOnly cookie parameter. - */ - private volatile boolean accessRestricted; - - /** The user's comment. */ - private volatile String comment; - - /** - * The maximum age in seconds. Use 0 to discard an existing cookie. - */ - private volatile int maxAge; - - /** Indicates if cookie should only be transmitted by secure means. */ - private volatile boolean secure; - - /** - * Default constructor. - */ - public CookieSetting() { - this(0, null, null); - } - - /** - * Constructor. - * - * @param version The cookie's version. - * @param name The cookie's name. - * @param value The cookie's value. - */ - public CookieSetting(int version, String name, String value) { - this(version, name, value, null, null); - } - - /** - * Constructor. - * - * @param version The cookie's version. - * @param name The cookie's name. - * @param value The cookie's value. - * @param path The cookie's path. - * @param domain The cookie's domain name. - */ - public CookieSetting(int version, String name, String value, String path, String domain) { - this(version, name, value, path, domain, null, -1, false, false); - } - - /** - * Constructor. - * - * @param version The cookie's version. - * @param name The cookie's name. - * @param value The cookie's value. - * @param path The cookie's path. - * @param domain The cookie's domain name. - * @param comment The cookie's comment. - * @param maxAge Sets the maximum age in seconds.
- * Use 0 to immediately discard an existing cookie.
- * Use -1 to discard the cookie at the end of the session - * (default). - * @param secure Indicates if cookie should only be transmitted by secure - * means. - */ - public CookieSetting(int version, String name, String value, String path, String domain, String comment, int maxAge, - boolean secure) { - this(version, name, value, path, domain, comment, maxAge, secure, false); - } - - /** - * Constructor. - * - * @param version The cookie's version. - * @param name The cookie's name. - * @param value The cookie's value. - * @param path The cookie's path. - * @param domain The cookie's domain name. - * @param comment The cookie's comment. - * @param maxAge Sets the maximum age in seconds.
- * Use 0 to immediately discard an existing cookie.
- * Use -1 to discard the cookie at the end of the - * session (default). - * @param secure Indicates if cookie should only be transmitted by - * secure means. - * @param accessRestricted Indicates whether to restrict cookie access to - * untrusted parties. Currently this toggles the - * non-standard but widely supported HttpOnly cookie - * parameter. - */ - public CookieSetting(int version, String name, String value, String path, String domain, String comment, int maxAge, - boolean secure, boolean accessRestricted) { - super(version, name, value, path, domain); - this.comment = comment; - this.maxAge = maxAge; - this.secure = secure; - this.accessRestricted = accessRestricted; - } - - /** - * Preferred constructor. - * - * @param name The cookie's name. - * @param value The cookie's value. - */ - public CookieSetting(String name, String value) { - this(0, name, value, null, null); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof CookieSetting that)) { - return false; - } - - return super.equals(obj) - && this.maxAge == that.maxAge - && this.secure == that.secure - && Objects.equals(this.comment, that.comment); - } - - /** - * Returns the comment for the user. - * - * @return The comment for the user. - */ - public String getComment() { - return this.comment; - } - - /** - * Returns the description of this REST element. - * - * @return The description of this REST element. - */ - public String getDescription() { - return "Cookie setting"; - } - - /** - * Returns the maximum age in seconds. Use 0 to immediately discard an existing - * cookie. Use -1 to discard the cookie at the end of the session (default). - * - * @return The maximum age in seconds. - */ - public int getMaxAge() { - return this.maxAge; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(super.hashCode(), getComment(), getMaxAge(), isSecure()); - } - - /** - * Indicates if cookie access is restricted for untrusted parties. Currently - * this toggles the non-standard but widely supported HttpOnly cookie parameter. - * - * @return accessRestricted True if cookie access should be restricted - */ - public boolean isAccessRestricted() { - return this.accessRestricted; - } - - /** - * Indicates if cookie should only be transmitted by secure means. - * - * @return True if cookie should only be transmitted by secure means. - */ - public boolean isSecure() { - return this.secure; - } - - /** - * Indicates whether to restrict cookie access to untrusted parties. Currently - * this toggles the non-standard but widely supported HttpOnly cookie parameter. - * - * @param accessRestricted True if cookie access should be restricted - */ - public void setAccessRestricted(boolean accessRestricted) { - this.accessRestricted = accessRestricted; - } - - /** - * Sets the comment for the user. - * - * @param comment The comment for the user. - */ - public void setComment(String comment) { - this.comment = comment; - } - - /** - * Sets the maximum age in seconds. Use 0 to immediately discard an existing - * cookie. Use -1 to discard the cookie at the end of the session (default). - * - * @param maxAge The maximum age in seconds. - */ - public void setMaxAge(int maxAge) { - this.maxAge = maxAge; - } - - /** - * Indicates if cookie should only be transmitted by secure means. - * - * @param secure True if cookie should only be transmitted by secure means. - */ - public void setSecure(boolean secure) { - this.secure = secure; - } - - @Override - public String toString() { - return "CookieSetting [accessRestricted=" + accessRestricted + ", comment=" + comment + ", maxAge=" + maxAge - + ", secure=" + secure + ", domain=" + getDomain() + ", name=" + getName() + ", path=" + getPath() - + ", value=" + getValue() + ", version=" + getVersion() + "]"; - } + /** + * Indicates whether to restrict cookie access to untrusted parties. Currently, this toggles the + * non-standard but widely supported HttpOnly cookie parameter. + */ + private volatile boolean accessRestricted; + + /** The user's comment. */ + private volatile String comment; + + /** The maximum age in seconds. Use 0 to discard an existing cookie. */ + private volatile int maxAge; + + /** Indicates if the cookie should only be transmitted by secure means. */ + private volatile boolean secure; + + /** Default constructor. */ + public CookieSetting() { + this(0, null, null); + } + + /** + * Constructor. + * + * @param version The cookie's version. + * @param name The cookie's name. + * @param value The cookie's value. + */ + public CookieSetting(int version, String name, String value) { + this(version, name, value, null, null); + } + + /** + * Constructor. + * + * @param version The cookie's version. + * @param name The cookie's name. + * @param value The cookie's value. + * @param path The cookie's path. + * @param domain The cookie's domain name. + */ + public CookieSetting(int version, String name, String value, String path, String domain) { + this(version, name, value, path, domain, null, -1, false, false); + } + + /** + * Constructor. + * + * @param version The cookie's version. + * @param name The cookie's name. + * @param value The cookie's value. + * @param path The cookie's path. + * @param domain The cookie's domain name. + * @param comment The cookie's comment. + * @param maxAge Sets the maximum age in seconds.
+ * Use 0 to immediately discard an existing cookie.
+ * Use -1 to discard the cookie at the end of the session (default). + * @param secure Indicates if the cookie should only be transmitted by secure means. + */ + public CookieSetting( + int version, + String name, + String value, + String path, + String domain, + String comment, + int maxAge, + boolean secure) { + this(version, name, value, path, domain, comment, maxAge, secure, false); + } + + /** + * Constructor. + * + * @param version The cookie's version. + * @param name The cookie's name. + * @param value The cookie's value. + * @param path The cookie's path. + * @param domain The cookie's domain name. + * @param comment The cookie's comment. + * @param maxAge Sets the maximum age in seconds.
+ * Use 0 to immediately discard an existing cookie.
+ * Use -1 to discard the cookie at the end of the session (default). + * @param secure Indicates if the cookie should only be transmitted by secure means. + * @param accessRestricted Indicates whether to restrict cookie access to untrusted parties. + * Currently, this toggles the non-standard but widely supported HttpOnly cookie parameter. + */ + public CookieSetting( + int version, + String name, + String value, + String path, + String domain, + String comment, + int maxAge, + boolean secure, + boolean accessRestricted) { + super(version, name, value, path, domain); + this.comment = comment; + this.maxAge = maxAge; + this.secure = secure; + this.accessRestricted = accessRestricted; + } + + /** + * Preferred constructor. + * + * @param name The cookie's name. + * @param value The cookie's value. + */ + public CookieSetting(String name, String value) { + this(0, name, value, null, null); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof CookieSetting that)) { + return false; + } + + return super.equals(obj) + && this.maxAge == that.maxAge + && this.secure == that.secure + && Objects.equals(this.comment, that.comment); + } + + /** + * Returns the comment for the user. + * + * @return The comment for the user. + */ + public String getComment() { + return this.comment; + } + + /** + * Returns the description of this REST element. + * + * @return The description of this REST element. + */ + public String getDescription() { + return "Cookie setting"; + } + + /** + * Returns the maximum age in seconds. Use 0 to immediately discard an existing cookie. Use -1 + * to discard the cookie at the end of the session (default). + * + * @return The maximum age in seconds. + */ + public int getMaxAge() { + return this.maxAge; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode(super.hashCode(), getComment(), getMaxAge(), isSecure()); + } + + /** + * Indicates if cookie access is restricted for untrusted parties. Currently this toggles the + * non-standard but widely supported HttpOnly cookie parameter. + * + * @return accessRestricted True if cookie access should be restricted + */ + public boolean isAccessRestricted() { + return this.accessRestricted; + } + + /** + * Indicates if the cookie should only be transmitted by secure means. + * + * @return True if the cookie should only be transmitted by secure means. + */ + public boolean isSecure() { + return this.secure; + } + + /** + * Indicates whether to restrict cookie access to untrusted parties. Currently, this toggles the + * non-standard but widely supported HttpOnly cookie parameter. + * + * @param accessRestricted True if cookie access should be restricted + */ + public void setAccessRestricted(boolean accessRestricted) { + this.accessRestricted = accessRestricted; + } + + /** + * Sets the comment for the user. + * + * @param comment The comment for the user. + */ + public void setComment(String comment) { + this.comment = comment; + } + + /** + * Sets the maximum age in seconds. Use 0 to immediately discard an existing cookie. Use -1 to + * discard the cookie at the end of the session (default). + * + * @param maxAge The maximum age in seconds. + */ + public void setMaxAge(int maxAge) { + this.maxAge = maxAge; + } + + /** + * Indicates if the cookie should only be transmitted by secure means. + * + * @param secure True if the cookie should only be transmitted by secure means. + */ + public void setSecure(boolean secure) { + this.secure = secure; + } + @Override + public String toString() { + return "CookieSetting [accessRestricted=" + + accessRestricted + + ", comment=" + + comment + + ", maxAge=" + + maxAge + + ", secure=" + + secure + + ", domain=" + + getDomain() + + ", name=" + + getName() + + ", path=" + + getPath() + + ", value=" + + getValue() + + ", version=" + + getVersion() + + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Digest.java b/org.restlet/src/main/java/org/restlet/data/Digest.java index bef8d0a5c9..5ce6fd62f3 100644 --- a/org.restlet/src/main/java/org/restlet/data/Digest.java +++ b/org.restlet/src/main/java/org/restlet/data/Digest.java @@ -1,126 +1,123 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.Arrays; import org.restlet.engine.util.SystemUtils; import org.restlet.representation.Representation; -import java.util.Arrays; - /** - * Describes a digest value and the digest algorithm used. Digests can have - * several use cases such as ensuring the integrity of representations exchanges - * between resources, or for authentication purpose. - * + * Describes a digest value and the digest algorithm used. Digests can have several use cases, such + * as ensuring the integrity of representations exchanges between resources, or for authentication + * purposes. + * * @see Representation#getDigest() * @author Jerome Louvel */ public class Digest { - /** Digest algorithm defined in RFC 1319. */ - public static final String ALGORITHM_MD2 = "MD2"; + /** Digest algorithm defined in RFC 1319. */ + public static final String ALGORITHM_MD2 = "MD2"; - /** Digest algorithm defined in RFC 1321. */ - public static final String ALGORITHM_MD5 = "MD5"; + /** Digest algorithm defined in RFC 1321. */ + public static final String ALGORITHM_MD5 = "MD5"; - /** No digest algorithm defined. */ - public static final String ALGORITHM_NONE = "NONE"; + /** No digest algorithm defined. */ + public static final String ALGORITHM_NONE = "NONE"; - /** Digest algorithm defined in Secure Hash Standard, NIST FIPS 180-1. */ - public static final String ALGORITHM_SHA_1 = "SHA-1"; + /** Digest algorithm defined in Secure Hash Standard, NIST FIPS 180-1. */ + public static final String ALGORITHM_SHA_1 = "SHA-1"; - /** NIST approved digest algorithm from SHA-2 family. */ - public static final String ALGORITHM_SHA_256 = "SHA-256"; + /** NIST approved digest algorithm from the SHA-2 family. */ + public static final String ALGORITHM_SHA_256 = "SHA-256"; - /** NIST approved digest algorithm from SHA-2 family. */ - public static final String ALGORITHM_SHA_384 = "SHA-384"; + /** NIST approved digest algorithm from the SHA-2 family. */ + public static final String ALGORITHM_SHA_384 = "SHA-384"; - /** NIST approved digest algorithm from SHA-2 family. */ - public static final String ALGORITHM_SHA_512 = "SHA-512"; + /** NIST approved digest algorithm from the SHA-2 family. */ + public static final String ALGORITHM_SHA_512 = "SHA-512"; - /** - * Digest algorithm for the HTTP DIGEST scheme. This is exactly the A1 value - * specified in RFC2617 which is a MD5 hash of the user name, realm and - * password, separated by a colon character. - */ - public static final String ALGORITHM_HTTP_DIGEST = "HTTP-DIGEST-A1"; + /** + * Digest algorithm for the HTTP DIGEST scheme. This is exactly the A1 value specified in + * RFC2617, which is a MD5 hash of the username, realm, and password, separated by a colon + * character. + */ + public static final String ALGORITHM_HTTP_DIGEST = "HTTP-DIGEST-A1"; - /** The digest algorithm. */ - private final String algorithm; + /** The digest algorithm. */ + private final String algorithm; - /** The digest value. */ - private final byte[] value; + /** The digest value. */ + private final byte[] value; - /** - * Constructor using the MD5 algorithm by default. - * - * @param value The digest value. - */ - public Digest(byte[] value) { - this(ALGORITHM_MD5, value); - } + /** + * Constructor using the MD5 algorithm by default. + * + * @param value The digest value. + */ + public Digest(byte[] value) { + this(ALGORITHM_MD5, value); + } - /** - * Constructor. - * - * @param algorithm The digest algorithm. - * @param value The digest value. - */ - public Digest(String algorithm, byte[] value) { - this.algorithm = algorithm; + /** + * Constructor. + * + * @param algorithm The digest algorithm. + * @param value The digest value. + */ + public Digest(String algorithm, byte[] value) { + this.algorithm = algorithm; - // In Java 6, use Arrays.copyOf. - this.value = new byte[value.length]; + // In Java 6, use Arrays.copyOf. + this.value = new byte[value.length]; System.arraycopy(value, 0, this.value, 0, value.length); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof Digest that) { - if (getAlgorithm().equals(that.getAlgorithm())) { - return Arrays.equals(getValue(), that.getValue()); - } - } - return false; - } - - /** - * Returns the digest algorithm. - * - * @return The digest algorithm. - */ - public String getAlgorithm() { - return algorithm; - } - - /** - * Returns the digest value. - * - * @return The digest value. - */ - public byte[] getValue() { - // In Java 6, use Arrays.copyOf. - byte[] result = new byte[this.value.length]; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Digest that) { + if (getAlgorithm().equals(that.getAlgorithm())) { + return Arrays.equals(getValue(), that.getValue()); + } + } + return false; + } + + /** + * Returns the digest algorithm. + * + * @return The digest algorithm. + */ + public String getAlgorithm() { + return algorithm; + } + + /** + * Returns the digest value. + * + * @return The digest value. + */ + public byte[] getValue() { + // In Java 6, use Arrays.copyOf. + byte[] result = new byte[this.value.length]; System.arraycopy(this.value, 0, result, 0, this.value.length); - return result; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(algorithm, value); - } + return result; + } - @Override - public String toString() { - return "Digest [algorithm=" + algorithm + ", value=" + Arrays.toString(value) + "]"; - } + @Override + public int hashCode() { + return SystemUtils.hashCode(algorithm, value); + } + @Override + public String toString() { + return "Digest [algorithm=" + algorithm + ", value=" + Arrays.toString(value) + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Dimension.java b/org.restlet/src/main/java/org/restlet/data/Dimension.java index bd298ff14a..82ee020ca7 100644 --- a/org.restlet/src/main/java/org/restlet/data/Dimension.java +++ b/org.restlet/src/main/java/org/restlet/data/Dimension.java @@ -1,25 +1,32 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import org.restlet.Response; /** - * Dimension on which the representations of a resource may vary. Note that when - * used with HTTP connectors, this class maps to the "Vary" header. - * + * Dimension on which the representations of a resource may vary. Note that when used with HTTP + * connectors, this class maps to the "Vary" header. + * * @see Response#getDimensions() * @author Jerome Louvel * @author Piyush Purang (ppurang@gmail.com) */ public enum Dimension { - AUTHORIZATION, CHARACTER_SET, CLIENT_ADDRESS, CLIENT_AGENT, UNSPECIFIED, ENCODING, LANGUAGE, MEDIA_TYPE, TIME, - ORIGIN + AUTHORIZATION, + CHARACTER_SET, + CLIENT_ADDRESS, + CLIENT_AGENT, + UNSPECIFIED, + ENCODING, + LANGUAGE, + MEDIA_TYPE, + TIME, + ORIGIN } diff --git a/org.restlet/src/main/java/org/restlet/data/Disposition.java b/org.restlet/src/main/java/org/restlet/data/Disposition.java index 97254c4e13..401442fa07 100644 --- a/org.restlet/src/main/java/org/restlet/data/Disposition.java +++ b/org.restlet/src/main/java/org/restlet/data/Disposition.java @@ -1,209 +1,200 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.Date; import org.restlet.engine.util.DateUtils; import org.restlet.util.Series; -import java.util.Date; - /** - * Describes the presentation of a single entity especially in the case of - * multipart documents. This is an equivalent of the HTTP "Content-Disposition" - * header. - * - * @see Content-Disposition header - * @see The - * Content-Disposition Header Field - * + * Describes the presentation of a single entity, especially in the case of multipart documents. + * This is an equivalent of the HTTP "Content-Disposition" header. + * + * @see Content-Disposition header + * @see The Content-Disposition Header + * Field * @author Thierry Boileau */ public class Disposition { - /** The creation date parameter name as presented by the RFC 2183. */ - public static final String NAME_CREATION_DATE = "creation-date"; - - /** The filename parameter name as presented by the RFC 2183. */ - public static final String NAME_FILENAME = "filename"; - - /** The modification date parameter name as presented by the RFCc 2183. */ - public static final String NAME_MODIFICATION_DATE = "modification-date"; - - /** The read date parameter name as presented by the RFC 2183. */ - public static final String NAME_READ_DATE = "read-date"; - - /** The size parameter name as presented by the RFC 2183. */ - public static final String NAME_SIZE = "size"; - - /** - * Indicates that the part is intended to be separated from the full message. - */ - public static final String TYPE_ATTACHMENT = "attachment"; - - /** - * Indicates that the part is intended to be displayed automatically upon - * display of the full message. - */ - public static final String TYPE_INLINE = "inline"; - - /** Indicates that the part is not intended to be displayed. */ - public static final String TYPE_NONE = "none"; - - /** The list of disposition parameters. */ - private Series parameters; - - /** The disposition type. */ - private String type; - - /** - * Constructor. Instantiated with the TYPE_NONE type. - */ - public Disposition() { - this(Disposition.TYPE_NONE); - } - - /** - * Constructor. - * - * @param type The disposition type. - */ - public Disposition(String type) { - super(); - this.type = type; - } - - /** - * Constructor. - * - * @param type The disposition type. - * @param parameters The list of disposition parameters. - */ - public Disposition(String type, Series parameters) { - this(type); - this.parameters = parameters; - } - - /** - * Adds a Date parameter. - * - * @param name The name of the parameter. - * @param value Its value as a date. - */ - public void addDate(String name, Date value) { - getParameters().add(name, DateUtils.format(value, DateUtils.FORMAT_RFC_822.get(0))); - } - - /** - * Returns the value of the "filename" parameter. - * - * @return The value of the "filename" parameter. - */ - public String getFilename() { - return getParameters().getFirstValue(NAME_FILENAME, true); - } - - /** - * Returns the list of disposition parameters. - * - * @return The list of disposition parameters. - */ - public Series getParameters() { - if (this.parameters == null) { - this.parameters = new Series(Parameter.class); - } - - return this.parameters; - } - - /** - * Returns the disposition type. - * - * @return The disposition type. - */ - public String getType() { - return type; - } - - /** - * Sets the creation date parameter. - * - * @param value The creation date. - */ - public void setCreationDate(Date value) { - setDate(NAME_CREATION_DATE, value); - } - - /** - * Sets a Date parameter. - * - * @param name The name of the parameter. - * @param value Its value as a date. - */ - public void setDate(String name, Date value) { - getParameters().set(name, DateUtils.format(value, DateUtils.FORMAT_RFC_822.get(0)), true); - } - - /** - * Sets the value of the "filename" parameter. - * - * @param fileName The file name value. - */ - public void setFilename(String fileName) { - getParameters().set(Disposition.NAME_FILENAME, fileName, true); - } - - /** - * Sets the modification date parameter. - * - * @param value The modification date. - */ - public void setModificationDate(Date value) { - setDate(NAME_MODIFICATION_DATE, value); - } - - /** - * Sets the list of disposition parameters. - * - * @param parameters The list of disposition parameters. - */ - public void setParameters(Series parameters) { - this.parameters = parameters; - } - - /** - * Sets the read date parameter. - * - * @param value The read date. - */ - public void setReadDate(Date value) { - setDate(NAME_READ_DATE, value); - } - - /** - * Sets the value of the "size" parameter. - * - * @param size The size. - */ - public void setSize(long size) { - getParameters().set(Disposition.NAME_SIZE, Long.toString(size), true); - } - - /** - * Sets the disposition type. - * - * @param type The disposition type. - */ - public void setType(String type) { - this.type = type; - } - + /** The creation date parameter name as presented by the RFC 2183. */ + public static final String NAME_CREATION_DATE = "creation-date"; + + /** The filename parameter name as presented by the RFC 2183. */ + public static final String NAME_FILENAME = "filename"; + + /** The modification date parameter name as presented by the RFCc 2183. */ + public static final String NAME_MODIFICATION_DATE = "modification-date"; + + /** The read date parameter name as presented by the RFC 2183. */ + public static final String NAME_READ_DATE = "read-date"; + + /** The size parameter name as presented by the RFC 2183. */ + public static final String NAME_SIZE = "size"; + + /** Indicates that the part is intended to be separated from the full message. */ + public static final String TYPE_ATTACHMENT = "attachment"; + + /** + * Indicates that the part is intended to be displayed automatically upon display of the full + * message. + */ + public static final String TYPE_INLINE = "inline"; + + /** Indicates that the part is not intended to be displayed. */ + public static final String TYPE_NONE = "none"; + + /** The list of disposition parameters. */ + private Series parameters; + + /** The disposition type. */ + private String type; + + /** Constructor. Instantiated with the TYPE_NONE type. */ + public Disposition() { + this(Disposition.TYPE_NONE); + } + + /** + * Constructor. + * + * @param type The disposition type. + */ + public Disposition(String type) { + super(); + this.type = type; + } + + /** + * Constructor. + * + * @param type The disposition type. + * @param parameters The list of disposition parameters. + */ + public Disposition(String type, Series parameters) { + this(type); + this.parameters = parameters; + } + + /** + * Adds a Date parameter. + * + * @param name The name of the parameter. + * @param value Its value as a date. + */ + public void addDate(String name, Date value) { + getParameters().add(name, DateUtils.format(value, DateUtils.FORMAT_RFC_822.getFirst())); + } + + /** + * Returns the value of the "filename" parameter. + * + * @return The value of the "filename" parameter. + */ + public String getFilename() { + return getParameters().getFirstValue(NAME_FILENAME, true); + } + + /** + * Returns the list of disposition parameters. + * + * @return The list of disposition parameters. + */ + public Series getParameters() { + if (this.parameters == null) { + this.parameters = new Series(Parameter.class); + } + + return this.parameters; + } + + /** + * Returns the disposition type. + * + * @return The disposition type. + */ + public String getType() { + return type; + } + + /** + * Sets the creation date parameter. + * + * @param value The creation date. + */ + public void setCreationDate(Date value) { + setDate(NAME_CREATION_DATE, value); + } + + /** + * Sets a Date parameter. + * + * @param name The name of the parameter. + * @param value Its value as a date. + */ + public void setDate(String name, Date value) { + getParameters() + .set(name, DateUtils.format(value, DateUtils.FORMAT_RFC_822.getFirst()), true); + } + + /** + * Sets the value of the "filename" parameter. + * + * @param fileName The file name value. + */ + public void setFilename(String fileName) { + getParameters().set(Disposition.NAME_FILENAME, fileName, true); + } + + /** + * Sets the modification date parameter. + * + * @param value The modification date. + */ + public void setModificationDate(Date value) { + setDate(NAME_MODIFICATION_DATE, value); + } + + /** + * Sets the list of disposition parameters. + * + * @param parameters The list of disposition parameters. + */ + public void setParameters(Series parameters) { + this.parameters = parameters; + } + + /** + * Sets the read date parameter. + * + * @param value The read date. + */ + public void setReadDate(Date value) { + setDate(NAME_READ_DATE, value); + } + + /** + * Sets the value of the "size" parameter. + * + * @param size The size. + */ + public void setSize(long size) { + getParameters().set(Disposition.NAME_SIZE, Long.toString(size), true); + } + + /** + * Sets the disposition type. + * + * @param type The disposition type. + */ + public void setType(String type) { + this.type = type; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Encoding.java b/org.restlet/src/main/java/org/restlet/data/Encoding.java index a622569f39..c90d098bd9 100644 --- a/org.restlet/src/main/java/org/restlet/data/Encoding.java +++ b/org.restlet/src/main/java/org/restlet/data/Encoding.java @@ -1,139 +1,146 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; /** - * Modifier of a representation's media type. Useful to apply compression - * without losing the identity of the underlying media type. - * + * Modifier of a representation's media-type. Useful to apply compression without losing the + * identity of the underlying media type. + * * @author Jerome Louvel */ public final class Encoding extends Metadata { - /** All encodings acceptable. */ - public static final Encoding ALL = new Encoding("*", "All encodings"); - - /** The common Unix file compression. */ - public static final Encoding COMPRESS = new Encoding("compress", "Common Unix compression"); - - /** The zlib format defined by RFC 1950 and 1951. */ - public static final Encoding DEFLATE = new Encoding("deflate", "Deflate compression using the zlib format"); - - /** The zlib format defined by RFC 1950 and 1951, without wrapping. */ - public static final Encoding DEFLATE_NOWRAP = new Encoding("deflate-no-wrap", - "Deflate compression using the zlib format (without wrapping)"); - - /** The FreeMarker encoding. */ - public static final Encoding FREEMARKER = new Encoding("freemarker", "FreeMarker templated representation"); - - /** The GNU Zip encoding. */ - public static final Encoding GZIP = new Encoding("gzip", "GZip compression"); - - /** The default (identity) encoding. */ - public static final Encoding IDENTITY = new Encoding("identity", "The default encoding with no transformation"); - - /** The Velocity encoding. */ - public static final Encoding VELOCITY = new Encoding("velocity", "Velocity templated representation"); - - /** The Info-Zip encoding. */ - public static final Encoding ZIP = new Encoding("zip", "Zip compression"); - - /** - * Returns the encoding associated to a name. If an existing constant exists - * then it is returned, otherwise a new instance is created. - * - * @param name The name. - * @return The associated encoding. - */ - public static Encoding valueOf(final String name) { - Encoding result = null; - - if ((name != null) && !name.isEmpty()) { - if (name.equalsIgnoreCase(ALL.getName())) { - result = ALL; - } else if (name.equalsIgnoreCase(GZIP.getName())) { - result = GZIP; - } else if (name.equalsIgnoreCase(ZIP.getName())) { - result = ZIP; - } else if (name.equalsIgnoreCase(COMPRESS.getName())) { - result = COMPRESS; - } else if (name.equalsIgnoreCase(DEFLATE.getName())) { - result = DEFLATE; - } else if (name.equalsIgnoreCase(DEFLATE_NOWRAP.getName())) { - result = DEFLATE_NOWRAP; - } else if (name.equalsIgnoreCase(IDENTITY.getName())) { - result = IDENTITY; - } else if (name.equalsIgnoreCase(FREEMARKER.getName())) { - result = FREEMARKER; - } else if (name.equalsIgnoreCase(VELOCITY.getName())) { - result = VELOCITY; - } else { - result = new Encoding(name); - } - } - - return result; - } - - /** - * Constructor. - * - * @param name The name. - */ - public Encoding(final String name) { - this(name, "Encoding applied to a representation"); - } - - /** - * Constructor. - * - * @param name The name. - * @param description The description. - */ - public Encoding(final String name, final String description) { - super(name, description); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(final Object object) { - return (object instanceof Encoding) && getName().equalsIgnoreCase(((Encoding) object).getName()); - } - - @Override - public Metadata getParent() { - return equals(ALL) ? null : ALL; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); - } - - /** - * Indicates if a given encoding is included in the current one. The test is - * true if both encodings are equal or if the given encoding is within the range - * of the current one. For example, ALL includes all encodings. A null encoding - * is considered as included into the current one. - *

- * Examples: - *

    - *
  • ALL.includes(COMPRESS) returns true
  • - *
  • COMPRESS.includes(ALL) returns false
  • - *
- * - * @param included The encoding to test for inclusion. - * @return True if the given encoding is included in the current one. - * @see #isCompatible(Metadata) - */ - public boolean includes(Metadata included) { - return equals(ALL) || (included == null) || equals(included); - } + /** All encodings acceptable. */ + public static final Encoding ALL = new Encoding("*", "All encodings"); + + /** The common Unix file compression. */ + public static final Encoding COMPRESS = new Encoding("compress", "Common Unix compression"); + + /** The zlib format defined by RFC 1950 and 1951. */ + public static final Encoding DEFLATE = + new Encoding("deflate", "Deflate compression using the zlib format"); + + /** The zlib format defined by RFC 1950 and 1951, without wrapping. */ + public static final Encoding DEFLATE_NOWRAP = + new Encoding( + "deflate-no-wrap", + "Deflate compression using the zlib format (without wrapping)"); + + /** The FreeMarker encoding. */ + public static final Encoding FREEMARKER = + new Encoding("freemarker", "FreeMarker templated representation"); + + /** The GNU Zip encoding. */ + public static final Encoding GZIP = new Encoding("gzip", "GZip compression"); + + /** The default (identity) encoding. */ + public static final Encoding IDENTITY = + new Encoding("identity", "The default encoding with no transformation"); + + /** The Velocity encoding. */ + public static final Encoding VELOCITY = + new Encoding("velocity", "Velocity templated representation"); + + /** The Info-Zip encoding. */ + public static final Encoding ZIP = new Encoding("zip", "Zip compression"); + + /** + * Returns the encoding associated with a name. If an existing constant exists, then it is + * returned; otherwise a new instance is created. + * + * @param name The name. + * @return The associated encoding. + */ + public static Encoding valueOf(final String name) { + Encoding result = null; + + if ((name != null) && !name.isEmpty()) { + if (name.equalsIgnoreCase(ALL.getName())) { + result = ALL; + } else if (name.equalsIgnoreCase(GZIP.getName())) { + result = GZIP; + } else if (name.equalsIgnoreCase(ZIP.getName())) { + result = ZIP; + } else if (name.equalsIgnoreCase(COMPRESS.getName())) { + result = COMPRESS; + } else if (name.equalsIgnoreCase(DEFLATE.getName())) { + result = DEFLATE; + } else if (name.equalsIgnoreCase(DEFLATE_NOWRAP.getName())) { + result = DEFLATE_NOWRAP; + } else if (name.equalsIgnoreCase(IDENTITY.getName())) { + result = IDENTITY; + } else if (name.equalsIgnoreCase(FREEMARKER.getName())) { + result = FREEMARKER; + } else if (name.equalsIgnoreCase(VELOCITY.getName())) { + result = VELOCITY; + } else { + result = new Encoding(name); + } + } + + return result; + } + + /** + * Constructor. + * + * @param name The name. + */ + public Encoding(final String name) { + this(name, "Encoding applied to a representation"); + } + + /** + * Constructor. + * + * @param name The name. + * @param description The description. + */ + public Encoding(final String name, final String description) { + super(name, description); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object object) { + return (object instanceof Encoding) + && getName().equalsIgnoreCase(((Encoding) object).getName()); + } + + @Override + public Metadata getParent() { + return equals(ALL) ? null : ALL; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); + } + + /** + * Indicates if a given encoding is included in the current one. The test is true if both + * encodings are equal or if the given encoding is within the range of the current one. For + * example, ALL includes all encodings. A null encoding is considered as included in the current + * one. + * + *

Examples: + * + *

    + *
  • ALL.includes(COMPRESS) returns true + *
  • COMPRESS.includes(ALL) returns false + *
+ * + * @param included The encoding to test for inclusion. + * @return True if the given encoding is included in the current one. + * @see #isCompatible(Metadata) + */ + public boolean includes(Metadata included) { + return equals(ALL) || (included == null) || equals(included); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Expectation.java b/org.restlet/src/main/java/org/restlet/data/Expectation.java index 812dc51c3e..a80195bf1b 100644 --- a/org.restlet/src/main/java/org/restlet/data/Expectation.java +++ b/org.restlet/src/main/java/org/restlet/data/Expectation.java @@ -1,167 +1,168 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.engine.header.HeaderConstants; -import org.restlet.engine.util.SystemUtils; -import org.restlet.util.NamedValue; - import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.engine.header.HeaderConstants; +import org.restlet.engine.util.SystemUtils; +import org.restlet.util.NamedValue; /** - * Particular server behavior that is required by a client. Note that when used - * with HTTP connectors, this class maps to the "Expect" header. - * + * Particular server behavior that is required by a client. Note that when used with HTTP + * connectors, this class maps to the "Expect" header. + * * @author Jerome Louvel */ public final class Expectation implements NamedValue { - /** - * Creates a "100-continue" expectation. If a client waits for a 100 - * (Continue) provisional response before sending the request body, it MUST send - * this expectation. A client MUST NOT send this expectation if it does not - * intend to send a request entity. - * - * @return A new "100-continue" expectation. - * @see HTTP - * 1.1 - Expect header - */ - public static Expectation continueResponse() { - return new Expectation(HeaderConstants.EXPECT_CONTINUE); - } - - /** The name. */ - private volatile String name; - - /** The list of parameters. */ - private volatile List parameters; - - /** The value. */ - private volatile String value; - - /** - * Constructor for directives with no value. - * - * @param name The directive name. - */ - public Expectation(String name) { - this(name, null); - } - - /** - * Constructor for directives with a value. - * - * @param name The directive name. - * @param value The directive value. - */ - public Expectation(String name, String value) { - this.name = name; - this.value = value; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (!(obj instanceof Expectation that)) { - return false; - } - - return Objects.equals(getName(), that.getName()) - && Objects.equals(getValue(), that.getValue()) - && getParameters().equals(that.getParameters()); - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the list of parameters. - * - * @return The list of parameters. - */ - public List getParameters() { - // Lazy initialization with double-check. - List r = this.parameters; - if (r == null) { - synchronized (this) { - r = this.parameters; - if (r == null) { - this.parameters = r = new CopyOnWriteArrayList(); - } - } - } - return r; - } - - /** - * Returns the value. - * - * @return The value. - */ - public String getValue() { - return value; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(getName(), getValue(), getParameters()); - } - - /** - * Sets the name. - * - * @param name The name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Sets the list of parameters. - * - * @param parameters The list of parameters. - */ - public void setParameters(List parameters) { - synchronized (this) { - List r = getParameters(); - r.clear(); - r.addAll(parameters); - } - } - - /** - * Sets the value. - * - * @param value The value. - */ - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "Expectation [name=" + name + ", parameters=" + parameters + ", value=" + value + "]"; - } - + /** + * Creates a "100-continue" expectation. If a client waits for a 100 (Continue) provisional + * response before sending the request body, it MUST send this expectation. A client MUST NOT + * send this expectation if it does not intend to send a request entity. + * + * @return A new "100-continue" expectation. + * @see HTTP 1.1 - + * Expect header + */ + public static Expectation continueResponse() { + return new Expectation(HeaderConstants.EXPECT_CONTINUE); + } + + /** The name. */ + private volatile String name; + + /** The list of parameters. */ + private volatile List parameters; + + /** The value. */ + private volatile String value; + + /** + * Constructor for directives with no value. + * + * @param name The directive name. + */ + public Expectation(String name) { + this(name, null); + } + + /** + * Constructor for directives with a value. + * + * @param name The directive name. + * @param value The directive value. + */ + public Expectation(String name, String value) { + this.name = name; + this.value = value; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (!(obj instanceof Expectation that)) { + return false; + } + + return Objects.equals(getName(), that.getName()) + && Objects.equals(getValue(), that.getValue()) + && getParameters().equals(that.getParameters()); + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the list of parameters. + * + * @return The list of parameters. + */ + public List getParameters() { + // Lazy initialization with double-check. + List r = this.parameters; + if (r == null) { + synchronized (this) { + r = this.parameters; + if (r == null) { + this.parameters = r = new CopyOnWriteArrayList(); + } + } + } + return r; + } + + /** + * Returns the value. + * + * @return The value. + */ + public String getValue() { + return value; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode(getName(), getValue(), getParameters()); + } + + /** + * Sets the name. + * + * @param name The name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the list of parameters. + * + * @param parameters The list of parameters. + */ + public void setParameters(List parameters) { + synchronized (this) { + List r = getParameters(); + r.clear(); + r.addAll(parameters); + } + } + + /** + * Sets the value. + * + * @param value The value. + */ + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "Expectation [name=" + + name + + ", parameters=" + + parameters + + ", value=" + + value + + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Form.java b/org.restlet/src/main/java/org/restlet/data/Form.java index 34ea1aebf5..baf8fcb062 100644 --- a/org.restlet/src/main/java/org/restlet/data/Form.java +++ b/org.restlet/src/main/java/org/restlet/data/Form.java @@ -1,285 +1,272 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.io.IOException; +import java.util.List; import org.restlet.engine.util.FormUtils; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import org.restlet.util.Series; -import java.io.IOException; -import java.util.List; - /** * Form which is a specialized modifiable list of parameters. - * + * * @author Jerome Louvel */ public class Form extends Series { - /** - * Empty constructor. - */ - public Form() { - super(Parameter.class); - } - - /** - * Constructor. - * - * @param initialCapacity The initial list capacity. - */ - public Form(int initialCapacity) { - super(Parameter.class, initialCapacity); - } + /** Empty constructor. */ + public Form() { + super(Parameter.class); + } - /** - * Constructor. - * - * @param delegate The delegate list. - */ - public Form(List delegate) { - super(Parameter.class, delegate); - } + /** + * Constructor. + * + * @param initialCapacity The initial list capacity. + */ + public Form(int initialCapacity) { + super(Parameter.class, initialCapacity); + } - /** - * Constructor. - * - * @param webForm The URL encoded Web form. - */ - public Form(Representation webForm) { - this(webForm, true); - } + /** + * Constructor. + * + * @param delegate The delegate list. + */ + public Form(List delegate) { + super(Parameter.class, delegate); + } - /** - * Constructor. - * - * @param webForm The URL encoded Web form. - */ - public Form(Representation webForm, boolean decode) { - this(); - FormUtils.parse(this, webForm, decode); - } + /** + * Constructor. + * + * @param webForm The URL encoded Web form. + */ + public Form(Representation webForm) { + this(webForm, true); + } - /** - * Constructor. Uses UTF-8 as the character set for encoding non-ASCII - * characters. - * - * @param queryString The Web form parameters as a string. - */ - public Form(String queryString) { - this(queryString, true); - } + /** + * Constructor. + * + * @param webForm The URL encoded Web form. + */ + public Form(Representation webForm, boolean decode) { + this(); + FormUtils.parse(this, webForm, decode); + } - /** - * Constructor. Uses UTF-8 as the character set for encoding non-ASCII - * characters. - * - * @param queryString The Web form parameters as a string. - * @param decode Indicates if the names and values should be automatically - * decoded. - */ - public Form(String queryString, boolean decode) { - this(queryString, CharacterSet.UTF_8, decode); - } + /** + * Constructor. Uses UTF-8 as the character set for encoding non-ASCII characters. + * + * @param queryString The Web form parameters as a string. + */ + public Form(String queryString) { + this(queryString, true); + } - /** - * Constructor. Uses UTF-8 as the character set for encoding non-ASCII - * characters. - * - * @param parametersString The parameters string to parse. - * @param separator The separator character to append between parameters. - */ - public Form(String parametersString, char separator) { - this(parametersString, separator, true); - } + /** + * Constructor. Uses UTF-8 as the character set for encoding non-ASCII characters. + * + * @param queryString The Web form parameters as a string. + * @param decode Indicates if the names and values should be automatically decoded. + */ + public Form(String queryString, boolean decode) { + this(queryString, CharacterSet.UTF_8, decode); + } - /** - * Constructor. Uses UTF-8 as the character set for encoding non-ASCII - * characters. - * - * @param parametersString The parameters string to parse. - * @param separator The separator character to append between parameters. - * @param decode Indicates if the names and values should be - * automatically decoded. - */ - public Form(String parametersString, char separator, boolean decode) { - this(parametersString, CharacterSet.UTF_8, separator, decode); - } + /** + * Constructor. Uses UTF-8 as the character set for encoding non-ASCII characters. + * + * @param parametersString The parameters string to parse. + * @param separator The separator character to append between parameters. + */ + public Form(String parametersString, char separator) { + this(parametersString, separator, true); + } - /** - * Constructor. - * - * @param queryString The Web form parameters as a string. - * @param characterSet The supported character encoding. - */ - public Form(String queryString, CharacterSet characterSet) { - this(queryString, characterSet, true); - } + /** + * Constructor. Uses UTF-8 as the character set for encoding non-ASCII characters. + * + * @param parametersString The parameters string to parse. + * @param separator The separator character to append between parameters. + * @param decode Indicates if the names and values should be automatically decoded. + */ + public Form(String parametersString, char separator, boolean decode) { + this(parametersString, CharacterSet.UTF_8, separator, decode); + } - /** - * Constructor. - * - * @param queryString The Web form parameters as a string. - * @param characterSet The supported character encoding. - * @param decode Indicates if the names and values should be automatically - * decoded. - */ - public Form(String queryString, CharacterSet characterSet, boolean decode) { - this(queryString, characterSet, '&', decode); - } + /** + * Constructor. + * + * @param queryString The Web form parameters as a string. + * @param characterSet The supported character encoding. + */ + public Form(String queryString, CharacterSet characterSet) { + this(queryString, characterSet, true); + } - /** - * Constructor. - * - * @param parametersString The parameters string to parse. - * @param characterSet The supported character encoding. - * @param separator The separator character to append between parameters. - */ - public Form(String parametersString, CharacterSet characterSet, char separator) { - this(parametersString, characterSet, separator, true); - } + /** + * Constructor. + * + * @param queryString The Web form parameters as a string. + * @param characterSet The supported character encoding. + * @param decode Indicates if the names and values should be automatically decoded. + */ + public Form(String queryString, CharacterSet characterSet, boolean decode) { + this(queryString, characterSet, '&', decode); + } - /** - * Constructor. - * - * @param parametersString The parameters string to parse. - * @param characterSet The supported character encoding. - * @param separator The separator character to append between parameters. - * @param decode Indicates if the names and values should be - * automatically decoded. - */ - public Form(String parametersString, CharacterSet characterSet, char separator, boolean decode) { - this(); - FormUtils.parse(this, parametersString, characterSet, decode, separator); - } + /** + * Constructor. + * + * @param parametersString The parameters string to parse. + * @param characterSet The supported character encoding. + * @param separator The separator character to append between parameters. + */ + public Form(String parametersString, CharacterSet characterSet, char separator) { + this(parametersString, characterSet, separator, true); + } - @Override - public Parameter createEntry(String name, String value) { - return new Parameter(name, value); - } + /** + * Constructor. + * + * @param parametersString The parameters string to parse. + * @param characterSet The supported character encoding. + * @param separator The separator character to append between parameters. + * @param decode Indicates if the names and values should be automatically decoded. + */ + public Form( + String parametersString, CharacterSet characterSet, char separator, boolean decode) { + this(); + FormUtils.parse(this, parametersString, characterSet, decode, separator); + } - /** - * Encodes the form using the standard URI encoding mechanism and the UTF-8 - * character set. - * - * @return The encoded form. - * @throws IOException - */ - public String encode() throws IOException { - return encode(CharacterSet.UTF_8); - } + @Override + public Parameter createEntry(String name, String value) { + return new Parameter(name, value); + } - /** - * URL encodes the form. The '&' character is used as a separator. - * - * @param characterSet The supported character encoding. - * @return The encoded form. - * @throws IOException - */ - public String encode(CharacterSet characterSet) throws IOException { - return encode(characterSet, '&'); - } + /** + * Encodes the form using the standard URI encoding mechanism and the UTF-8 character set. + * + * @return The encoded form. + * @throws IOException + */ + public String encode() throws IOException { + return encode(CharacterSet.UTF_8); + } - /** - * URL encodes the form. - * - * @param characterSet The supported character encoding. - * @param separator The separator character to append between parameters. - * @return The encoded form. - * @throws IOException - */ - public String encode(CharacterSet characterSet, char separator) throws IOException { - StringBuilder sb = new StringBuilder(); + /** + * URL encodes the form. The '&' character is used as a separator. + * + * @param characterSet The supported character encoding. + * @return The encoded form. + * @throws IOException + */ + public String encode(CharacterSet characterSet) throws IOException { + return encode(characterSet, '&'); + } - for (int i = 0; i < size(); i++) { - if (i > 0) { - sb.append(separator); - } + /** + * URL encodes the form. + * + * @param characterSet The supported character encoding. + * @param separator The separator character to append between parameters. + * @return The encoded form. + * @throws IOException + */ + public String encode(CharacterSet characterSet, char separator) throws IOException { + StringBuilder sb = new StringBuilder(); - get(i).encode(sb, characterSet); - } + for (int i = 0; i < size(); i++) { + if (i > 0) { + sb.append(separator); + } - return sb.toString(); - } + get(i).encode(sb, characterSet); + } - /** - * Formats the form as a matrix path string. Uses UTF-8 as the character set for - * encoding non-ASCII characters. - * - * @return The form as a matrix string. - * @see Matrix URIs by - * Tim Berners Lee - */ - public String getMatrixString() { - return getMatrixString(CharacterSet.UTF_8); - } + return sb.toString(); + } - /** - * Formats the form as a query string. - * - * @param characterSet The supported character encoding. - * @return The form as a matrix string. - * @see Matrix URIs by - * Tim Berners Lee - */ - public String getMatrixString(CharacterSet characterSet) { - try { - return encode(characterSet, ';'); - } catch (IOException ioe) { - return null; - } - } + /** + * Formats the form as a matrix path string. Uses UTF-8 as the character set for encoding + * non-ASCII characters. + * + * @return The form as a matrix string. + * @see Matrix URIs by Tim + * Berners-Lee + */ + public String getMatrixString() { + return getMatrixString(CharacterSet.UTF_8); + } - /** - * Formats the form as a query string. Uses UTF-8 as the character set for - * encoding non-ASCII characters. - * - * @return The form as a query string. - */ - public String getQueryString() { - return getQueryString(CharacterSet.UTF_8); - } + /** + * Formats the form as a query string. + * + * @param characterSet The supported character encoding. + * @return The form as a matrix string. + * @see Matrix URIs by Tim + * Berners-Lee + */ + public String getMatrixString(CharacterSet characterSet) { + try { + return encode(characterSet, ';'); + } catch (IOException ioe) { + return null; + } + } - /** - * Formats the form as a query string. - * - * @param characterSet The supported character encoding. - * @return The form as a query string. - */ - public String getQueryString(CharacterSet characterSet) { - try { - return encode(characterSet); - } catch (IOException ioe) { - return null; - } - } + /** + * Formats the form as a query string. Uses UTF-8 as the character set for encoding non-ASCII + * characters. + * + * @return The form as a query string. + */ + public String getQueryString() { + return getQueryString(CharacterSet.UTF_8); + } - /** - * Returns the form as a Web representation (MediaType.APPLICATION_WWW_FORM). - * Uses UTF-8 as the character set for encoding non-ASCII characters. - * - * @return The form as a Web representation. - */ - public Representation getWebRepresentation() { - return getWebRepresentation(CharacterSet.UTF_8); - } + /** + * Formats the form as a query string. + * + * @param characterSet The supported character encoding. + * @return The form as a query string. + */ + public String getQueryString(CharacterSet characterSet) { + try { + return encode(characterSet); + } catch (IOException ioe) { + return null; + } + } - /** - * Returns the form as a Web representation (MediaType.APPLICATION_WWW_FORM). - * - * @param characterSet The supported character encoding. - * @return The form as a Web representation. - */ - public Representation getWebRepresentation(CharacterSet characterSet) { - return new StringRepresentation(getQueryString(characterSet), MediaType.APPLICATION_WWW_FORM, null, - characterSet); - } + /** + * Returns the form as a Web representation (MediaType.APPLICATION_WWW_FORM). Uses UTF-8 as the + * character set for encoding non-ASCII characters. + * + * @return The form as a Web representation. + */ + public Representation getWebRepresentation() { + return getWebRepresentation(CharacterSet.UTF_8); + } + /** + * Returns the form as a Web representation (MediaType.APPLICATION_WWW_FORM). + * + * @param characterSet The supported character encoding. + * @return The form as a Web representation. + */ + public Representation getWebRepresentation(CharacterSet characterSet) { + return new StringRepresentation( + getQueryString(characterSet), MediaType.APPLICATION_WWW_FORM, null, characterSet); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Header.java b/org.restlet/src/main/java/org/restlet/data/Header.java index d050d51711..b0fd0934a6 100644 --- a/org.restlet/src/main/java/org/restlet/data/Header.java +++ b/org.restlet/src/main/java/org/restlet/data/Header.java @@ -1,109 +1,103 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.util.Objects; import org.restlet.engine.util.SystemUtils; import org.restlet.util.NamedValue; -import java.util.Objects; - /** * Represents an HTTP header. - * + * * @author Jerome Louvel */ public class Header implements NamedValue { - /** The name. */ - private volatile String name; - - /** The value. */ - private volatile String value; - - /** - * Default constructor. - */ - public Header() { - } - - /** - * Constructor. - * - * @param name The header name. - * @param value The header value. - */ - public Header(String name, String value) { - super(); - this.name = name; - this.value = value; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Header that)) { - return false; - } - - return Objects.equals(getName(), that.getName()) - && Objects.equals(getValue(), that.getValue()); - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the value. - * - * @return The value. - */ - public String getValue() { - return value; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(getName(), getValue()); - } - - /** - * Sets the name. - * - * @param name The name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Sets the value. - * - * @param value The value. - */ - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "[" + getName() + ": " + getValue() + "]"; - } - + /** The name. */ + private volatile String name; + + /** The value. */ + private volatile String value; + + /** Default constructor. */ + public Header() {} + + /** + * Constructor. + * + * @param name The header name. + * @param value The header value. + */ + public Header(String name, String value) { + super(); + this.name = name; + this.value = value; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Header that)) { + return false; + } + + return Objects.equals(getName(), that.getName()) + && Objects.equals(getValue(), that.getValue()); + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the value. + * + * @return The value. + */ + public String getValue() { + return value; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode(getName(), getValue()); + } + + /** + * Sets the name. + * + * @param name The name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the value. + * + * @param value The value. + */ + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "[" + getName() + ": " + getValue() + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Language.java b/org.restlet/src/main/java/org/restlet/data/Language.java index 7c2a4810f4..675c7556d9 100644 --- a/org.restlet/src/main/java/org/restlet/data/Language.java +++ b/org.restlet/src/main/java/org/restlet/data/Language.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import java.util.Collections; @@ -14,192 +13,195 @@ import java.util.concurrent.CopyOnWriteArrayList; /** - * Language used in representations and preferences. A language tag is composed - * of one or more parts: A primary language tag and a possibly empty series of - * sub-tags. When formatted as a string, parts are separated by hyphens. - * + * Language used in representations and preferences. A language tag is composed of one or more + * parts: A primary language tag and a possibly empty series of subtags. When formatted as a string, + * parts are separated by hyphens. + * * @author Jerome Louvel */ public final class Language extends Metadata { - /** All languages acceptable. */ - public static final Language ALL = new Language("*", "All languages"); - - /** - * The default language of the JVM. - * - * @see java.util.Locale#getDefault() - */ - public static final Language DEFAULT = new Language(java.util.Locale.getDefault().getLanguage()); - - /** English language. */ - public static final Language ENGLISH = new Language("en", "English language"); - - /** English language spoken in USA. */ - public static final Language ENGLISH_US = new Language("en-us", "English language in USA"); - - /** French language. */ - public static final Language FRENCH = new Language("fr", "French language"); - - /** French language spoken in France. */ - public static final Language FRENCH_FRANCE = new Language("fr-fr", "French language in France"); - - /** Spanish language. */ - public static final Language SPANISH = new Language("es", "Spanish language"); - - /** - * Returns the language associated to a name. If an existing constant exists - * then it is returned, otherwise a new instance is created. - * - * @param name The name. - * @return The associated language. - */ - public static Language valueOf(final String name) { - Language result = null; - - if ((name != null) && !name.isEmpty()) { - if (name.equalsIgnoreCase(ALL.getName())) { - result = ALL; - } else if (name.equalsIgnoreCase(ENGLISH.getName())) { - result = ENGLISH; - } else if (name.equalsIgnoreCase(ENGLISH_US.getName())) { - result = ENGLISH_US; - } else if (name.equalsIgnoreCase(FRENCH.getName())) { - result = FRENCH; - } else if (name.equalsIgnoreCase(FRENCH_FRANCE.getName())) { - result = FRENCH_FRANCE; - } else if (name.equalsIgnoreCase(SPANISH.getName())) { - result = SPANISH; - } else { - result = new Language(name); - } - } - - return result; - } - - /** The metadata main list of subtags taken from the metadata name. */ - private volatile List subTags; - - /** - * Constructor. - * - * @param name The name. - */ - public Language(final String name) { - this(name, "Language or range of languages"); - } - - /** - * Constructor. - * - * @param name The name. - * @param description The description. - */ - public Language(final String name, final String description) { - super(name, description); - this.subTags = null; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(final Object object) { - return (object instanceof Language) && getName().equalsIgnoreCase(((Language) object).getName()); - } - - @Override - public Language getParent() { - Language result = null; - - if ((getSubTags() != null) && !getSubTags().isEmpty()) { - result = Language.valueOf(getPrimaryTag()); - } else { - result = equals(ALL) ? null : ALL; - } - - return result; - } - - /** - * Returns the primary tag. - * - * @return The primary tag. - */ - public String getPrimaryTag() { - final int separator = getName().indexOf('-'); - - if (separator == -1) { - return getName(); - } - - return getName().substring(0, separator); - } - - /** - * Returns the unmodifiable list of subtags. This list can be empty. - * - * @return The list of subtags for this language Tag. - */ - public List getSubTags() { - // Lazy initialization with double-check. - List v = this.subTags; - if (v == null) { - synchronized (this) { - v = this.subTags; - if (v == null) { - List tokens = new CopyOnWriteArrayList(); - if (getName() != null) { - final String[] tags = getName().split("-"); - if (tags.length > 0) { - for (int i = 1; i < tags.length; i++) { - tokens.add(tags[i]); - } - } - } - - this.subTags = v = Collections.unmodifiableList(tokens); - } - } - } - return v; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); - } - - /** - * Indicates if a given language is included in the current one. The test is - * true if both languages are equal or if the given language is within the range - * of the current one. For example, ALL includes all languages. A null language - * is considered as included into the current one. - *

- * Examples: - *

    - *
  • ENGLISH.includes(ENGLISH_US) returns true
  • - *
  • ENGLISH_US.includes(ENGLISH) returns false
  • - *
- * - * @param included The language to test for inclusion. - * @return True if the language type is included in the current one. - * @see #isCompatible(Metadata) - */ - public boolean includes(Metadata included) { - boolean result = equals(ALL) || (included == null) || equals(included); - - if (!result && (included instanceof Language includedLanguage)) { - - if (getPrimaryTag().equals(includedLanguage.getPrimaryTag())) { - // Both languages are different - if (getSubTags().equals(includedLanguage.getSubTags())) { - result = true; - } else if (getSubTags().isEmpty()) { - result = true; - } - } - } - - return result; - } + /** All languages acceptable. */ + public static final Language ALL = new Language("*", "All languages"); + + /** + * The default language of the JVM. + * + * @see java.util.Locale#getDefault() + */ + public static final Language DEFAULT = + new Language(java.util.Locale.getDefault().getLanguage()); + + /** English language. */ + public static final Language ENGLISH = new Language("en", "English language"); + + /** English language spoken in the USA. */ + public static final Language ENGLISH_US = new Language("en-us", "English language in USA"); + + /** French language. */ + public static final Language FRENCH = new Language("fr", "French language"); + + /** French language spoken in France. */ + public static final Language FRENCH_FRANCE = new Language("fr-fr", "French language in France"); + + /** Spanish language. */ + public static final Language SPANISH = new Language("es", "Spanish language"); + + /** + * Returns the language associated with a name. If an existing constant exists, then it is + * returned; otherwise a new instance is created. + * + * @param name The name. + * @return The associated language. + */ + public static Language valueOf(final String name) { + Language result = null; + + if ((name != null) && !name.isEmpty()) { + if (name.equalsIgnoreCase(ALL.getName())) { + result = ALL; + } else if (name.equalsIgnoreCase(ENGLISH.getName())) { + result = ENGLISH; + } else if (name.equalsIgnoreCase(ENGLISH_US.getName())) { + result = ENGLISH_US; + } else if (name.equalsIgnoreCase(FRENCH.getName())) { + result = FRENCH; + } else if (name.equalsIgnoreCase(FRENCH_FRANCE.getName())) { + result = FRENCH_FRANCE; + } else if (name.equalsIgnoreCase(SPANISH.getName())) { + result = SPANISH; + } else { + result = new Language(name); + } + } + + return result; + } + + /** The metadata main list of subtag taken from the metadata name. */ + private volatile List subTags; + + /** + * Constructor. + * + * @param name The name. + */ + public Language(final String name) { + this(name, "Language or range of languages"); + } + + /** + * Constructor. + * + * @param name The name. + * @param description The description. + */ + public Language(final String name, final String description) { + super(name, description); + this.subTags = null; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object object) { + return (object instanceof Language) + && getName().equalsIgnoreCase(((Language) object).getName()); + } + + @Override + public Language getParent() { + Language result = null; + + if ((getSubTags() != null) && !getSubTags().isEmpty()) { + result = Language.valueOf(getPrimaryTag()); + } else { + result = equals(ALL) ? null : ALL; + } + + return result; + } + + /** + * Returns the primary tag. + * + * @return The primary tag. + */ + public String getPrimaryTag() { + final int separator = getName().indexOf('-'); + + if (separator == -1) { + return getName(); + } + + return getName().substring(0, separator); + } + + /** + * Returns the unmodifiable list of subtag. This list can be empty. + * + * @return The list of subtag for this language Tag. + */ + public List getSubTags() { + // Lazy initialization with double-check. + List v = this.subTags; + if (v == null) { + synchronized (this) { + v = this.subTags; + if (v == null) { + List tokens = new CopyOnWriteArrayList(); + if (getName() != null) { + final String[] tags = getName().split("-"); + if (tags.length > 0) { + for (int i = 1; i < tags.length; i++) { + tokens.add(tags[i]); + } + } + } + + this.subTags = v = Collections.unmodifiableList(tokens); + } + } + } + return v; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); + } + + /** + * Indicates if a given language is included in the current one. The test is true if both + * languages are equal or if the given language is within the range of the current one. For + * example, ALL includes all languages. A null language is considered as included in the current + * one. + * + *

Examples: + * + *

    + *
  • ENGLISH.includes(ENGLISH_US) returns true + *
  • ENGLISH_US.includes(ENGLISH) returns false + *
+ * + * @param included The language to test for inclusion. + * @return True if the language type is included in the current one. + * @see #isCompatible(Metadata) + */ + public boolean includes(Metadata included) { + boolean result = equals(ALL) || (included == null) || equals(included); + + if (!result && (included instanceof Language includedLanguage)) { + + if (getPrimaryTag().equals(includedLanguage.getPrimaryTag())) { + // Both languages are different + if (getSubTags().equals(includedLanguage.getSubTags())) { + result = true; + } else if (getSubTags().isEmpty()) { + result = true; + } + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/LocalReference.java b/org.restlet/src/main/java/org/restlet/data/LocalReference.java index 0f6a74653c..e63b5451c6 100644 --- a/org.restlet/src/main/java/org/restlet/data/LocalReference.java +++ b/org.restlet/src/main/java/org/restlet/data/LocalReference.java @@ -1,187 +1,183 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import java.io.File; /** - * Reference to a local resource. It has helper methods for the three following - * schemes: {@link Protocol#CLAP}, {@link Protocol#FILE}, {@link Protocol#JAR} - * and {@link Protocol#RIAP}. - * + * Reference to a local resource. It has helper methods for the three following schemes: {@link + * Protocol#CLAP}, {@link Protocol#FILE}, {@link Protocol#JAR} and {@link Protocol#RIAP}. + * * @author Jerome Louvel */ public final class LocalReference extends Reference { - /** - * The resources will be resolved from the classloader associated with the local - * class. This is the same as the {@link #CLAP_CLASS} authority. Examples: - * clap:///rootPkg/subPkg/myClass.class or clap:///rootPkg/file.html - * - * @see java.lang.Class#getClassLoader() - */ - public static final int CLAP_DEFAULT = 0; - - /** - * The resources will be resolved from the classloader associated with the local - * class. This is the default CLAP authority. Examples: - * clap://class/rootPkg/subPkg/myClass.class or clap://class/rootPkg/file.html - * or clap:///rootPkg/file.html - * - * @see java.lang.Class#getClassLoader() - */ - public static final int CLAP_CLASS = 1; - - /** - * The resources will be resolved from the system's classloader. Examples: - * clap://system/rootPkg/subPkg/myClass.class or clap://system/rootPkg/file.html - * - * @see java.lang.ClassLoader#getSystemClassLoader() - */ - public static final int CLAP_SYSTEM = 2; - - /** - * The resources will be resolved from the current thread's classloader. - * Examples: clap://thread/rootPkg/subPkg/myClass.class or - * clap://thread/rootPkg/file.html - * - * @see java.lang.Thread#getContextClassLoader() - */ - public static final int CLAP_THREAD = 3; - - /** - * The resources will be resolved from the current application's root Restlet. - * Example riap://application/myPath/myResource - */ - public static final int RIAP_APPLICATION = 4; - - /** - * The resources will be resolved from the current component's internal - * (private) router. Example riap://component/myAppPath/myResource - */ - public static final int RIAP_COMPONENT = 5; - - /** - * The resources will be resolved from the current component's virtual host. - * Example riap://host/myAppPath/myResource - */ - public static final int RIAP_HOST = 6; - - /** - * Constructor. - * - * @param pkg The package to identify. - */ - public static LocalReference createClapReference(Package pkg) { - return createClapReference(CLAP_DEFAULT, pkg); - } - - /** - * Constructor for CLAP URIs to a given package. - * - * @param authorityType The authority type for the resource path. - * @param pkg The package to identify. - */ - public static LocalReference createClapReference(int authorityType, Package pkg) { - String pkgPath = pkg.getName().replaceAll("\\.", "/"); - return new LocalReference("clap://" + getAuthorityName(authorityType) + "/" + pkgPath); - } - - /** - * Constructor. - * - * @param path The resource path. - */ - public static LocalReference createClapReference(String path) { - return createClapReference(CLAP_DEFAULT, path); - } - - /** - * Constructor. - * - * @param authorityType The authority type for the resource path. - * @param path The resource path. - */ - public static LocalReference createClapReference(int authorityType, String path) { - return new LocalReference("clap://" + getAuthorityName(authorityType) + path); - } - - /** - * Constructor. - * - * @param file The file whose path must be used. - * @return The new local reference. - * @see #createFileReference(String) - */ - public static LocalReference createFileReference(File file) { - return createFileReference(file.getAbsolutePath()); - } - - /** - * Constructor. - * - * @param filePath The local file path. - * @see #createFileReference(String, String) - */ - public static LocalReference createFileReference(String filePath) { - return createFileReference("", filePath); - } - - /** - * Constructor. - * - * @param hostName The authority (can be a host name or the special "localhost" - * or an empty value). - * @param filePath The file path. - */ - public static LocalReference createFileReference(String hostName, String filePath) { - return new LocalReference("file://" + hostName + "/" + normalizePath(filePath)); - } - - /** - * Constructor. - * - * @param jarFile The JAR file reference. - * @param entryPath The entry path inside the JAR file. - */ - public static LocalReference createJarReference(Reference jarFile, String entryPath) { - return new LocalReference("jar:" + jarFile.getTargetRef().toString() + "!/" + entryPath); - } - - /** - * Constructor. - * - * @param authorityType The authority type for the resource path. - * @param path The resource path. - */ - public static LocalReference createRiapReference(int authorityType, String path) { - return new LocalReference("riap://" + getAuthorityName(authorityType) + path); - } - - /** - * Constructor. - * - * @param zipFile The Zip file reference. - * @param entryPath The entry path inside the Zip file. - */ - public static LocalReference createZipReference(Reference zipFile, String entryPath) { - return new LocalReference("zip:" + zipFile.getTargetRef().toString() + "!/" + entryPath); - } - - /** - * Returns an authority name. - * - * @param authority The authority. - * @return The name. - */ - public static String getAuthorityName(int authority) { - return switch (authority) { + /** + * The resources will be resolved from the classloader associated with the local class. This is + * the same as the {@link #CLAP_CLASS} authority. Examples: clap:///rootPkg/subPkg/myClass.class + * or clap:///rootPkg/file.html + * + * @see java.lang.Class#getClassLoader() + */ + public static final int CLAP_DEFAULT = 0; + + /** + * The resources will be resolved from the classloader associated with the local class. This is + * the default CLAP authority. Examples: clap://class/rootPkg/subPkg/myClass.class or + * clap://class/rootPkg/file.html or clap:///rootPkg/file.html + * + * @see java.lang.Class#getClassLoader() + */ + public static final int CLAP_CLASS = 1; + + /** + * The resources will be resolved from the system's classloader. Examples: + * clap://system/rootPkg/subPkg/myClass.class or clap://system/rootPkg/file.html + * + * @see java.lang.ClassLoader#getSystemClassLoader() + */ + public static final int CLAP_SYSTEM = 2; + + /** + * The resources will be resolved from the current thread's classloader. Examples: + * clap://thread/rootPkg/subPkg/myClass.class or clap://thread/rootPkg/file.html + * + * @see java.lang.Thread#getContextClassLoader() + */ + public static final int CLAP_THREAD = 3; + + /** + * The resources will be resolved from the current application's root Restlet. Example + * riap://application/myPath/myResource + */ + public static final int RIAP_APPLICATION = 4; + + /** + * The resources will be resolved from the current component's internal (private) router. + * Example riap://component/myAppPath/myResource + */ + public static final int RIAP_COMPONENT = 5; + + /** + * The resources will be resolved from the current component's virtual host. Example + * riap://host/myAppPath/myResource + */ + public static final int RIAP_HOST = 6; + + /** + * Constructor. + * + * @param pkg The package to identify. + */ + public static LocalReference createClapReference(Package pkg) { + return createClapReference(CLAP_DEFAULT, pkg); + } + + /** + * Constructor for CLAP URIs to a given package. + * + * @param authorityType The authority type for the resource path. + * @param pkg The package to identify. + */ + public static LocalReference createClapReference(int authorityType, Package pkg) { + String pkgPath = pkg.getName().replaceAll("\\.", "/"); + return new LocalReference("clap://" + getAuthorityName(authorityType) + "/" + pkgPath); + } + + /** + * Constructor. + * + * @param path The resource path. + */ + public static LocalReference createClapReference(String path) { + return createClapReference(CLAP_DEFAULT, path); + } + + /** + * Constructor. + * + * @param authorityType The authority type for the resource path. + * @param path The resource path. + */ + public static LocalReference createClapReference(int authorityType, String path) { + return new LocalReference("clap://" + getAuthorityName(authorityType) + path); + } + + /** + * Constructor. + * + * @param file The file whose path must be used. + * @return The new local reference. + * @see #createFileReference(String) + */ + public static LocalReference createFileReference(File file) { + return createFileReference(file.getAbsolutePath()); + } + + /** + * Constructor. + * + * @param filePath The local file path. + * @see #createFileReference(String, String) + */ + public static LocalReference createFileReference(String filePath) { + return createFileReference("", filePath); + } + + /** + * Constructor. + * + * @param hostName The authority (can be a host name or the special "localhost" or an empty + * value). + * @param filePath The file path. + */ + public static LocalReference createFileReference(String hostName, String filePath) { + return new LocalReference("file://" + hostName + "/" + normalizePath(filePath)); + } + + /** + * Constructor. + * + * @param jarFile The JAR file reference. + * @param entryPath The entry path inside the JAR file. + */ + public static LocalReference createJarReference(Reference jarFile, String entryPath) { + return new LocalReference("jar:" + jarFile.getTargetRef().toString() + "!/" + entryPath); + } + + /** + * Constructor. + * + * @param authorityType The authority type for the resource path. + * @param path The resource path. + */ + public static LocalReference createRiapReference(int authorityType, String path) { + return new LocalReference("riap://" + getAuthorityName(authorityType) + path); + } + + /** + * Constructor. + * + * @param zipFile The Zip file reference. + * @param entryPath The entry path inside the Zip file. + */ + public static LocalReference createZipReference(Reference zipFile, String entryPath) { + return new LocalReference("zip:" + zipFile.getTargetRef().toString() + "!/" + entryPath); + } + + /** + * Returns an authority name. + * + * @param authority The authority. + * @return The name. + */ + public static String getAuthorityName(int authority) { + return switch (authority) { case CLAP_DEFAULT -> ""; case CLAP_CLASS -> "class"; case CLAP_SYSTEM -> "system"; @@ -191,195 +187,194 @@ public static String getAuthorityName(int authority) { case RIAP_HOST -> "host"; default -> null; }; - } - - /** - * Localize a path by converting all the separator characters to the - * system-dependent separator character. - * - * @param path The path to localize. - * @return The localized path. - */ - public static String localizePath(String path) { - final StringBuilder result = new StringBuilder(); - char nextChar; - for (int i = 0; i < path.length(); i++) { - nextChar = path.charAt(i); - if (nextChar == '/') { - // Convert the URI separator to - // the system dependent path separator - result.append(File.separatorChar); - } else { - result.append(nextChar); - } - } - - return result.toString(); - } - - /** - * Normalize a path by converting all the system-dependent separator characters - * to the standard '/' separator character. - * - * @param path The path to normalize. - * @return The normalize path. - */ - public static String normalizePath(String path) { - final StringBuilder result = new StringBuilder(); - char nextChar; - for (int i = 0; i < path.length(); i++) { - nextChar = path.charAt(i); - if ((nextChar == File.separatorChar)) { - // Convert the Windows style path separator - // to the standard path separator - result.append('/'); - } else if (!isUnreserved(nextChar)) { - result.append(Reference.encode("" + nextChar)); - } else { - result.append(nextChar); - } - } - - return result.toString(); - } - - /** - * Constructor. - * - * @param localRef The local reference. - */ - public LocalReference(Reference localRef) { - super(localRef.getTargetRef().toString()); - } - - /** - * Constructor. - * - * @param localUri The local URI. - */ - public LocalReference(String localUri) { - super(localUri); - } - - /** - * Returns the type of authority. - * - * @return The type of authority. - */ - public int getClapAuthorityType() { - int result = 0; - - if (Protocol.CLAP.equals(getSchemeProtocol())) { - final String authority = getAuthority(); - - if (authority != null) { - if (authority.equalsIgnoreCase(getAuthorityName(CLAP_CLASS))) { - result = CLAP_CLASS; - } else if (authority.equalsIgnoreCase(getAuthorityName(CLAP_SYSTEM))) { - result = CLAP_SYSTEM; - } else if (authority.equalsIgnoreCase(getAuthorityName(CLAP_THREAD))) { - result = CLAP_THREAD; - } else { - result = CLAP_DEFAULT; - } - } - } - - return result; - } - - /** - * Gets the local file corresponding to the reference. Only URIs referring to - * the "localhost" or to an empty authority are supported. - * - * @return The local file corresponding to the reference. - */ - public File getFile() { - File result = null; - - if (Protocol.FILE.equals(getSchemeProtocol())) { - final String hostName = getAuthority(); - - if ((hostName == null) || hostName.isEmpty() || hostName.equalsIgnoreCase("localhost")) { - final String filePath = Reference.decode(getPath()); - result = new File(filePath); - } else { - throw new RuntimeException("Can't resolve files on remote host machines"); - } - } - - return result; - } - - /** - * Returns the JAR entry path. - * - * @return The JAR entry path. - */ - public String getJarEntryPath() { - String result = null; - - if (Protocol.JAR.equals(getSchemeProtocol())) { - final String ssp = getSchemeSpecificPart(); - - if (ssp != null) { - final int separatorIndex = ssp.indexOf("!/"); - - if (separatorIndex != -1) { - result = ssp.substring(separatorIndex + 2); - } - } - } - - return result; - } - - /** - * Returns the JAR file reference. - * - * @return The JAR file reference. - */ - public Reference getJarFileRef() { - Reference result = null; - - if (Protocol.JAR.equals(getSchemeProtocol())) { - final String ssp = getSchemeSpecificPart(); - - if (ssp != null) { - final int separatorIndex = ssp.indexOf("!/"); - - if (separatorIndex != -1) { - result = new Reference(ssp.substring(0, separatorIndex)); - } - } - } - - return result; - } - - /** - * Returns the type of authority. - * - * @return The type of authority. - */ - public int getRiapAuthorityType() { - int result = 0; - - if (Protocol.RIAP.equals(getSchemeProtocol())) { - final String authority = getAuthority(); - - if (authority != null) { - if (authority.equalsIgnoreCase(getAuthorityName(RIAP_APPLICATION))) { - result = RIAP_APPLICATION; - } else if (authority.equalsIgnoreCase(getAuthorityName(RIAP_COMPONENT))) { - result = RIAP_COMPONENT; - } else if (authority.equalsIgnoreCase(getAuthorityName(RIAP_HOST))) { - result = RIAP_HOST; - } - } - } - - return result; - } - + } + + /** + * Localize a path by converting all the separator characters to the system-dependent separator + * character. + * + * @param path The path to localize. + * @return The localized path. + */ + public static String localizePath(String path) { + final StringBuilder result = new StringBuilder(); + char nextChar; + for (int i = 0; i < path.length(); i++) { + nextChar = path.charAt(i); + if (nextChar == '/') { + // Convert the URI separator to + // the system dependent path separator + result.append(File.separatorChar); + } else { + result.append(nextChar); + } + } + + return result.toString(); + } + + /** + * Normalize a path by converting all the system-dependent separator characters to the standard + * '/' separator character. + * + * @param path The path to normalize. + * @return The normalized path. + */ + public static String normalizePath(String path) { + final StringBuilder result = new StringBuilder(); + char nextChar; + for (int i = 0; i < path.length(); i++) { + nextChar = path.charAt(i); + if ((nextChar == File.separatorChar)) { + // Convert the Windows style path separator + // to the standard path separator + result.append('/'); + } else if (!isUnreserved(nextChar)) { + result.append(Reference.encode("" + nextChar)); + } else { + result.append(nextChar); + } + } + + return result.toString(); + } + + /** + * Constructor. + * + * @param localRef The local reference. + */ + public LocalReference(Reference localRef) { + super(localRef.getTargetRef().toString()); + } + + /** + * Constructor. + * + * @param localUri The local URI. + */ + public LocalReference(String localUri) { + super(localUri); + } + + /** + * Returns the type of authority. + * + * @return The type of authority. + */ + public int getClapAuthorityType() { + int result = CLAP_DEFAULT; + + if (Protocol.CLAP.equals(getSchemeProtocol())) { + final String authority = getAuthority(); + + if (authority != null) { + if (authority.equalsIgnoreCase(getAuthorityName(CLAP_CLASS))) { + result = CLAP_CLASS; + } else if (authority.equalsIgnoreCase(getAuthorityName(CLAP_SYSTEM))) { + result = CLAP_SYSTEM; + } else if (authority.equalsIgnoreCase(getAuthorityName(CLAP_THREAD))) { + result = CLAP_THREAD; + } + } + } + + return result; + } + + /** + * Gets the local file corresponding to the reference. Only URIs referring to the "localhost" or + * to an empty authority are supported. + * + * @return The local file corresponding to the reference. + */ + public File getFile() { + File result = null; + + if (Protocol.FILE.equals(getSchemeProtocol())) { + final String hostName = getAuthority(); + + if ((hostName == null) + || hostName.isEmpty() + || hostName.equalsIgnoreCase("localhost")) { + final String filePath = Reference.decode(getPath()); + result = new File(filePath); + } else { + throw new RuntimeException("Can't resolve files on remote host machines"); + } + } + + return result; + } + + /** + * Returns the JAR entry path. + * + * @return The JAR entry path. + */ + public String getJarEntryPath() { + String result = null; + + if (Protocol.JAR.equals(getSchemeProtocol())) { + final String ssp = getSchemeSpecificPart(); + + if (ssp != null) { + final int separatorIndex = ssp.indexOf("!/"); + + if (separatorIndex != -1) { + result = ssp.substring(separatorIndex + 2); + } + } + } + + return result; + } + + /** + * Returns the JAR file reference. + * + * @return The JAR file reference. + */ + public Reference getJarFileRef() { + Reference result = null; + + if (Protocol.JAR.equals(getSchemeProtocol())) { + final String ssp = getSchemeSpecificPart(); + + if (ssp != null) { + final int separatorIndex = ssp.indexOf("!/"); + + if (separatorIndex != -1) { + result = new Reference(ssp.substring(0, separatorIndex)); + } + } + } + + return result; + } + + /** + * Returns the type of authority. + * + * @return The type of authority. + */ + public int getRiapAuthorityType() { + int result = 0; + + if (Protocol.RIAP.equals(getSchemeProtocol())) { + final String authority = getAuthority(); + + if (authority != null) { + if (authority.equalsIgnoreCase(getAuthorityName(RIAP_APPLICATION))) { + result = RIAP_APPLICATION; + } else if (authority.equalsIgnoreCase(getAuthorityName(RIAP_COMPONENT))) { + result = RIAP_COMPONENT; + } else if (authority.equalsIgnoreCase(getAuthorityName(RIAP_HOST))) { + result = RIAP_HOST; + } + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/MediaType.java b/org.restlet/src/main/java/org/restlet/data/MediaType.java index 0e2126df3a..f6532a2ea1 100644 --- a/org.restlet/src/main/java/org/restlet/data/MediaType.java +++ b/org.restlet/src/main/java/org/restlet/data/MediaType.java @@ -1,30 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.engine.header.HeaderWriter; import org.restlet.engine.util.StringUtils; import org.restlet.engine.util.SystemUtils; import org.restlet.util.Series; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; - /** - * Metadata used to specify the format of representations. The - * {@link #getName()} method returns a full String representation of the media - * type including the parameters. - * + * Metadata used to specify the format of representations. The {@link #getName()} method returns a + * full String representation of the media type including the parameters. + * * @see MIME types on Wikipedia * @author Jerome Louvel */ @@ -33,506 +30,487 @@ public final class MediaType extends Metadata { /** * Illegal ASCII characters as defined in RFC 1521.
* Keep the underscore for the ordering - * + * * @see RFC 1521 */ private static final String _TSPECIALS = "()<>@,;:/[]?=\\\""; /** - * The known media types registered with {@link #register(String, String)}, - * retrievable using {@link #valueOf(String)}.
+ * The known media types registered with {@link #register(String, String)}, retrievable using + * {@link #valueOf(String)}.
* Keep the underscore for the ordering. */ private static volatile Map _types = null; public static final MediaType ALL = register("*/*", "All media"); - public static final MediaType APPLICATION_ALL = register("application/*", - "All application documents"); - - public static final MediaType APPLICATION_ALL_JSON = register( - "application/*+json", "All application/*+json documents"); - - public static final MediaType APPLICATION_ALL_XML = register( - "application/*+xml", "All application/*+xml documents"); - - public static final MediaType APPLICATION_ATOM = register( - "application/atom+xml", "Atom document"); - - public static final MediaType APPLICATION_ATOMPUB_CATEGORY = register( - "application/atomcat+xml", "Atom category document"); - - public static final MediaType APPLICATION_ATOMPUB_SERVICE = register( - "application/atomsvc+xml", "Atom service document"); + public static final MediaType APPLICATION_ALL = + register("application/*", "All application documents"); - public static final MediaType APPLICATION_CAB = register( - "application/vnd.ms-cab-compressed", "Microsoft Cabinet archive"); + public static final MediaType APPLICATION_ALL_JSON = + register("application/*+json", "All application/*+json documents"); - public static final MediaType APPLICATION_COMPRESS = register( - "application/x-compress", "Compressed file"); + public static final MediaType APPLICATION_ALL_XML = + register("application/*+xml", "All application/*+xml documents"); - public static final MediaType APPLICATION_ECORE = register( - "application/x-ecore+xmi+xml", "EMOF ECore metamodel"); + public static final MediaType APPLICATION_ATOM = + register("application/atom+xml", "Atom document"); - public static final MediaType APPLICATION_EXCEL = register( - "application/vnd.ms-excel", "Microsoft Excel document"); + public static final MediaType APPLICATION_ATOMPUB_CATEGORY = + register("application/atomcat+xml", "Atom category document"); - public static final MediaType APPLICATION_FLASH = register( - "application/x-shockwave-flash", "Shockwave Flash object"); + public static final MediaType APPLICATION_ATOMPUB_SERVICE = + register("application/atomsvc+xml", "Atom service document"); - public static final MediaType APPLICATION_GNU_TAR = register( - "application/x-gtar", "GNU Tar archive"); + public static final MediaType APPLICATION_CAB = + register("application/vnd.ms-cab-compressed", "Microsoft Cabinet archive"); - public static final MediaType APPLICATION_GNU_ZIP = register( - "application/x-gzip", "GNU Zip archive"); + public static final MediaType APPLICATION_COMPRESS = + register("application/x-compress", "Compressed file"); - public static final MediaType APPLICATION_HTTP_COOKIES = register( - "application/x-http-cookies", "HTTP cookies"); + public static final MediaType APPLICATION_ECORE = + register("application/x-ecore+xmi+xml", "EMOF ECore metamodel"); - public static final MediaType APPLICATION_JAVA = register( - "application/java", "Java class"); + public static final MediaType APPLICATION_EXCEL = + register("application/vnd.ms-excel", "Microsoft Excel document"); - public static final MediaType APPLICATION_JAVA_ARCHIVE = register( - "application/java-archive", "Java archive"); + public static final MediaType APPLICATION_FLASH = + register("application/x-shockwave-flash", "Shockwave Flash object"); - public static final MediaType APPLICATION_JAVA_OBJECT = register( - "application/x-java-serialized-object", "Java serialized object"); + public static final MediaType APPLICATION_GNU_TAR = + register("application/x-gtar", "GNU Tar archive"); - public static final MediaType APPLICATION_JAVA_OBJECT_GWT = register( - "text/x-gwt-rpc", "Java serialized object (using GWT-RPC encoder)"); + public static final MediaType APPLICATION_GNU_ZIP = + register("application/x-gzip", "GNU Zip archive"); - public static final MediaType APPLICATION_JAVA_OBJECT_XML = register( - "text/x-gwt-rpc+xml", - "Java serialized object (using JavaBeans XML encoder)"); + public static final MediaType APPLICATION_HTTP_COOKIES = + register("application/x-http-cookies", "HTTP cookies"); - public static final MediaType APPLICATION_JAVASCRIPT = register( - "application/x-javascript", "Javascript document"); + public static final MediaType APPLICATION_JAVA = register("application/java", "Java class"); - public static final MediaType APPLICATION_JNLP = register( - "application/x-java-jnlp-file", "JNLP"); + public static final MediaType APPLICATION_JAVA_ARCHIVE = + register("application/java-archive", "Java archive"); - public static final MediaType APPLICATION_JSON = register( - "application/json", "JavaScript Object Notation document"); + public static final MediaType APPLICATION_JAVA_OBJECT = + register("application/x-java-serialized-object", "Java serialized object"); - public static final MediaType APPLICATION_JSON_ACTIVITY = register( - "application/activity+json", "Activity Streams JSON document"); + public static final MediaType APPLICATION_JAVA_OBJECT_GWT = + register("text/x-gwt-rpc", "Java serialized object (using GWT-RPC encoder)"); - public static final MediaType APPLICATION_JSON_PATCH = register( - "application/json-patch", "JSON patch document"); + public static final MediaType APPLICATION_JAVA_OBJECT_XML = + register("text/x-gwt-rpc+xml", "Java serialized object (using JavaBeans XML encoder)"); - public static final MediaType APPLICATION_JSON_SMILE = register( - "application/x-json-smile", - "JavaScript Object Notation smile document"); + public static final MediaType APPLICATION_JAVASCRIPT = + register("application/x-javascript", "Javascript document"); - public static final MediaType APPLICATION_KML = register( - "application/vnd.google-earth.kml+xml", - "Google Earth/Maps KML document"); + public static final MediaType APPLICATION_JNLP = + register("application/x-java-jnlp-file", "JNLP"); - public static final MediaType APPLICATION_KMZ = register( - "application/vnd.google-earth.kmz", - "Google Earth/Maps KMZ document"); + public static final MediaType APPLICATION_JSON = + register("application/json", "JavaScript Object Notation document"); - public static final MediaType APPLICATION_LATEX = register( - "application/x-latex", "LaTeX"); + public static final MediaType APPLICATION_JSON_ACTIVITY = + register("application/activity+json", "Activity Streams JSON document"); - public static final MediaType APPLICATION_MAC_BINHEX40 = register( - "application/mac-binhex40", "Mac binhex40"); + public static final MediaType APPLICATION_JSON_PATCH = + register("application/json-patch", "JSON patch document"); - public static final MediaType APPLICATION_MATHML = register( - "application/mathml+xml", "MathML XML document"); + public static final MediaType APPLICATION_JSON_SMILE = + register("application/x-json-smile", "JavaScript Object Notation smile document"); - public static final MediaType APPLICATION_MSML = register( - "application/msml+xml", "Media Server Markup Language"); + public static final MediaType APPLICATION_KML = + register("application/vnd.google-earth.kml+xml", "Google Earth/Maps KML document"); - public static final MediaType APPLICATION_MSOFFICE_DOCM = register( - "application/vnd.ms-word.document.macroEnabled.12", - "Office Word 2007 macro-enabled document"); + public static final MediaType APPLICATION_KMZ = + register("application/vnd.google-earth.kmz", "Google Earth/Maps KMZ document"); - public static final MediaType APPLICATION_MSOFFICE_DOCX = register( - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "Microsoft Office Word 2007 document"); + public static final MediaType APPLICATION_LATEX = register("application/x-latex", "LaTeX"); - public static final MediaType APPLICATION_MSOFFICE_DOTM = register( - "application/vnd.ms-word.template.macroEnabled.12", - "Office Word 2007 macro-enabled document template"); + public static final MediaType APPLICATION_MAC_BINHEX40 = + register("application/mac-binhex40", "Mac binhex40"); - public static final MediaType APPLICATION_MSOFFICE_DOTX = register( - "application/vnd.openxmlformats-officedocument.wordprocessingml.template", - "Office Word 2007 template"); + public static final MediaType APPLICATION_MATHML = + register("application/mathml+xml", "MathML XML document"); - public static final MediaType APPLICATION_MSOFFICE_ONETOC = register( - "application/onenote", "Microsoft Office OneNote 2007 TOC"); + public static final MediaType APPLICATION_MSML = + register("application/msml+xml", "Media Server Markup Language"); - public static final MediaType APPLICATION_MSOFFICE_ONETOC2 = register( - "application/onenote", "Office OneNote 2007 TOC"); + public static final MediaType APPLICATION_MSOFFICE_DOCM = + register( + "application/vnd.ms-word.document.macroEnabled.12", + "Office Word 2007 macro-enabled document"); - public static final MediaType APPLICATION_MSOFFICE_POTM = register( - "application/vnd.ms-powerpoint.template.macroEnabled.12", - "Office PowerPoint 2007 macro-enabled presentation template"); + public static final MediaType APPLICATION_MSOFFICE_DOCX = + register( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Microsoft Office Word 2007 document"); - public static final MediaType APPLICATION_MSOFFICE_POTX = register( - "application/vnd.openxmlformats-officedocument.presentationml.template", - "Office PowerPoint 2007 template"); + public static final MediaType APPLICATION_MSOFFICE_DOTM = + register( + "application/vnd.ms-word.template.macroEnabled.12", + "Office Word 2007 macro-enabled document template"); - public static final MediaType APPLICATION_MSOFFICE_PPAM = register( - "application/vnd.ms-powerpoint.addin.macroEnabled.12", - "Office PowerPoint 2007 add-in"); + public static final MediaType APPLICATION_MSOFFICE_DOTX = + register( + "application/vnd.openxmlformats-officedocument.wordprocessingml.template", + "Office Word 2007 template"); - public static final MediaType APPLICATION_MSOFFICE_PPSM = register( - "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", - "Office PowerPoint 2007 macro-enabled slide show"); + public static final MediaType APPLICATION_MSOFFICE_ONETOC = + register("application/onenote", "Microsoft Office OneNote 2007 TOC"); - public static final MediaType APPLICATION_MSOFFICE_PPSX = register( - "application/vnd.openxmlformats-officedocument.presentationml.slideshow", - "Office PowerPoint 2007 slide show"); + public static final MediaType APPLICATION_MSOFFICE_ONETOC2 = + register("application/onenote", "Office OneNote 2007 TOC"); - public static final MediaType APPLICATION_MSOFFICE_PPTM = register( - "application/vnd.ms-powerpoint.presentation.macroEnabled.12", - "Office PowerPoint 2007 macro-enabled presentation"); + public static final MediaType APPLICATION_MSOFFICE_POTM = + register( + "application/vnd.ms-powerpoint.template.macroEnabled.12", + "Office PowerPoint 2007 macro-enabled presentation template"); - public static final MediaType APPLICATION_MSOFFICE_PPTX = register( - "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "Microsoft Office PowerPoint 2007 presentation"); + public static final MediaType APPLICATION_MSOFFICE_POTX = + register( + "application/vnd.openxmlformats-officedocument.presentationml.template", + "Office PowerPoint 2007 template"); - public static final MediaType APPLICATION_MSOFFICE_SLDM = register( - "application/vnd.ms-powerpoint.slide.macroEnabled.12", - "Office PowerPoint 2007 macro-enabled slide"); + public static final MediaType APPLICATION_MSOFFICE_PPAM = + register( + "application/vnd.ms-powerpoint.addin.macroEnabled.12", + "Office PowerPoint 2007 add-in"); - public static final MediaType APPLICATION_MSOFFICE_SLDX = register( - "application/vnd.openxmlformats-officedocument.presentationml.slide", - "Office PowerPoint 2007 slide"); + public static final MediaType APPLICATION_MSOFFICE_PPSM = + register( + "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", + "Office PowerPoint 2007 macro-enabled slide show"); - public static final MediaType APPLICATION_MSOFFICE_XLAM = register( - "application/vnd.ms-excel.addin.macroEnabled.12", - "Office Excel 2007 add-in"); + public static final MediaType APPLICATION_MSOFFICE_PPSX = + register( + "application/vnd.openxmlformats-officedocument.presentationml.slideshow", + "Office PowerPoint 2007 slide show"); - public static final MediaType APPLICATION_MSOFFICE_XLSB = register( - "application/vnd.ms-excel.sheet.binary.macroEnabled.12", - "Office Excel 2007 binary workbook"); + public static final MediaType APPLICATION_MSOFFICE_PPTM = + register( + "application/vnd.ms-powerpoint.presentation.macroEnabled.12", + "Office PowerPoint 2007 macro-enabled presentation"); - public static final MediaType APPLICATION_MSOFFICE_XLSM = register( - "application/vnd.ms-excel.sheet.macroEnabled.12", - "Office Excel 2007 macro-enabled workbook"); + public static final MediaType APPLICATION_MSOFFICE_PPTX = + register( + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Microsoft Office PowerPoint 2007 presentation"); - public static final MediaType APPLICATION_MSOFFICE_XLSX = register( - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "Microsoft Office Excel 2007 workbook"); + public static final MediaType APPLICATION_MSOFFICE_SLDM = + register( + "application/vnd.ms-powerpoint.slide.macroEnabled.12", + "Office PowerPoint 2007 macro-enabled slide"); - public static final MediaType APPLICATION_MSOFFICE_XLTM = register( - "application/vnd.ms-excel.template.macroEnabled.12", - "Office Excel 2007 macro-enabled workbook template"); + public static final MediaType APPLICATION_MSOFFICE_SLDX = + register( + "application/vnd.openxmlformats-officedocument.presentationml.slide", + "Office PowerPoint 2007 slide"); - public static final MediaType APPLICATION_MSOFFICE_XLTX = register( - "application/vnd.openxmlformats-officedocument.spreadsheetml.template", - "Office Excel 2007 template"); + public static final MediaType APPLICATION_MSOFFICE_XLAM = + register("application/vnd.ms-excel.addin.macroEnabled.12", "Office Excel 2007 add-in"); - public static final MediaType APPLICATION_OCTET_STREAM = register( - "application/octet-stream", "Raw octet stream"); + public static final MediaType APPLICATION_MSOFFICE_XLSB = + register( + "application/vnd.ms-excel.sheet.binary.macroEnabled.12", + "Office Excel 2007 binary workbook"); - public static final MediaType APPLICATION_OPENOFFICE_ODB = register( - "application/vnd.oasis.opendocument.database", - "OpenDocument Database"); + public static final MediaType APPLICATION_MSOFFICE_XLSM = + register( + "application/vnd.ms-excel.sheet.macroEnabled.12", + "Office Excel 2007 macro-enabled workbook"); + + public static final MediaType APPLICATION_MSOFFICE_XLSX = + register( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Microsoft Office Excel 2007 workbook"); + + public static final MediaType APPLICATION_MSOFFICE_XLTM = + register( + "application/vnd.ms-excel.template.macroEnabled.12", + "Office Excel 2007 macro-enabled workbook template"); + + public static final MediaType APPLICATION_MSOFFICE_XLTX = + register( + "application/vnd.openxmlformats-officedocument.spreadsheetml.template", + "Office Excel 2007 template"); + + public static final MediaType APPLICATION_OCTET_STREAM = + register("application/octet-stream", "Raw octet stream"); + + public static final MediaType APPLICATION_OPENOFFICE_ODB = + register("application/vnd.oasis.opendocument.database", "OpenDocument Database"); + + public static final MediaType APPLICATION_OPENOFFICE_ODC = + register("application/vnd.oasis.opendocument.chart", "OpenDocument Chart"); + + public static final MediaType APPLICATION_OPENOFFICE_ODF = + register("application/vnd.oasis.opendocument.formula", "OpenDocument Formula"); + + public static final MediaType APPLICATION_OPENOFFICE_ODG = + register("application/vnd.oasis.opendocument.graphics", "OpenDocument Drawing"); - public static final MediaType APPLICATION_OPENOFFICE_ODC = register( - "application/vnd.oasis.opendocument.chart", "OpenDocument Chart"); + public static final MediaType APPLICATION_OPENOFFICE_ODI = + register("application/vnd.oasis.opendocument.image", "OpenDocument Image "); - public static final MediaType APPLICATION_OPENOFFICE_ODF = register( - "application/vnd.oasis.opendocument.formula", - "OpenDocument Formula"); + public static final MediaType APPLICATION_OPENOFFICE_ODM = + register( + "application/vnd.oasis.opendocument.text-master", + "OpenDocument Master Document"); - public static final MediaType APPLICATION_OPENOFFICE_ODG = register( - "application/vnd.oasis.opendocument.graphics", - "OpenDocument Drawing"); + public static final MediaType APPLICATION_OPENOFFICE_ODP = + register( + "application/vnd.oasis.opendocument.presentation", + "OpenDocument Presentation "); - public static final MediaType APPLICATION_OPENOFFICE_ODI = register( - "application/vnd.oasis.opendocument.image", "OpenDocument Image "); + public static final MediaType APPLICATION_OPENOFFICE_ODS = + register("application/vnd.oasis.opendocument.spreadsheet", "OpenDocument Spreadsheet"); - public static final MediaType APPLICATION_OPENOFFICE_ODM = register( - "application/vnd.oasis.opendocument.text-master", - "OpenDocument Master Document"); + public static final MediaType APPLICATION_OPENOFFICE_ODT = + register("application/vnd.oasis.opendocument.text ", "OpenDocument Text"); - public static final MediaType APPLICATION_OPENOFFICE_ODP = register( - "application/vnd.oasis.opendocument.presentation", - "OpenDocument Presentation "); + public static final MediaType APPLICATION_OPENOFFICE_OTG = + register( + "application/vnd.oasis.opendocument.graphics-template", + "OpenDocument Drawing Template"); - public static final MediaType APPLICATION_OPENOFFICE_ODS = register( - "application/vnd.oasis.opendocument.spreadsheet", - "OpenDocument Spreadsheet"); + public static final MediaType APPLICATION_OPENOFFICE_OTH = + register("application/vnd.oasis.opendocument.text-web", "HTML Document Template"); - public static final MediaType APPLICATION_OPENOFFICE_ODT = register( - "application/vnd.oasis.opendocument.text ", "OpenDocument Text"); + public static final MediaType APPLICATION_OPENOFFICE_OTP = + register( + "application/vnd.oasis.opendocument.presentation-template", + "OpenDocument Presentation Template"); - public static final MediaType APPLICATION_OPENOFFICE_OTG = register( - "application/vnd.oasis.opendocument.graphics-template", - "OpenDocument Drawing Template"); + public static final MediaType APPLICATION_OPENOFFICE_OTS = + register( + "application/vnd.oasis.opendocument.spreadsheet-template", + "OpenDocument Spreadsheet Template"); - public static final MediaType APPLICATION_OPENOFFICE_OTH = register( - "application/vnd.oasis.opendocument.text-web", - "HTML Document Template"); + public static final MediaType APPLICATION_OPENOFFICE_OTT = + register( + "application/vnd.oasis.opendocument.text-template", + "OpenDocument Text Template"); - public static final MediaType APPLICATION_OPENOFFICE_OTP = register( - "application/vnd.oasis.opendocument.presentation-template", - "OpenDocument Presentation Template"); + public static final MediaType APPLICATION_OPENOFFICE_OXT = + register("application/vnd.openofficeorg.extension", "OpenOffice.org extension"); - public static final MediaType APPLICATION_OPENOFFICE_OTS = register( - "application/vnd.oasis.opendocument.spreadsheet-template", - "OpenDocument Spreadsheet Template"); + public static final MediaType APPLICATION_PDF = + register("application/pdf", "Adobe PDF document"); - public static final MediaType APPLICATION_OPENOFFICE_OTT = register( - "application/vnd.oasis.opendocument.text-template", - "OpenDocument Text Template"); + public static final MediaType APPLICATION_POSTSCRIPT = + register("application/postscript", "Postscript document"); - public static final MediaType APPLICATION_OPENOFFICE_OXT = register( - "application/vnd.openofficeorg.extension", - "OpenOffice.org extension"); + public static final MediaType APPLICATION_POWERPOINT = + register("application/vnd.ms-powerpoint", "Microsoft Powerpoint document"); - public static final MediaType APPLICATION_PDF = register("application/pdf", - "Adobe PDF document"); + public static final MediaType APPLICATION_PROJECT = + register("application/vnd.ms-project", "Microsoft Project document"); - public static final MediaType APPLICATION_POSTSCRIPT = register( - "application/postscript", "Postscript document"); + public static final MediaType APPLICATION_RDF_TRIG = + register( + "application/x-trig", + "Plain text serialized Resource Description Framework document"); - public static final MediaType APPLICATION_POWERPOINT = register( - "application/vnd.ms-powerpoint", "Microsoft Powerpoint document"); + public static final MediaType APPLICATION_RDF_TRIX = + register( + "application/trix", + "Simple XML serialized Resource Description Framework document"); - public static final MediaType APPLICATION_PROJECT = register( - "application/vnd.ms-project", "Microsoft Project document"); + public static final MediaType APPLICATION_RDF_XML = + register( + "application/rdf+xml", + "Normalized XML serialized Resource Description Framework document"); - public static final MediaType APPLICATION_RDF_TRIG = register( - "application/x-trig", - "Plain text serialized Resource Description Framework document"); + public static final MediaType APPLICATION_RELAXNG_COMPACT = + register( + "application/relax-ng-compact-syntax", + "Relax NG Schema document, Compact syntax"); - public static final MediaType APPLICATION_RDF_TRIX = register( - "application/trix", - "Simple XML serialized Resource Description Framework document"); + public static final MediaType APPLICATION_RELAXNG_XML = + register("application/x-relax-ng+xml", "Relax NG Schema document, XML syntax"); - public static final MediaType APPLICATION_RDF_XML = register( - "application/rdf+xml", - "Normalized XML serialized Resource Description Framework document"); + public static final MediaType APPLICATION_RSS = + register("application/rss+xml", "Really Simple Syndication document"); - public static final MediaType APPLICATION_RELAXNG_COMPACT = register( - "application/relax-ng-compact-syntax", - "Relax NG Schema document, Compact syntax"); + public static final MediaType APPLICATION_RTF = + register("application/rtf", "Rich Text Format document"); - public static final MediaType APPLICATION_RELAXNG_XML = register( - "application/x-relax-ng+xml", - "Relax NG Schema document, XML syntax"); + public static final MediaType APPLICATION_SDP = + register("application/sdp", "Session Description Protocol"); - public static final MediaType APPLICATION_RSS = register( - "application/rss+xml", "Really Simple Syndication document"); + public static final MediaType APPLICATION_SPARQL_RESULTS_JSON = + register("application/sparql-results+json", "SPARQL Query Results JSON document"); - public static final MediaType APPLICATION_RTF = register("application/rtf", - "Rich Text Format document"); + public static final MediaType APPLICATION_SPARQL_RESULTS_XML = + register("application/sparql-results+xml", "SPARQL Query Results XML document"); - public static final MediaType APPLICATION_SDP = register("application/sdp", - "Session Description Protocol"); + public static final MediaType APPLICATION_SPSS_SAV = + register("application/x-spss-sav", "SPSS Data"); - public static final MediaType APPLICATION_SPARQL_RESULTS_JSON = register( - "application/sparql-results+json", - "SPARQL Query Results JSON document"); + public static final MediaType APPLICATION_SPSS_SPS = + register("application/x-spss-sps", "SPSS Script Syntax"); - public static final MediaType APPLICATION_SPARQL_RESULTS_XML = register( - "application/sparql-results+xml", - "SPARQL Query Results XML document"); + public static final MediaType APPLICATION_STATA_STA = + register("application/x-stata", "Stata data file"); - public static final MediaType APPLICATION_SPSS_SAV = register( - "application/x-spss-sav", "SPSS Data"); + public static final MediaType APPLICATION_STUFFIT = + register("application/x-stuffit", "Stuffit archive"); - public static final MediaType APPLICATION_SPSS_SPS = register( - "application/x-spss-sps", "SPSS Script Syntax"); + public static final MediaType APPLICATION_TAR = register("application/x-tar", "Tar archive"); - public static final MediaType APPLICATION_STATA_STA = register( - "application/x-stata", "Stata data file"); + public static final MediaType APPLICATION_TEX = register("application/x-tex", "Tex file"); - public static final MediaType APPLICATION_STUFFIT = register( - "application/x-stuffit", "Stuffit archive"); + public static final MediaType APPLICATION_TROFF_MAN = + register("application/x-troff-man", "LaTeX"); - public static final MediaType APPLICATION_TAR = register( - "application/x-tar", "Tar archive"); + public static final MediaType APPLICATION_VOICEXML = + register("application/voicexml+xml", "VoiceXML"); - public static final MediaType APPLICATION_TEX = register( - "application/x-tex", "Tex file"); + public static final MediaType APPLICATION_W3C_SCHEMA = + register("application/x-xsd+xml", "W3C XML Schema document"); - public static final MediaType APPLICATION_TROFF_MAN = register( - "application/x-troff-man", "LaTeX"); + public static final MediaType APPLICATION_W3C_XSLT = + register("application/xslt+xml", "W3C XSLT Stylesheet"); - public static final MediaType APPLICATION_VOICEXML = register( - "application/voicexml+xml", "VoiceXML"); + public static final MediaType APPLICATION_WADL = + register( + "application/vnd.sun.wadl+xml", + "Web Application Description Language document"); - public static final MediaType APPLICATION_W3C_SCHEMA = register( - "application/x-xsd+xml", "W3C XML Schema document"); + public static final MediaType APPLICATION_WORD = + register("application/msword", "Microsoft Word document"); - public static final MediaType APPLICATION_W3C_XSLT = register( - "application/xslt+xml", "W3C XSLT Stylesheet"); + public static final MediaType APPLICATION_WWW_FORM = + register("application/x-www-form-urlencoded", "Web form (URL encoded)"); - public static final MediaType APPLICATION_WADL = register( - "application/vnd.sun.wadl+xml", - "Web Application Description Language document"); + public static final MediaType APPLICATION_XHTML = + register("application/xhtml+xml", "XHTML document"); - public static final MediaType APPLICATION_WORD = register( - "application/msword", "Microsoft Word document"); + public static final MediaType APPLICATION_XMI = register("application/xmi+xml", "XMI document"); - public static final MediaType APPLICATION_WWW_FORM = register( - "application/x-www-form-urlencoded", "Web form (URL encoded)"); + public static final MediaType APPLICATION_XML = register("application/xml", "XML document"); - public static final MediaType APPLICATION_XHTML = register( - "application/xhtml+xml", "XHTML document"); + public static final MediaType APPLICATION_XML_DTD = register("application/xml-dtd", "XML DTD"); - public static final MediaType APPLICATION_XMI = register( - "application/xmi+xml", "XMI document"); + public static final MediaType APPLICATION_XQUERY = + register("application/xquery", "XQuery document"); - public static final MediaType APPLICATION_XML = register("application/xml", - "XML document"); + public static final MediaType APPLICATION_XUL = + register("application/vnd.mozilla.xul+xml", "XUL document"); - public static final MediaType APPLICATION_XML_DTD = register( - "application/xml-dtd", "XML DTD"); + public static final MediaType APPLICATION_YAML = + register("application/x-yaml", "YAML document"); - public static final MediaType APPLICATION_XQUERY = register( - "application/xquery", "XQuery document"); - - public static final MediaType APPLICATION_XUL = register( - "application/vnd.mozilla.xul+xml", "XUL document"); - - public static final MediaType APPLICATION_YAML = register( - "application/x-yaml", "YAML document"); - - public static final MediaType APPLICATION_ZIP = register("application/zip", - "Zip archive"); + public static final MediaType APPLICATION_ZIP = register("application/zip", "Zip archive"); public static final MediaType AUDIO_ALL = register("audio/*", "All audios"); - public static final MediaType AUDIO_BASIC = register("audio/basic", - "AU audio"); + public static final MediaType AUDIO_BASIC = register("audio/basic", "AU audio"); - public static final MediaType AUDIO_MIDI = register("audio/midi", - "MIDI audio"); + public static final MediaType AUDIO_MIDI = register("audio/midi", "MIDI audio"); - public static final MediaType AUDIO_MPEG = register("audio/mpeg", - "MPEG audio (MP3)"); + public static final MediaType AUDIO_MPEG = register("audio/mpeg", "MPEG audio (MP3)"); - public static final MediaType AUDIO_REAL = register("audio/x-pn-realaudio", - "Real audio"); + public static final MediaType AUDIO_REAL = register("audio/x-pn-realaudio", "Real audio"); - public static final MediaType AUDIO_WAV = register("audio/x-wav", - "Waveform audio"); + public static final MediaType AUDIO_WAV = register("audio/x-wav", "Waveform audio"); public static final MediaType IMAGE_ALL = register("image/*", "All images"); - public static final MediaType IMAGE_BMP = register("image/bmp", - "Windows bitmap"); + public static final MediaType IMAGE_BMP = register("image/bmp", "Windows bitmap"); - public static final MediaType IMAGE_GIF = register("image/gif", - "GIF image"); + public static final MediaType IMAGE_GIF = register("image/gif", "GIF image"); - public static final MediaType IMAGE_ICON = register("image/x-icon", - "Windows icon (Favicon)"); + public static final MediaType IMAGE_ICON = register("image/x-icon", "Windows icon (Favicon)"); - public static final MediaType IMAGE_JPEG = register("image/jpeg", - "JPEG image"); + public static final MediaType IMAGE_JPEG = register("image/jpeg", "JPEG image"); - public static final MediaType IMAGE_PNG = register("image/png", - "PNG image"); + public static final MediaType IMAGE_PNG = register("image/png", "PNG image"); - public static final MediaType IMAGE_SVG = register("image/svg+xml", - "Scalable Vector Graphics"); + public static final MediaType IMAGE_SVG = register("image/svg+xml", "Scalable Vector Graphics"); - public static final MediaType IMAGE_TIFF = register("image/tiff", - "TIFF image"); + public static final MediaType IMAGE_TIFF = register("image/tiff", "TIFF image"); - public static final MediaType MESSAGE_ALL = register("message/*", - "All messages"); + public static final MediaType MESSAGE_ALL = register("message/*", "All messages"); - public static final MediaType MESSAGE_HTTP = register("message/http", - "HTTP message"); + public static final MediaType MESSAGE_HTTP = register("message/http", "HTTP message"); public static final MediaType MODEL_ALL = register("model/*", "All models"); public static final MediaType MODEL_VRML = register("model/vrml", "VRML"); - public static final MediaType MULTIPART_ALL = register("multipart/*", - "All multipart data"); + public static final MediaType MULTIPART_ALL = register("multipart/*", "All multipart data"); - public static final MediaType MULTIPART_FORM_DATA = register( - "multipart/form-data", "Multipart form data"); + public static final MediaType MULTIPART_FORM_DATA = + register("multipart/form-data", "Multipart form data"); public static final MediaType TEXT_ALL = register("text/*", "All texts"); - public static final MediaType TEXT_CALENDAR = register("text/calendar", - "iCalendar event"); + public static final MediaType TEXT_CALENDAR = register("text/calendar", "iCalendar event"); - public static final MediaType TEXT_CSS = register("text/css", - "CSS stylesheet"); + public static final MediaType TEXT_CSS = register("text/css", "CSS stylesheet"); - public static final MediaType TEXT_CSV = register("text/csv", - "Comma-separated Values"); + public static final MediaType TEXT_CSV = register("text/csv", "Comma-separated Values"); - public static final MediaType TEXT_DAT = register("text/x-fixed-field", - "Fixed-width Values"); + public static final MediaType TEXT_DAT = register("text/x-fixed-field", "Fixed-width Values"); - public static final MediaType TEXT_HTML = register("text/html", - "HTML document"); + public static final MediaType TEXT_HTML = register("text/html", "HTML document"); - public static final MediaType TEXT_J2ME_APP_DESCRIPTOR = register( - "text/vnd.sun.j2me.app-descriptor", "J2ME Application Descriptor"); + public static final MediaType TEXT_J2ME_APP_DESCRIPTOR = + register("text/vnd.sun.j2me.app-descriptor", "J2ME Application Descriptor"); - public static final MediaType TEXT_JAVASCRIPT = register("text/javascript", - "Javascript document"); + public static final MediaType TEXT_JAVASCRIPT = + register("text/javascript", "Javascript document"); - public static final MediaType TEXT_PLAIN = register("text/plain", - "Plain text"); + public static final MediaType TEXT_PLAIN = register("text/plain", "Plain text"); - public static final MediaType TEXT_RDF_N3 = register("text/n3", - "N3 serialized Resource Description Framework document"); + public static final MediaType TEXT_RDF_N3 = + register("text/n3", "N3 serialized Resource Description Framework document"); - public static final MediaType TEXT_RDF_NTRIPLES = register("text/n-triples", - "N-Triples serialized Resource Description Framework document"); + public static final MediaType TEXT_RDF_NTRIPLES = + register( + "text/n-triples", + "N-Triples serialized Resource Description Framework document"); - public static final MediaType TEXT_TSV = register( - "text/tab-separated-values", "Tab-separated Values"); + public static final MediaType TEXT_TSV = + register("text/tab-separated-values", "Tab-separated Values"); - public static final MediaType TEXT_TURTLE = register("text/turtle", - "Plain text serialized Resource Description Framework document"); + public static final MediaType TEXT_TURTLE = + register( + "text/turtle", "Plain text serialized Resource Description Framework document"); - public static final MediaType TEXT_URI_LIST = register("text/uri-list", - "List of URIs"); + public static final MediaType TEXT_URI_LIST = register("text/uri-list", "List of URIs"); - public static final MediaType TEXT_VCARD = register("text/x-vcard", - "vCard"); + public static final MediaType TEXT_VCARD = register("text/x-vcard", "vCard"); public static final MediaType TEXT_XML = register("text/xml", "XML text"); - public static final MediaType TEXT_YAML = register("text/x-yaml", - "YAML document"); + public static final MediaType TEXT_YAML = register("text/x-yaml", "YAML document"); public static final MediaType VIDEO_ALL = register("video/*", "All videos"); - public static final MediaType VIDEO_AVI = register("video/x-msvideo", - "AVI video"); + public static final MediaType VIDEO_AVI = register("video/x-msvideo", "AVI video"); - public static final MediaType VIDEO_MP4 = register("video/mp4", - "MPEG-4 video"); + public static final MediaType VIDEO_MP4 = register("video/mp4", "MPEG-4 video"); - public static final MediaType VIDEO_MPEG = register("video/mpeg", - "MPEG video"); + public static final MediaType VIDEO_MPEG = register("video/mpeg", "MPEG video"); - public static final MediaType VIDEO_QUICKTIME = register("video/quicktime", - "Quicktime video"); + public static final MediaType VIDEO_QUICKTIME = register("video/quicktime", "Quicktime video"); - public static final MediaType VIDEO_WMV = register("video/x-ms-wmv", - "Windows movie"); + public static final MediaType VIDEO_WMV = register("video/x-ms-wmv", "Windows movie"); /** - * Returns the first of the most specific media type of the given array of - * {@link MediaType}s. - *

- * Examples: + * Returns the first of the most specific media type of the given array of {@link MediaType}s. + * + *

Examples: + * *

    - *
  • "text/plain" is more specific than "text/*" or "image/*"
  • - *
  • "text/html" is same specific as "application/pdf" or "image/jpg"
  • - *
  • "text/*" is same specific than "application/*" or "image/*"
  • - *
  • "*/*" is the most unspecific MediaType
  • + *
  • "text/plain" is more specific than "text/*" or "image/*" + *
  • "text/html" is as specific as "application/pdf" or "image/jpg" + *
  • "text/*" is as specific as "application/*" or "image/*" + *
  • "*" is the most unspecific MediaType *
- * + * * @param mediaTypes An array of media types. * @return The most concrete MediaType. * @throws IllegalArgumentException If the array is null or empty. @@ -540,8 +518,7 @@ public final class MediaType extends Metadata { public static MediaType getMostSpecific(MediaType... mediaTypes) throws IllegalArgumentException { if ((mediaTypes == null) || (mediaTypes.length == 0)) { - throw new IllegalArgumentException( - "You must give at least one MediaType"); + throw new IllegalArgumentException("You must give at least one MediaType"); } if (mediaTypes.length == 1) { @@ -575,19 +552,19 @@ public static MediaType getMostSpecific(MediaType... mediaTypes) /** * Returns the known media types map. - * + * * @return the known media types map. */ private static Map getTypes() { if (_types == null) { - _types = new HashMap(); + _types = new HashMap<>(); } return _types; } /** * Normalizes the specified token. - * + * * @param token Token to normalize. * @return The normalized token. * @throws IllegalArgumentException if token is not legal. @@ -598,8 +575,7 @@ private static String normalizeToken(String token) { // Makes sure we're not dealing with a "*" token. token = token.trim(); - if (token.isEmpty() || "*".equals(token)) - return "*"; + if (token.isEmpty() || "*".equals(token)) return "*"; // Makes sure the token is RFC compliant. length = token.length(); @@ -614,13 +590,12 @@ private static String normalizeToken(String token) { /** * Normalizes the specified media type. - * - * @param name The name of the type to normalize. + * + * @param name The name of the type to normalize. * @param parameters The parameters of the type to normalize. * @return The normalized type. */ - private static String normalizeType(String name, - Series parameters) { + private static String normalizeType(String name, Series parameters) { int slashIndex; int colonIndex; String mainType; @@ -628,10 +603,9 @@ private static String normalizeType(String name, StringBuilder params = null; // Ignore null names (backward compatibility). - if (name == null) - return null; + if (name == null) return null; - // Check presence of parameters + // Check the presence of parameters if ((colonIndex = name.indexOf(';')) != -1) { params = new StringBuilder(name.substring(colonIndex)); name = name.substring(0, colonIndex); @@ -642,7 +616,7 @@ private static String normalizeType(String name, mainType = normalizeToken(name); subType = "*"; } else { - // Normalizes the main and sub types. + // Normalizes the main and subtypes. mainType = normalizeToken(name.substring(0, slashIndex)); subType = normalizeToken(name.substring(slashIndex + 1)); } @@ -653,14 +627,14 @@ private static String normalizeType(String name, if (params == null) { params = new StringBuilder(); } - HeaderWriter hw = new HeaderWriter() { - @Override - public HeaderWriter append(Parameter value) { - return appendExtension(value); - } - }; - for (int i = 0; i < parameters.size(); i++) { - Parameter p = parameters.get(i); + HeaderWriter hw = + new HeaderWriter<>() { + @Override + public HeaderWriter append(Parameter value) { + return appendExtension(value); + } + }; + for (Parameter p : parameters) { hw.appendParameterSeparator(); hw.appendSpace(); hw.append(p); @@ -668,26 +642,26 @@ public HeaderWriter append(Parameter value) { params.append(hw.toString()); hw.close(); } catch (IOException e) { - Context.getCurrentLogger().log(Level.INFO, - "Unable to parse the media type parameter", e); + Context.getCurrentLogger() + .log(Level.INFO, "Unable to parse the media type parameter", e); } } - return (params == null) ? mainType + '/' + subType + return (params == null) + ? mainType + '/' + subType : mainType + '/' + subType + params.toString(); } /** - * Register a media type as a known type that can later be retrieved using - * {@link #valueOf(String)}. If the type already exists, the existing type - * is returned, otherwise a new instance is created. - * - * @param name The name. + * Register a media type as a known type that can later be retrieved using {@link + * #valueOf(String)}. If the type already exists, the existing type is returned, otherwise a new + * instance is created. + * + * @param name The name. * @param description The description. * @return The registered media type */ - public static synchronized MediaType register(String name, - String description) { + public static synchronized MediaType register(String name, String description) { if (!getTypes().containsKey(name)) { final MediaType type = new MediaType(name, description); @@ -698,9 +672,9 @@ public static synchronized MediaType register(String name, } /** - * Returns the media type associated to a name. If an existing constant - * exists then it is returned, otherwise a new instance is created. - * + * Returns the media type associated with a name. If an existing constant exists, then it is + * returned; otherwise a new instance is created. + * * @param name The name. * @return The associated media type. */ @@ -722,9 +696,9 @@ public static MediaType valueOf(String name) { /** * Constructor that clones an original media type. - * - * @param original The original media type to clone. - * @param paramName The name of the unique parameter to set. + * + * @param original The original media type to clone. + * @param paramName The name of the unique parameter to set. * @param paramValue The value of the unique parameter to set. */ public MediaType(MediaType original, String paramName, String paramValue) { @@ -733,8 +707,8 @@ public MediaType(MediaType original, String paramName, String paramValue) { /** * Constructor that clones an original media type. - * - * @param original The original media type to clone. + * + * @param original The original media type to clone. * @param parameter The unique parameter to set. */ public MediaType(MediaType original, Parameter parameter) { @@ -742,20 +716,22 @@ public MediaType(MediaType original, Parameter parameter) { } /** - * Constructor that clones an original media type by extracting its parent - * media type then adding a new set of parameters. - * - * @param original The original media type to clone. + * Constructor that clones an original media type by extracting its parent media type then + * adding a new set of parameters. + * + * @param original The original media type to clone. * @param parameters The list of parameters to set. */ public MediaType(MediaType original, Series parameters) { - this((original == null) ? null : original.getName(), parameters, + this( + (original == null) ? null : original.getName(), + parameters, (original == null) ? null : original.getDescription()); } /** * Constructor. - * + * * @param name The name. */ public MediaType(String name) { @@ -764,8 +740,8 @@ public MediaType(String name) { /** * Constructor. - * - * @param name The name. + * + * @param name The name. * @param parameters The list of parameters. */ public MediaType(String name, Series parameters) { @@ -774,26 +750,24 @@ public MediaType(String name, Series parameters) { /** * Constructor. - * - * @param name The name. - * @param parameters The list of parameters. + * + * @param name The name. + * @param parameters The list of parameters. * @param description The description. */ @SuppressWarnings("unchecked") - public MediaType(String name, Series parameters, - String description) { + public MediaType(String name, Series parameters, String description) { super(normalizeType(name, parameters), description); if (parameters != null) { - this.parameters = (Series) Series - .unmodifiableSeries(parameters); + this.parameters = (Series) Series.unmodifiableSeries(parameters); } } /** * Constructor. - * - * @param name The name. + * + * @param name The name. * @param description The description. */ public MediaType(String name, String description) { @@ -807,12 +781,10 @@ public boolean equals(Object obj) { } /** - * Test the equality of two media types, with the possibility to ignore the - * parameters. - * - * @param obj The object to compare to. - * @param ignoreParameters Indicates if parameters should be ignored during - * comparison. + * Test the equality of two media types, with the possibility to ignore the parameters. + * + * @param obj The object to compare to. + * @param ignoreParameters Indicates if parameters should be ignored during comparison. * @return True if both media types are equal. */ public boolean equals(Object obj, boolean ignoreParameters) { @@ -820,13 +792,11 @@ public boolean equals(Object obj, boolean ignoreParameters) { // if obj == this no need to go further if (!result) { - // if obj isn't a mediatype or is null don't evaluate further - if (obj instanceof MediaType) { - final MediaType that = (MediaType) obj; + // if obj isn't a media type or is null, don't evaluate further + if (obj instanceof final MediaType that) { if (getMainType().equals(that.getMainType()) && getSubType().equals(that.getSubType())) { - result = ignoreParameters - || getParameters().equals(that.getParameters()); + result = ignoreParameters || getParameters().equals(that.getParameters()); } } } @@ -836,7 +806,7 @@ && getSubType().equals(that.getSubType())) { /** * Returns the main type. - * + * * @return The main type. */ public String getMainType() { @@ -861,9 +831,9 @@ public String getMainType() { } /** - * Returns the unmodifiable list of parameters corresponding to subtype - * modifiers. Creates a new instance if no one has been set. - * + * Returns the unmodifiable list of parameters corresponding to subtype modifiers. Creates a new + * instance if no one has been set. + * * @return The list of parameters. */ @SuppressWarnings("unchecked") @@ -880,17 +850,15 @@ public Series getParameters() { int index = getName().indexOf(';'); if (index != -1) { - params = new Form( - getName().substring(index + 1).trim(), ';'); + params = new Form(getName().substring(index + 1).trim(), ';'); } } if (params == null) { - params = new Series(Parameter.class); + params = new Series<>(Parameter.class); } - this.parameters = p = (Series) Series - .unmodifiableSeries(params); + this.parameters = p = (Series) Series.unmodifiableSeries(params); } } } @@ -899,33 +867,32 @@ public Series getParameters() { /** * {@inheritDoc}
- * In case the media type has parameters, this method returns the - * concatenation of the main type and the subtype. If the subtype is not - * equal to "*", it returns the concatenation of the main type and "*". - * Otherwise, it returns either the {@link #ALL} media type if it is already - * the {@link #ALL} media type, or null. + * In case the media type has parameters, this method returns the concatenation of the main type + * and the subtype. If the subtype is not equal to "*", it returns the concatenation of the main + * type and "*". Otherwise, it returns either the {@link #ALL} media type if it is already the + * {@link #ALL} media type, or null. */ @Override public MediaType getParent() { - MediaType result = null; + final MediaType result; - if (getParameters().size() > 0) { - result = MediaType.valueOf(getMainType() + "/" + getSubType()); - } else { + if (getParameters().isEmpty()) { if (getSubType().equals("*")) { result = equals(ALL) ? null : ALL; } else { result = MediaType.valueOf(getMainType() + "/*"); } + } else { + result = MediaType.valueOf(getMainType() + "/" + getSubType()); } return result; } /** - * Returns the sub-type. - * - * @return The sub-type. + * Returns the subtype. + * + * @return The subtype. */ public String getSubType() { String result = null; @@ -956,9 +923,9 @@ public int hashCode() { } /** - * Indicates if a given media type is included in the current one @see - * {@link #includes(Metadata, boolean)}. It ignores the parameters. - * + * Indicates if a given media type is included in the current one @see {@link + * #includes(Metadata, boolean)}. It ignores the parameters. + * * @param included The media type to test for inclusion. * @return True if the given media type is included in the current one. * @see #isCompatible(Metadata) @@ -969,19 +936,19 @@ public boolean includes(Metadata included) { } /** - * Indicates if a given media type is included in the current one @see - * {@link #includes(Metadata, boolean)}. The test is true if both types are - * equal or if the given media type is within the range of the current one. - * For example, ALL includes all media types. Parameters are ignored for - * this comparison. A null media type is considered as included into the - * current one. It ignores the parameters. - *

- * Examples: + * Indicates if a given media type is included in the current one @see {@link + * #includes(Metadata, boolean)}. The test is true if both types are equal or if the given media + * type is within the range of the current one. For example, ALL includes all media types. + * Parameters are ignored for this comparison. A null media type is considered as included into + * the current one. It ignores the parameters. + * + *

Examples: + * *

    - *
  • TEXT_ALL.includes(TEXT_PLAIN) returns true
  • - *
  • TEXT_PLAIN.includes(TEXT_ALL) returns false
  • + *
  • TEXT_ALL.includes(TEXT_PLAIN) returns true + *
  • TEXT_PLAIN.includes(TEXT_ALL) returns false *
- * + * * @param included The media type to test for inclusion. * @return True if the given media type is included in the current one. * @see #isCompatible(Metadata) @@ -989,8 +956,7 @@ public boolean includes(Metadata included) { public boolean includes(Metadata included, boolean ignoreParameters) { boolean result = equals(ALL) || equals(included); - if (!result && (included instanceof MediaType)) { - MediaType includedMediaType = (MediaType) included; + if (!result && (included instanceof final MediaType includedMediaType)) { if (getMainType().equals(includedMediaType.getMainType())) { // Both media types are different @@ -1002,23 +968,23 @@ public boolean includes(Metadata included, boolean ignoreParameters) { // Media type A includes media type B if for each param // name/value pair in A, B contains the same name/value. result = true; - for (int i = 0; result - && i < getParameters().size(); i++) { + for (int i = 0; result && i < getParameters().size(); i++) { Parameter param = getParameters().get(i); - Parameter includedParam = includedMediaType - .getParameters().getFirst(param.getName()); + Parameter includedParam = + includedMediaType.getParameters().getFirst(param.getName()); // If there was no param with the same name, or the // param with the same name had a different value, // then no match. - result = (includedParam != null && param.getValue() - .equals(includedParam.getValue())); + result = + (includedParam != null + && param.getValue().equals(includedParam.getValue())); } } } else if (getSubType().equals("*")) { result = true; - } else if (getSubType().startsWith("*+") && includedMediaType - .getSubType().endsWith(getSubType().substring(2))) { + } else if (getSubType().startsWith("*+") + && includedMediaType.getSubType().endsWith(getSubType().substring(2))) { result = true; } } @@ -1028,13 +994,12 @@ public boolean includes(Metadata included, boolean ignoreParameters) { } /** - * Checks if the current media type is concrete. A media type is concrete if - * neither the main type nor the sub-type are equal to "*". - * + * Checks if the current media type is concrete. A media type is concrete if neither the main + * type nor the subtype are equal to "*". + * * @return True if this media type is concrete. */ public boolean isConcrete() { return !getName().contains("*"); } - } diff --git a/org.restlet/src/main/java/org/restlet/data/Metadata.java b/org.restlet/src/main/java/org/restlet/data/Metadata.java index 1063cb541c..a9aa88402c 100644 --- a/org.restlet/src/main/java/org/restlet/data/Metadata.java +++ b/org.restlet/src/main/java/org/restlet/data/Metadata.java @@ -1,136 +1,133 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; /** - * Representations metadata for content negotiation. "Metadata is in the form of - * name-value pairs, where the name corresponds to a standard that defines the - * value's structure and semantics. Response messages may include both - * representation metadata and resource metadata: information about the resource - * that is not specific to the supplied representation." Roy T. Fielding - * + * Representations metadata for content negotiation. "Metadata is in the form of name-value pairs, + * where the name corresponds to a standard that defines the value's structure and semantics. + * Response messages may include both representation metadata and resource metadata: information + * about the resource that is not specific to the supplied representation." Roy T. Fielding + * * @see Preference - * @see Source dissertation + * @see Source + * dissertation * @author Jerome Louvel */ public abstract class Metadata { - /** The description of this metadata. */ - private final String description; + /** The description of this metadata. */ + private final String description; - /** The metadata name like "text/html" or "compress" or "iso-8851-1". */ - private final String name; + /** The metadata name like "text/html" or "compress" or "iso-8851-1". */ + private final String name; - /** - * Constructor. - * - * @param name The unique name. - */ - public Metadata(String name) { - this(name, null); - } + /** + * Constructor. + * + * @param name The unique name. + */ + public Metadata(String name) { + this(name, null); + } - /** - * Constructor. - * - * @param name The unique name. - * @param description The description. - */ - public Metadata(String name, String description) { - this.name = name; - this.description = description; - } + /** + * Constructor. + * + * @param name The unique name. + * @param description The description. + */ + public Metadata(String name, String description) { + this.name = name; + this.description = description; + } - /** {@inheritDoc} */ - @Override - public boolean equals(Object object) { - return (object instanceof Metadata) && ((Metadata) object).getName().equals(getName()); - } + /** {@inheritDoc} */ + @Override + public boolean equals(Object object) { + return (object instanceof Metadata) && ((Metadata) object).getName().equals(getName()); + } - /** - * Returns the description. - * - * @return The description. - */ - public String getDescription() { - return this.description; - } + /** + * Returns the description. + * + * @return The description. + */ + public String getDescription() { + return this.description; + } - /** - * Returns the name (ex: "text/html" or "compress" or "iso-8851-1"). - * - * @return The name (ex: "text/html" or "compress" or "iso-8851-1"). - */ - public String getName() { - return this.name; - } + /** + * Returns the name (ex: "text/html" or "compress" or "iso-8851-1"). + * + * @return The name (ex: "text/html" or "compress" or "iso-8851-1"). + */ + public String getName() { + return this.name; + } - /** - * Returns the parent metadata if available or null. - * - * @return The parent metadata. - */ - public abstract Metadata getParent(); + /** + * Returns the parent metadata if available or null. + * + * @return The parent metadata. + */ + public abstract Metadata getParent(); - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (getName() == null) ? 0 : getName().hashCode(); - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (getName() == null) ? 0 : getName().hashCode(); + } - /** - * Indicates if a given metadata is included in the current one. The test is - * true if both metadata are equal or if the given metadata is within the range - * of the current one. For example, {@link MediaType#ALL} includes all media - * types. - *

- * Examples: - *

    - *
  • TEXT_ALL.includes(TEXT_PLAIN) returns true
  • - *
  • TEXT_PLAIN.includes(TEXT_ALL) returns false
  • - *
- * - * @param included The metadata to test for inclusion. - * @return True if the given metadata is included in the current one. - * @see #isCompatible(Metadata) - */ - public abstract boolean includes(Metadata included); + /** + * Indicates if a given metadata is included in the current one. The test is true if both + * metadata is equal or if the given metadata is within the range of the current one. For + * example, {@link MediaType#ALL} includes all media types. + * + *

Examples: + * + *

    + *
  • TEXT_ALL.includes(TEXT_PLAIN) returns true + *
  • TEXT_PLAIN.includes(TEXT_ALL) returns false + *
+ * + * @param included The metadata to test for inclusion. + * @return True if the given metadata is included in the current one. + * @see #isCompatible(Metadata) + */ + public abstract boolean includes(Metadata included); - /** - * Checks if this metadata is compatible with the given metadata. - *

- * Examples: - *

    - *
  • TEXT_ALL.isCompatible(TEXT_PLAIN) returns true
  • - *
  • TEXT_PLAIN.isCompatible(TEXT_ALL) returns true
  • - *
  • TEXT_PLAIN.isCompatible(APPLICATION_ALL) returns false
  • - *
- * - * @param otherMetadata The other metadata to compare. - * @return True if the metadata are compatible. - * @see #includes(Metadata) - */ - public boolean isCompatible(Metadata otherMetadata) { - return (otherMetadata != null) - && (includes(otherMetadata) || otherMetadata.includes(this)); - } + /** + * Checks if this metadata is compatible with the given metadata. + * + *

Examples: + * + *

    + *
  • TEXT_ALL.isCompatible(TEXT_PLAIN) returns true + *
  • TEXT_PLAIN.isCompatible(TEXT_ALL) returns true + *
  • TEXT_PLAIN.isCompatible(APPLICATION_ALL) returns false + *
+ * + * @param otherMetadata The other metadata to compare. + * @return True if the metadata is compatible. + * @see #includes(Metadata) + */ + public boolean isCompatible(Metadata otherMetadata) { + return (otherMetadata != null) && (includes(otherMetadata) || otherMetadata.includes(this)); + } - /** - * Returns the metadata name. - * - * @return The metadata name. - */ - @Override - public String toString() { - return getName(); - } + /** + * Returns the metadata name. + * + * @return The metadata name. + */ + @Override + public String toString() { + return getName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Method.java b/org.restlet/src/main/java/org/restlet/data/Method.java index 0ee42f8729..1ea39c5460 100644 --- a/org.restlet/src/main/java/org/restlet/data/Method.java +++ b/org.restlet/src/main/java/org/restlet/data/Method.java @@ -1,371 +1,388 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.engine.Engine; - -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.restlet.engine.Engine; /** * Method to execute when handling a call. - * + * * @author Jerome Louvel */ public final class Method implements Comparable { - /** Map of registered methods. */ - private static final Map _methods = new ConcurrentHashMap(); - - /** - * Pseudo-method use to match all methods. - */ - public static final Method ALL = new Method("*", "Pseudo-method use to match all methods."); - - private static final String BASE_HTTP = "http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html"; - - /** - * Used with a proxy that can dynamically switch to being a tunnel. - * - * @see HTTP RFC - * - 9.9 CONNECT - */ - public static final Method CONNECT = new Method("CONNECT", - "Used with a proxy that can dynamically switch to being a tunnel", BASE_HTTP + "#sec9.9", false, false); - - /** - * Requests that the origin server deletes the resource identified by the - * request URI. - * - * @see HTTP RFC - * - 9.7 DELETE - */ - public static final Method DELETE = new Method("DELETE", - "Requests that the origin server deletes the resource identified by the request URI", BASE_HTTP + "#sec9.7", - false, true); - - /** - * Retrieves whatever information (in the form of an entity) that is identified - * by the request URI. - * - * @see HTTP RFC - * - 9.3 GET - */ - public static final Method GET = new Method("GET", - "Retrieves whatever information (in the form of an entity) that is identified by the request URI", - BASE_HTTP + "#sec9.3", true, true); - - /** - * Identical to GET except that the server must not return a message body in the - * response but only the message header. - * - * @see HTTP RFC - * - 9.4 HEAD - */ - public static final Method HEAD = new Method("HEAD", - "Identical to GET except that the server must not return a message body in the response", - BASE_HTTP + "#sec9.4", true, true); - - /** - * Requests for information about the communication options available on the - * request/response chain identified by the URI. - * - * @see HTTP RFC - * - 9.2 OPTIONS - */ - public static final Method OPTIONS = new Method("OPTIONS", - "Requests for information about the communication options available on the request/response chain identified by the URI", - BASE_HTTP + "#sec9.2", true, true); - - /** - * Requests that the origin server applies partial modifications contained in - * the entity enclosed in the request to the resource identified by the request - * URI. - * - * @see HTTP PATCH RFC 5789 - */ - public static final Method PATCH = new Method("PATCH", - "Requests that the origin server applies partial modifications to the resource identified by the request URI", - "http://tools.ietf.org/html/rfc5789", false, false); - - /** - * Requests that the origin server accepts the entity enclosed in the request as - * a new subordinate of the resource identified by the request URI. - * - * @see HTTP RFC - * - 9.5 POST - */ - public static final Method POST = new Method("POST", - "Requests that the origin server accepts the entity enclosed in the request as a new subordinate of the resource identified by the request URI", - BASE_HTTP + "#sec9.5", false, false); - - /** - * Requests that the enclosed entity be stored under the supplied request URI. - * - * @see - */ - public static final Method PUT = new Method("PUT", - "Requests that the enclosed entity be stored under the supplied request URI", BASE_HTTP + "#sec9.6", false, - true); - - /** - * Used to invoke a remote, application-layer loop-back of the request message. - * - * @see HTTP RFC - * - 9.8 TRACE - */ - public static final Method TRACE = new Method("TRACE", - "Used to invoke a remote, application-layer loop-back of the request message", BASE_HTTP + "#sec9.8", true, - true); - - /** - * Adds a new Method to the list of registered methods. - * - * @param method The method to register. - */ - public static void register(Method method) { - String name = (method == null) ? null : method.getName().toLowerCase(); - if ((name != null) && !name.isEmpty()) { - _methods.put(name, method); - } - } - - /** - * Sorts the given list of methods by name. - * - * @param methods The methods to sort. - */ - public static void sort(List methods) { - Collections.sort(methods, new Comparator() { - public int compare(Method m1, Method m2) { - return m1.getName().compareTo(m2.getName()); - } - }); - } - - /** - * Returns the method associated to a given method name. If an existing constant - * exists then it is returned, otherwise a new instance is created. - * - * @param name The method name. - * @return The associated method. - */ - public static Method valueOf(final String name) { - Method result = null; - - if ((name != null) && !name.isEmpty()) { - result = Method._methods.get(name.toLowerCase()); - if (result == null) { - result = new Method(name); - } - } - - return result; - } - - /** The description. */ - private final String description; - - /** - * Indicates if the side effects of several requests is the same as a single - * request. - */ - private volatile boolean idempotent; - - /** The name. */ - private volatile String name; - - /** Indicates if the method replies with a response. */ - private final boolean replying; - - /** - * Indicates if it should have the significance of taking an action other than - * retrieval. - */ - private final boolean safe; - - /** The URI of the specification describing the method. */ - private volatile String uri; - - static { - // Let the engine register all methods (the default ones and the ones to - // be discovered) as soon as the Method class is loaded or at least - // used. - Engine.getInstance(); - } - - /** - * Constructor for unsafe and non idempotent methods. - * - * @param name The technical name of the method. - * @see org.restlet.data.Method#valueOf(String) - */ - public Method(final String name) { - this(name, null); - } - - /** - * Constructor for unsafe and non idempotent methods. - * - * @param name The technical name of the method. - * @param description The description. - * @see org.restlet.data.Method#valueOf(String) - */ - public Method(String name, String description) { - this(name, description, null, false, false); - } - - /** - * Constructor for unsafe and non idempotent methods. - * - * @param name The technical name. - * @param description The description. - * @param uri The URI of the specification describing the method. - * @see org.restlet.data.Method#valueOf(String) - */ - public Method(String name, String description, String uri) { - this(name, description, uri, false, false); - } - - /** - * Constructor for methods that reply to requests with responses. - * - * @param name The technical name. - * @param description The description. - * @param uri The URI of the specification describing the method. - * @param safe Indicates if the method is safe. - * @param idempotent Indicates if the method is idempotent. - * @see org.restlet.data.Method#valueOf(String) - */ - public Method(String name, String description, String uri, boolean safe, boolean idempotent) { - this(name, description, uri, safe, idempotent, true); - } - - /** - * Constructor. - * - * @param name The technical name. - * @param description The description. - * @param uri The URI of the specification describing the method. - * @param safe Indicates if the method is safe. - * @param idempotent Indicates if the method is idempotent. - * @param replying Indicates if the method replies with a response. - * @see org.restlet.data.Method#valueOf(String) - */ - public Method(String name, String description, String uri, boolean safe, boolean idempotent, boolean replying) { - this.name = name; - this.description = description; - this.uri = uri; - this.safe = safe; - this.idempotent = idempotent; - this.replying = replying; - } - - /** - * Compares this method to another. Based on the method name. - * - * @param o The other method. - */ - public int compareTo(Method o) { - if (o != null) { - return this.getName().compareTo(o.getName()); - } - return 1; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(final Object object) { - return (object instanceof Method) && ((Method) object).getName().equals(getName()); - } - - /** - * Returns the description. - * - * @return The description. - */ - public String getDescription() { - return this.description; - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the URI of the specification describing the method. - * - * @return The URI of the specification describing the method. - */ - public String getUri() { - return this.uri; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (getName() == null) ? 0 : getName().hashCode(); - } - - /** - * Indicates if the side-effects of several requests is the same as a single - * request. - * - * @return True if the method is idempotent. - */ - public boolean isIdempotent() { - return idempotent; - } - - /** - * Indicates if the method replies with a response. - * - * @return True if the method replies with a response. - */ - public boolean isReplying() { - return replying; - } - - /** - * Indicates if it should have the significance of taking an action other than - * retrieval. - * - * @return True if the method is safe. - */ - public boolean isSafe() { - return safe; - } - - /** - * Returns the name. - * - * @return The name. - */ - @Override - public String toString() { - return getName(); - } + /** Map of registered methods. */ + private static final Map _methods = new ConcurrentHashMap(); + + /** Pseudo-method use to match all methods. */ + public static final Method ALL = new Method("*", "Pseudo-method use to match all methods."); + + private static final String BASE_HTTP = "http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html"; + + /** + * Used with a proxy that can dynamically switch to being a tunnel. + * + * @see HTTP RFC - 9.9 + * CONNECT + */ + public static final Method CONNECT = + new Method( + "CONNECT", + "Used with a proxy that can dynamically switch to being a tunnel", + BASE_HTTP + "#sec9.9", + false, + false); + + /** + * Requests that the origin server deletes the resource identified by the request URI. + * + * @see HTTP RFC - 9.7 + * DELETE + */ + public static final Method DELETE = + new Method( + "DELETE", + "Requests that the origin server deletes the resource identified by the request URI", + BASE_HTTP + "#sec9.7", + false, + true); + + /** + * Retrieves whatever information (in the form of an entity) that is identified by the request + * URI. + * + * @see HTTP RFC - 9.3 + * GET + */ + public static final Method GET = + new Method( + "GET", + "Retrieves whatever information (in the form of an entity) that is identified by the request URI", + BASE_HTTP + "#sec9.3", + true, + true); + + /** + * Identical to GET except that the server must not return a message body in the response but + * only the message header. + * + * @see HTTP RFC - 9.4 + * HEAD + */ + public static final Method HEAD = + new Method( + "HEAD", + "Identical to GET except that the server must not return a message body in the response", + BASE_HTTP + "#sec9.4", + true, + true); + + /** + * Requests for information about the communication options available on the request/response + * chain identified by the URI. + * + * @see HTTP RFC - 9.2 + * OPTIONS + */ + public static final Method OPTIONS = + new Method( + "OPTIONS", + "Requests for information about the communication options available on the request/response chain identified by the URI", + BASE_HTTP + "#sec9.2", + true, + true); + + /** + * Requests that the origin server applies partial modifications contained in the entity + * enclosed in the request to the resource identified by the request URI. + * + * @see HTTP PATCH RFC 5789 + */ + public static final Method PATCH = + new Method( + "PATCH", + "Requests that the origin server applies partial modifications to the resource identified by the request URI", + "http://tools.ietf.org/html/rfc5789", + false, + false); + + /** + * Requests that the origin server accepts the entity enclosed in the request as a new + * subordinate of the resource identified by the request URI. + * + * @see HTTP RFC - 9.5 + * POST + */ + public static final Method POST = + new Method( + "POST", + "Requests that the origin server accepts the entity enclosed in the request as a new subordinate of the resource identified by the request URI", + BASE_HTTP + "#sec9.5", + false, + false); + + /** + * Requests that the enclosed entity be stored under the supplied request URI. + * + * @see + */ + public static final Method PUT = + new Method( + "PUT", + "Requests that the enclosed entity be stored under the supplied request URI", + BASE_HTTP + "#sec9.6", + false, + true); + + /** + * Used to invoke a remote, application-layer loop-back of the request message. + * + * @see HTTP RFC - 9.8 + * TRACE + */ + public static final Method TRACE = + new Method( + "TRACE", + "Used to invoke a remote, application-layer loop-back of the request message", + BASE_HTTP + "#sec9.8", + true, + true); + + /** + * Adds a new Method to the list of registered methods. + * + * @param method The method to register. + */ + public static void register(Method method) { + String name = (method == null) ? null : method.getName().toLowerCase(); + if ((name != null) && !name.isEmpty()) { + _methods.put(name, method); + } + } + + /** + * Sorts the given list of methods by name. + * + * @param methods The methods to sort. + */ + public static void sort(List methods) { + methods.sort(Comparator.comparing(Method::getName)); + } + + /** + * Returns the method associated with a given method name. If an existing constant exists, then + * it is returned, otherwise a new instance is created. + * + * @param name The method name. + * @return The associated method. + */ + public static Method valueOf(final String name) { + Method result = null; + + if ((name != null) && !name.isEmpty()) { + result = Method._methods.get(name.toLowerCase()); + if (result == null) { + result = new Method(name); + } + } + + return result; + } + + /** The description. */ + private final String description; + + /** Indicates if the side effects of several requests is the same as a single request. */ + private volatile boolean idempotent; + + /** The name. */ + private volatile String name; + + /** Indicates if the method replies with a response. */ + private final boolean replying; + + /** Indicates if it should have the significance of taking an action other than retrieval. */ + private final boolean safe; + + /** The URI of the specification describing the method. */ + private volatile String uri; + + static { + // Let the engine register all methods (the default ones and the ones to + // be discovered) as soon as the Method class is loaded or at least + // used. + Engine.getInstance(); + } + + /** + * Constructor for unsafe and non-idempotent methods. + * + * @param name The technical name of the method. + * @see org.restlet.data.Method#valueOf(String) + */ + public Method(final String name) { + this(name, null); + } + + /** + * Constructor for unsafe and non-idempotent methods. + * + * @param name The technical name of the method. + * @param description The description. + * @see org.restlet.data.Method#valueOf(String) + */ + public Method(String name, String description) { + this(name, description, null, false, false); + } + + /** + * Constructor for unsafe and non-idempotent methods. + * + * @param name The technical name. + * @param description The description. + * @param uri The URI of the specification describing the method. + * @see org.restlet.data.Method#valueOf(String) + */ + public Method(String name, String description, String uri) { + this(name, description, uri, false, false); + } + + /** + * Constructor for methods that reply to requests with responses. + * + * @param name The technical name. + * @param description The description. + * @param uri The URI of the specification describing the method. + * @param safe Indicates if the method is safe. + * @param idempotent Indicates if the method is idempotent. + * @see org.restlet.data.Method#valueOf(String) + */ + public Method(String name, String description, String uri, boolean safe, boolean idempotent) { + this(name, description, uri, safe, idempotent, true); + } + + /** + * Constructor. + * + * @param name The technical name. + * @param description The description. + * @param uri The URI of the specification describing the method. + * @param safe Indicates if the method is safe. + * @param idempotent Indicates if the method is idempotent. + * @param replying Indicates if the method replies with a response. + * @see org.restlet.data.Method#valueOf(String) + */ + public Method( + String name, + String description, + String uri, + boolean safe, + boolean idempotent, + boolean replying) { + this.name = name; + this.description = description; + this.uri = uri; + this.safe = safe; + this.idempotent = idempotent; + this.replying = replying; + } + + /** + * Compares this method to another. Based on the method name. + * + * @param o The other method. + */ + public int compareTo(Method o) { + if (o != null) { + return this.getName().compareTo(o.getName()); + } + return 1; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object object) { + return (object instanceof Method) && ((Method) object).getName().equals(getName()); + } + + /** + * Returns the description. + * + * @return The description. + */ + public String getDescription() { + return this.description; + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the URI of the specification describing the method. + * + * @return The URI of the specification describing the method. + */ + public String getUri() { + return this.uri; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (getName() == null) ? 0 : getName().hashCode(); + } + + /** + * Indicates if the side effects of several requests is the same as a single request. + * + * @return True if the method is idempotent. + */ + public boolean isIdempotent() { + return idempotent; + } + + /** + * Indicates if the method replies with a response. + * + * @return True if the method replies with a response. + */ + public boolean isReplying() { + return replying; + } + + /** + * Indicates if it should have the significance of taking an action other than retrieval. + * + * @return True if the method is safe. + */ + public boolean isSafe() { + return safe; + } + + /** + * Returns the name. + * + * @return The name. + */ + @Override + public String toString() { + return getName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Parameter.java b/org.restlet/src/main/java/org/restlet/data/Parameter.java index 8334d8d17f..ad6bae9654 100644 --- a/org.restlet/src/main/java/org/restlet/data/Parameter.java +++ b/org.restlet/src/main/java/org/restlet/data/Parameter.java @@ -1,41 +1,37 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import java.io.IOException; +import java.util.Objects; import org.restlet.engine.util.SystemUtils; import org.restlet.util.NamedValue; import org.restlet.util.Series; -import java.io.IOException; -import java.util.Objects; - /** - * Multi-usage parameter. Note that the name and value properties are thread - * safe, stored in volatile members. - * + * Multi-usage parameter. Note that the name and value properties are thread-safe, stored in + * volatile members. + * * @author Jerome Louvel */ public class Parameter implements Comparable, NamedValue { - /** The first object. */ - private volatile String name; + /** The first object. */ + private volatile String name; - /** The second object. */ - private volatile String value; + /** The second object. */ + private volatile String value; /** - * Creates a series that includes the current parameter as the initial - * entry. - * - * @return A series that includes the current parameter as the initial - * entry. + * Creates a series that includes the current parameter as the initial entry. + * + * @return A series that includes the current parameter as the initial entry. */ public Series createSeries() { Series result = new Form(); @@ -43,139 +39,135 @@ public Series createSeries() { return result; } - /** - * Creates a parameter. - * - * @param name The parameter name buffer. - * @param value The parameter value buffer (can be null). - * @return The created parameter. - * @throws IOException - */ - public static Parameter create(CharSequence name, CharSequence value) { - if (value != null) { - return new Parameter(name.toString(), value.toString()); - } else { - return new Parameter(name.toString(), null); - } - } - - /** - * Default constructor. - */ - public Parameter() { - this(null, null); - } - - /** - * Preferred constructor. - * - * @param name The name. - * @param value The value. - */ - public Parameter(String name, String value) { - this.name = name; - this.value = value; - } - - /* - * (non-Javadoc) - * - * @see org.restlet.data.NamedValue#compareTo(org.restlet.data.Parameter) - */ - public int compareTo(Parameter o) { - return getName().compareTo(o.getName()); - } - - /** - * Encodes the parameter into the target buffer. - * - * @param buffer The target buffer. - * @param characterSet The character set to use. - * @throws IOException - */ - public void encode(Appendable buffer, CharacterSet characterSet) throws IOException { - if (getName() != null) { - buffer.append(Reference.encode(getName(), characterSet)); - - if (getValue() != null) { - buffer.append('='); - buffer.append(Reference.encode(getValue(), characterSet)); - } - } - } - - /** - * Encodes the parameter as a string. - * - * @param characterSet The character set to use. - * @return The encoded string? - * @throws IOException - */ - public String encode(CharacterSet characterSet) throws IOException { - StringBuilder sb = new StringBuilder(); - encode(sb, characterSet); - return sb.toString(); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } + /** + * Creates a parameter. + * + * @param name The parameter name buffer. + * @param value The parameter value buffer (can be null). + * @return The created parameter. + * @throws IOException + */ + public static Parameter create(CharSequence name, CharSequence value) { + if (value != null) { + return new Parameter(name.toString(), value.toString()); + } else { + return new Parameter(name.toString(), null); + } + } + + /** Default constructor. */ + public Parameter() { + this(null, null); + } + + /** + * Preferred constructor. + * + * @param name The name. + * @param value The value. + */ + public Parameter(String name, String value) { + this.name = name; + this.value = value; + } + + /* + * (non-Javadoc) + * + * @see org.restlet.data.NamedValue#compareTo(org.restlet.data.Parameter) + */ + public int compareTo(Parameter o) { + return getName().compareTo(o.getName()); + } + + /** + * Encodes the parameter into the target buffer. + * + * @param buffer The target buffer. + * @param characterSet The character set to use. + * @throws IOException + */ + public void encode(Appendable buffer, CharacterSet characterSet) throws IOException { + if (getName() != null) { + buffer.append(Reference.encode(getName(), characterSet)); + + if (getValue() != null) { + buffer.append('='); + buffer.append(Reference.encode(getValue(), characterSet)); + } + } + } + + /** + * Encodes the parameter as a string. + * + * @param characterSet The character set to use. + * @return The encoded string? + * @throws IOException + */ + public String encode(CharacterSet characterSet) throws IOException { + StringBuilder sb = new StringBuilder(); + encode(sb, characterSet); + return sb.toString(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (obj instanceof Parameter that) { return Objects.equals(getName(), that.getName()) && Objects.equals(getValue(), that.getValue()); } return false; + } + /* + * (non-Javadoc) + * + * @see org.restlet.data.NamedValue#getName() + */ + public String getName() { + return this.name; } - /* - * (non-Javadoc) - * - * @see org.restlet.data.NamedValue#getName() - */ - public String getName() { - return this.name; - } - - /* - * (non-Javadoc) - * - * @see org.restlet.data.NamedValue#getValue() - */ - public String getValue() { - return this.value; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return SystemUtils.hashCode(getName(), getValue()); - } - - /* - * (non-Javadoc) - * - * @see org.restlet.data.NamedValue#setName(java.lang.String) - */ - public void setName(String name) { - this.name = name; - } - - /* - * (non-Javadoc) - * - * @see org.restlet.data.NamedValue#setValue(java.lang.String) - */ - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "[" + getName() + "=" + getValue() + "]"; - } + /* + * (non-Javadoc) + * + * @see org.restlet.data.NamedValue#getValue() + */ + public String getValue() { + return this.value; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return SystemUtils.hashCode(getName(), getValue()); + } + + /* + * (non-Javadoc) + * + * @see org.restlet.data.NamedValue#setName(java.lang.String) + */ + public void setName(String name) { + this.name = name; + } + /* + * (non-Javadoc) + * + * @see org.restlet.data.NamedValue#setValue(java.lang.String) + */ + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "[" + getName() + "=" + getValue() + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Preference.java b/org.restlet/src/main/java/org/restlet/data/Preference.java index 8d97568de8..6780331856 100644 --- a/org.restlet/src/main/java/org/restlet/data/Preference.java +++ b/org.restlet/src/main/java/org/restlet/data/Preference.java @@ -1,137 +1,133 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import org.restlet.util.Series; /** * Metadata preference definition. - * + * * @author Jerome Louvel */ public final class Preference { - /** The metadata associated with this preference. */ - private volatile T metadata; - - /** The modifiable list of parameters. */ - private volatile Series parameters; - - /** The quality/preference level. */ - private volatile float quality; - - /** - * Constructor. - */ - public Preference() { - this(null, 1F, null); - } - - /** - * Constructor. - * - * @param metadata The associated metadata. - */ - public Preference(T metadata) { - this(metadata, 1F, null); - } - - /** - * Constructor. - * - * @param metadata The associated metadata. - * @param quality The quality/preference level. - */ - public Preference(T metadata, float quality) { - this(metadata, quality, null); - } - - /** - * Constructor. - * - * @param metadata The associated metadata. - * @param quality The quality/preference level. - * @param parameters The list of parameters. - */ - public Preference(T metadata, float quality, Series parameters) { - this.metadata = metadata; - this.quality = quality; - this.parameters = parameters; - } - - /** - * Returns the metadata associated with this preference. - * - * @return The metadata associated with this preference. - */ - public T getMetadata() { - return this.metadata; - } - - /** - * Returns the modifiable list of parameters. Creates a new instance if no one - * has been set. - * - * @return The modifiable list of parameters. - */ - public Series getParameters() { - // Lazy initialization with double-check. - Series p = this.parameters; - if (p == null) { - synchronized (this) { - p = this.parameters; - if (p == null) { - this.parameters = p = new Series(Parameter.class); - } - } - } - return p; - } - - /** - * Returns the quality/preference level. - * - * @return The quality/preference level. - */ - public float getQuality() { - return this.quality; - } - - /** - * Sets the metadata associated with this preference. - * - * @param metadata The metadata associated with this preference. - */ - public void setMetadata(T metadata) { - this.metadata = metadata; - } - - /** - * Sets the modifiable list of parameters. - * - * @param parameters The modifiable list of parameters. - */ - public void setParameters(Series parameters) { - this.parameters = parameters; - } - - /** - * Sets the quality/preference level. - * - * @param quality The quality/preference level. - */ - public void setQuality(float quality) { - this.quality = quality; - } - - @Override - public String toString() { - return (getMetadata() == null) ? "" : (getMetadata().getName() + ":" + getQuality()); - } + /** The metadata associated with this preference. */ + private volatile T metadata; + + /** The modifiable list of parameters. */ + private volatile Series parameters; + + /** The quality/preference level. */ + private volatile float quality; + + /** Constructor. */ + public Preference() { + this(null, 1F, null); + } + + /** + * Constructor. + * + * @param metadata The associated metadata. + */ + public Preference(T metadata) { + this(metadata, 1F, null); + } + + /** + * Constructor. + * + * @param metadata The associated metadata. + * @param quality The quality/preference level. + */ + public Preference(T metadata, float quality) { + this(metadata, quality, null); + } + + /** + * Constructor. + * + * @param metadata The associated metadata. + * @param quality The quality/preference level. + * @param parameters The list of parameters. + */ + public Preference(T metadata, float quality, Series parameters) { + this.metadata = metadata; + this.quality = quality; + this.parameters = parameters; + } + + /** + * Returns the metadata associated with this preference. + * + * @return The metadata associated with this preference. + */ + public T getMetadata() { + return this.metadata; + } + + /** + * Returns the modifiable list of parameters. Creates a new instance if no one has been set. + * + * @return The modifiable list of parameters. + */ + public Series getParameters() { + // Lazy initialization with double-check. + Series p = this.parameters; + if (p == null) { + synchronized (this) { + p = this.parameters; + if (p == null) { + this.parameters = p = new Series(Parameter.class); + } + } + } + return p; + } + + /** + * Returns the quality/preference level. + * + * @return The quality/preference level. + */ + public float getQuality() { + return this.quality; + } + + /** + * Sets the metadata associated with this preference. + * + * @param metadata The metadata associated with this preference. + */ + public void setMetadata(T metadata) { + this.metadata = metadata; + } + + /** + * Sets the modifiable list of parameters. + * + * @param parameters The modifiable list of parameters. + */ + public void setParameters(Series parameters) { + this.parameters = parameters; + } + + /** + * Sets the quality/preference level. + * + * @param quality The quality/preference level. + */ + public void setQuality(float quality) { + this.quality = quality; + } + + @Override + public String toString() { + return (getMetadata() == null) ? "" : (getMetadata().getName() + ":" + getQuality()); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Product.java b/org.restlet/src/main/java/org/restlet/data/Product.java index 034573b151..f21e438384 100644 --- a/org.restlet/src/main/java/org/restlet/data/Product.java +++ b/org.restlet/src/main/java/org/restlet/data/Product.java @@ -1,101 +1,97 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; /** - * Product tokens are used to allow communicating applications to identify - * themselves by software name and version. - * + * Product tokens are used to allow communicating applications to identify themselves by software + * name and version. + * * @author Thierry Boileau - * @see User-Agent - * @see Product - * Tokens + * @see User-Agent + * @see Product Tokens */ public class Product { - /** Comment. */ - private volatile String comment; + /** Comment. */ + private volatile String comment; - /** Product name. */ - private volatile String name; + /** Product name. */ + private volatile String name; - /** Version number. */ - private volatile String version; + /** Version number. */ + private volatile String version; - /** - * Constructor. - * - * @param name The product name. - * @param version The product version. - * @param comment The product comment. - */ - public Product(String name, String version, String comment) { - super(); - this.name = name; - this.version = version; - this.comment = comment; - } + /** + * Constructor. + * + * @param name The product name. + * @param version The product version. + * @param comment The product comment. + */ + public Product(String name, String version, String comment) { + super(); + this.name = name; + this.version = version; + this.comment = comment; + } - /** - * Returns the facultative comment. - * - * @return The facultative comment. - */ - public String getComment() { - return this.comment; - } + /** + * Returns the facultative comment. + * + * @return The facultative comment. + */ + public String getComment() { + return this.comment; + } - /** - * Returns the product name. - * - * @return The product name. - */ - public String getName() { - return this.name; - } + /** + * Returns the product name. + * + * @return The product name. + */ + public String getName() { + return this.name; + } - /** - * Returns the version of the product. - * - * @return The version of the product. - */ - public String getVersion() { - return this.version; - } + /** + * Returns the version of the product. + * + * @return The version of the product. + */ + public String getVersion() { + return this.version; + } - /** - * Sets the facultative comment. - * - * @param comment The facultative comment. - */ - public void setComment(String comment) { - this.comment = comment; - } + /** + * Sets the facultative comment. + * + * @param comment The facultative comment. + */ + public void setComment(String comment) { + this.comment = comment; + } - /** - * Sets the product name. - * - * @param name The product name. - */ - public void setName(String name) { - this.name = name; - } + /** + * Sets the product name. + * + * @param name The product name. + */ + public void setName(String name) { + this.name = name; + } - /** - * Sets the version of the product. - * - * @param version The version of the product. - */ - public void setVersion(String version) { - this.version = version; - } + /** + * Sets the version of the product. + * + * @param version The version of the product. + */ + public void setVersion(String version) { + this.version = version; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Protocol.java b/org.restlet/src/main/java/org/restlet/data/Protocol.java index a87ed066a5..dfc2f30dc0 100644 --- a/org.restlet/src/main/java/org/restlet/data/Protocol.java +++ b/org.restlet/src/main/java/org/restlet/data/Protocol.java @@ -1,349 +1,384 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import org.restlet.engine.util.StringUtils; /** - * Protocol used by client and server connectors. Connectors enable the - * communication between components by implementing standard protocols. - * + * Protocol used by client and server connectors. Connectors enable the communication between + * components by implementing standard protocols. + * * @author Jerome Louvel */ public final class Protocol { - /** Indicates that the port number is undefined. */ - public static final int UNKNOWN_PORT = -1; - - /** All-protocols wildcard. */ - public static final Protocol ALL = new Protocol("all", "ALL", "Wildcard for all protocols", UNKNOWN_PORT); - - /** - * CLAP (ClassLoader Access Protocol) is a custom scheme to access to - * representations via classloaders. Example URI: - * "clap://thread/org/restlet/Restlet.class".
- *
- * In order to work, CLAP requires a client connector provided by the core - * Restlet engine. - * - * @see org.restlet.data.LocalReference - */ - public static final Protocol CLAP = new Protocol("clap", "CLAP", "Class Loader Access Protocol", UNKNOWN_PORT, - true); - - /** - * FILE is a standard scheme to access to representations stored in the file - * system (locally most of the time). Example URI: - * "file:///D/root/index.html".
- *
- * In order to work, FILE requires a client connector provided by the core - * Restlet engine. - * - * @see org.restlet.data.LocalReference - */ - public static final Protocol FILE = new Protocol("file", "FILE", "Local File System Protocol", UNKNOWN_PORT, true); - - /** FTP protocol. */ - public static final Protocol FTP = new Protocol("ftp", "FTP", "File Transfer Protocol", 21); - - /** HTTP protocol. */ - public static final Protocol HTTP = new Protocol("http", "HTTP", "HyperText Transport Protocol", 80, "1.1"); - - /** HTTPS protocol (via SSL socket). */ - public static final Protocol HTTPS = new Protocol("https", "HTTPS", "HTTP", "HyperText Transport Protocol (Secure)", - 443, true, "1.1"); - - /** - * JAR (Java ARchive) is a common scheme to access to representations inside - * archive files. Example URI: - * "jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class". - * - * @see org.restlet.data.LocalReference#createJarReference(Reference, String) - */ - public static final Protocol JAR = new Protocol("jar", "JAR", "Java ARchive", UNKNOWN_PORT, true); - - /** JDBC protocol. */ - public static final Protocol JDBC = new Protocol("jdbc", "JDBC", "Java DataBase Connectivity", UNKNOWN_PORT); - - /** - * RIAP (Restlet Internal Access Protocol) is a custom scheme to access - * representations via internal calls to virtual hosts/components. Example URIs: - * "riap://component/myAppPath/myResource" and - * "riap://application/myResource".
- *
- * In order to work, RIAP doesn't require any client connector and is - * automatically supported by the Restlet engine. - * - * @see org.restlet.data.LocalReference - */ - public static final Protocol RIAP = new Protocol("riap", "RIAP", "Restlet Internal Access Protocol", UNKNOWN_PORT, - true); - - /** Local Web Archive access protocol. */ - public static final Protocol WAR = new Protocol("war", "WAR", "Web Archive Access Protocol", UNKNOWN_PORT, true); - - /** - * ZIP is a special scheme to access to representations inside Zip archive - * files. Example URI: "zip:file:///tmp/test.zip!/test.txt". - * - * @see org.restlet.data.LocalReference#createZipReference(Reference, String) - */ - public static final Protocol ZIP = new Protocol("zip", "ZIP", "Zip Archive Access Protocol", UNKNOWN_PORT, true); - - /** - * Creates the protocol associated to a URI scheme name. If an existing constant - * exists then it is returned, otherwise a new instance is created. - * - * @param name The scheme name. - * @return The associated protocol. - */ - public static Protocol valueOf(String name) { - Protocol result = null; - - if (!StringUtils.isNullOrEmpty(name)) { - if (name.equalsIgnoreCase(CLAP.getSchemeName())) { - result = CLAP; - } else if (name.equalsIgnoreCase(FILE.getSchemeName())) { - result = FILE; - } else if (name.equalsIgnoreCase(FTP.getSchemeName())) { - result = FTP; - } else if (name.equalsIgnoreCase(HTTP.getSchemeName())) { - result = HTTP; - } else if (name.equalsIgnoreCase(HTTPS.getSchemeName())) { - result = HTTPS; - } else if (name.equalsIgnoreCase(JAR.getSchemeName())) { - result = JAR; - } else if (name.equalsIgnoreCase(JDBC.getSchemeName())) { - result = JDBC; - } else if (name.equalsIgnoreCase(RIAP.getSchemeName())) { - result = RIAP; - } else if (name.equalsIgnoreCase(WAR.getSchemeName())) { - result = WAR; - } else if (name.equalsIgnoreCase(ZIP.getSchemeName())) { - result = ZIP; - } else { - result = new Protocol(name); - } - } - - return result; - } - - /** - * Creates the protocol associated to a URI scheme name. If an existing constant - * exists then it is returned, otherwise a new instance is created. - * - * @param name The scheme name. - * @param version The version number. - * @return The associated protocol. - */ - public static Protocol valueOf(String name, String version) { - Protocol result = valueOf(name); - - if (!version.equals(result.getVersion())) { - result = new Protocol(result.getSchemeName(), result.getName(), result.getTechnicalName(), - result.getDescription(), result.getDefaultPort(), result.isConfidential(), version); - } - - return result; - } - - /** The confidentiality. */ - private final boolean confidential; - - /** The default port if known or -1. */ - private final int defaultPort; - - /** The description. */ - private final String description; - - /** The name. */ - private final String name; - - /** The scheme name. */ - private final String schemeName; - - /** The technical name that appears on the wire. */ - private final String technicalName; - - /** The version. */ - private final String version; - - /** - * Constructor. - * - * @param schemeName The scheme name. - */ - public Protocol(String schemeName) { - this(schemeName, schemeName.toUpperCase(), schemeName.toUpperCase() + " Protocol", UNKNOWN_PORT); - } - - /** - * Constructor. - * - * @param schemeName The scheme name. - * @param name The unique name. - * @param description The description. - * @param defaultPort The default port. - */ - public Protocol(String schemeName, String name, String description, int defaultPort) { - this(schemeName, name, description, defaultPort, false); - } - - /** - * Constructor. - * - * @param schemeName The scheme name. - * @param name The unique name. - * @param description The description. - * @param defaultPort The default port. - * @param confidential The confidentiality. - */ - public Protocol(String schemeName, String name, String description, int defaultPort, boolean confidential) { - this(schemeName, name, description, defaultPort, confidential, null); - } - - /** - * Constructor. - * - * @param schemeName The scheme name. - * @param name The unique name. - * @param description The description. - * @param defaultPort The default port. - * @param confidential The confidentiality. - * @param version The version. - */ - public Protocol(String schemeName, String name, String description, int defaultPort, boolean confidential, - String version) { - this(schemeName, name, name, description, defaultPort, confidential, version); - } - - /** - * Constructor. - * - * @param schemeName The scheme name. - * @param name The unique name. - * @param description The description. - * @param defaultPort The default port. - * @param version The version. - */ - public Protocol(String schemeName, String name, String description, int defaultPort, String version) { - this(schemeName, name, description, defaultPort, false, version); - } - - /** - * Constructor. - * - * @param schemeName The scheme name. - * @param name The unique name. - * @param technicalName The technical name that appears on the wire. - * @param description The description. - * @param defaultPort The default port. - * @param confidential The confidentiality. - * @param version The version. - */ - public Protocol(String schemeName, String name, String technicalName, String description, int defaultPort, - boolean confidential, String version) { - this.name = name; - this.description = description; - this.schemeName = schemeName; - this.technicalName = technicalName; - this.defaultPort = defaultPort; - this.confidential = confidential; - this.version = version; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(final Object object) { - return (object instanceof Protocol) && getName().equalsIgnoreCase(((Protocol) object).getName()); - } - - /** - * Returns the default port number. - * - * @return The default port number. - */ - public int getDefaultPort() { - return this.defaultPort; - } - - /** - * Returns the description. - * - * @return The description. - */ - public String getDescription() { - return this.description; - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the URI scheme name. - * - * @return The URI scheme name. - */ - public String getSchemeName() { - return this.schemeName; - } - - /** - * Returns the technical name that appears on the wire. - * - * @return The technical name that appears on the wire. - */ - public String getTechnicalName() { - return technicalName; - } - - /** - * Returns the version. - * - * @return The version. - */ - public String getVersion() { - return version; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); - } - - /** - * Indicates if the protocol guarantees the confidentially of the messages - * exchanged, for example via a SSL-secured connection. - * - * @return True if the protocol is confidential. - */ - public boolean isConfidential() { - return this.confidential; - } - - /** - * Returns the name. - * - * @return The name. - */ - @Override - public String toString() { - return getName() + ((getVersion() == null) ? "" : "/" + getVersion()); - } - + /** Indicates that the port number is undefined. */ + public static final int UNKNOWN_PORT = -1; + + /** All-protocols wildcard. */ + public static final Protocol ALL = + new Protocol("all", "ALL", "Wildcard for all protocols", UNKNOWN_PORT); + + /** + * CLAP (ClassLoader Access Protocol) is a custom scheme to access to representations via + * classloaders. Example URI: "clap://thread/org/restlet/Restlet.class".
+ *
+ * To work, CLAP requires a client connector provided by the core Restlet engine. + * + * @see org.restlet.data.LocalReference + */ + public static final Protocol CLAP = + new Protocol("clap", "CLAP", "Class Loader Access Protocol", UNKNOWN_PORT, true); + + /** + * FILE is a standard scheme to access to representations stored in the file system (locally + * most of the time). Example URI: "file:///D/root/index.html".
+ *
+ * To work, FILE requires a client connector provided by the core Restlet engine. + * + * @see org.restlet.data.LocalReference + */ + public static final Protocol FILE = + new Protocol("file", "FILE", "Local File System Protocol", UNKNOWN_PORT, true); + + /** FTP protocol. */ + public static final Protocol FTP = new Protocol("ftp", "FTP", "File Transfer Protocol", 21); + + /** HTTP protocol. */ + public static final Protocol HTTP = + new Protocol("http", "HTTP", "HyperText Transport Protocol", 80, "1.1"); + + /** HTTPS protocol (via SSL socket). */ + public static final Protocol HTTPS = + new Protocol( + "https", + "HTTPS", + "HTTP", + "HyperText Transport Protocol (Secure)", + 443, + true, + "1.1"); + + /** + * JAR (Java ARchive) is a common scheme to access to representations inside archive files. + * Example URI: "jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class". + * + * @see org.restlet.data.LocalReference#createJarReference(Reference, String) + */ + public static final Protocol JAR = + new Protocol("jar", "JAR", "Java ARchive", UNKNOWN_PORT, true); + + /** JDBC protocol. */ + public static final Protocol JDBC = + new Protocol("jdbc", "JDBC", "Java DataBase Connectivity", UNKNOWN_PORT); + + /** + * RIAP (Restlet Internal Access Protocol) is a custom scheme to access representations via + * internal calls to virtual hosts/components. Example URIs: + * "riap://component/myAppPath/myResource" and "riap://application/myResource".
+ *
+ * To work, RIAP doesn't require any client connector and is automatically supported by the + * Restlet engine. + * + * @see org.restlet.data.LocalReference + */ + public static final Protocol RIAP = + new Protocol("riap", "RIAP", "Restlet Internal Access Protocol", UNKNOWN_PORT, true); + + /** Local Web Archive access protocol. */ + public static final Protocol WAR = + new Protocol("war", "WAR", "Web Archive Access Protocol", UNKNOWN_PORT, true); + + /** + * ZIP is a special scheme to access to representations inside Zip archive files. Example URI: + * "zip:file:///tmp/test.zip!/test.txt". + * + * @see org.restlet.data.LocalReference#createZipReference(Reference, String) + */ + public static final Protocol ZIP = + new Protocol("zip", "ZIP", "Zip Archive Access Protocol", UNKNOWN_PORT, true); + + /** + * Creates the protocol associated with a URI scheme name. If an existing constant exists, then + * it is returned, otherwise a new instance is created. + * + * @param name The scheme name. + * @return The associated protocol. + */ + public static Protocol valueOf(String name) { + Protocol result = null; + + if (!StringUtils.isNullOrEmpty(name)) { + if (name.equalsIgnoreCase(CLAP.getSchemeName())) { + result = CLAP; + } else if (name.equalsIgnoreCase(FILE.getSchemeName())) { + result = FILE; + } else if (name.equalsIgnoreCase(FTP.getSchemeName())) { + result = FTP; + } else if (name.equalsIgnoreCase(HTTP.getSchemeName())) { + result = HTTP; + } else if (name.equalsIgnoreCase(HTTPS.getSchemeName())) { + result = HTTPS; + } else if (name.equalsIgnoreCase(JAR.getSchemeName())) { + result = JAR; + } else if (name.equalsIgnoreCase(JDBC.getSchemeName())) { + result = JDBC; + } else if (name.equalsIgnoreCase(RIAP.getSchemeName())) { + result = RIAP; + } else if (name.equalsIgnoreCase(WAR.getSchemeName())) { + result = WAR; + } else if (name.equalsIgnoreCase(ZIP.getSchemeName())) { + result = ZIP; + } else { + result = new Protocol(name); + } + } + + return result; + } + + /** + * Creates the protocol associated with a URI scheme name. If an existing constant exists, then + * it is returned, otherwise a new instance is created. + * + * @param name The scheme name. + * @param version The version number. + * @return The associated protocol. + */ + public static Protocol valueOf(String name, String version) { + Protocol result = valueOf(name); + + if (!version.equals(result.getVersion())) { + result = + new Protocol( + result.getSchemeName(), + result.getName(), + result.getTechnicalName(), + result.getDescription(), + result.getDefaultPort(), + result.isConfidential(), + version); + } + + return result; + } + + /** The confidentiality. */ + private final boolean confidential; + + /** The default port if known or -1. */ + private final int defaultPort; + + /** The description. */ + private final String description; + + /** The name. */ + private final String name; + + /** The scheme name. */ + private final String schemeName; + + /** The technical name that appears on the wire. */ + private final String technicalName; + + /** The version. */ + private final String version; + + /** + * Constructor. + * + * @param schemeName The scheme name. + */ + public Protocol(String schemeName) { + this( + schemeName, + schemeName.toUpperCase(), + schemeName.toUpperCase() + " Protocol", + UNKNOWN_PORT); + } + + /** + * Constructor. + * + * @param schemeName The scheme name. + * @param name The unique name. + * @param description The description. + * @param defaultPort The default port. + */ + public Protocol(String schemeName, String name, String description, int defaultPort) { + this(schemeName, name, description, defaultPort, false); + } + + /** + * Constructor. + * + * @param schemeName The scheme name. + * @param name The unique name. + * @param description The description. + * @param defaultPort The default port. + * @param confidential The confidentiality. + */ + public Protocol( + String schemeName, + String name, + String description, + int defaultPort, + boolean confidential) { + this(schemeName, name, description, defaultPort, confidential, null); + } + + /** + * Constructor. + * + * @param schemeName The scheme name. + * @param name The unique name. + * @param description The description. + * @param defaultPort The default port. + * @param confidential The confidentiality. + * @param version The version. + */ + public Protocol( + String schemeName, + String name, + String description, + int defaultPort, + boolean confidential, + String version) { + this(schemeName, name, name, description, defaultPort, confidential, version); + } + + /** + * Constructor. + * + * @param schemeName The scheme name. + * @param name The unique name. + * @param description The description. + * @param defaultPort The default port. + * @param version The version. + */ + public Protocol( + String schemeName, String name, String description, int defaultPort, String version) { + this(schemeName, name, description, defaultPort, false, version); + } + + /** + * Constructor. + * + * @param schemeName The scheme name. + * @param name The unique name. + * @param technicalName The technical name that appears on the wire. + * @param description The description. + * @param defaultPort The default port. + * @param confidential The confidentiality. + * @param version The version. + */ + public Protocol( + String schemeName, + String name, + String technicalName, + String description, + int defaultPort, + boolean confidential, + String version) { + this.name = name; + this.description = description; + this.schemeName = schemeName; + this.technicalName = technicalName; + this.defaultPort = defaultPort; + this.confidential = confidential; + this.version = version; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object object) { + return (object instanceof Protocol) + && getName().equalsIgnoreCase(((Protocol) object).getName()); + } + + /** + * Returns the default port number. + * + * @return The default port number. + */ + public int getDefaultPort() { + return this.defaultPort; + } + + /** + * Returns the description. + * + * @return The description. + */ + public String getDescription() { + return this.description; + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the URI scheme name. + * + * @return The URI scheme name. + */ + public String getSchemeName() { + return this.schemeName; + } + + /** + * Returns the technical name that appears on the wire. + * + * @return The technical name that appears on the wire. + */ + public String getTechnicalName() { + return technicalName; + } + + /** + * Returns the version. + * + * @return The version. + */ + public String getVersion() { + return version; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (getName() == null) ? 0 : getName().toLowerCase().hashCode(); + } + + /** + * Indicates if the protocol guarantees the confidentially of the messages exchanged, for + * example via a SSL-secured connection. + * + * @return True if the protocol is confidential. + */ + public boolean isConfidential() { + return this.confidential; + } + + /** + * Returns the name. + * + * @return The name. + */ + @Override + public String toString() { + return getName() + ((getVersion() == null) ? "" : "/" + getVersion()); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Range.java b/org.restlet/src/main/java/org/restlet/data/Range.java index 03c65a0da8..ee9fe01d92 100644 --- a/org.restlet/src/main/java/org/restlet/data/Range.java +++ b/org.restlet/src/main/java/org/restlet/data/Range.java @@ -1,242 +1,233 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import org.restlet.engine.util.SystemUtils; /** * Describes a range of bytes. - * + * * @author Jerome Louvel */ public class Range { - /** - * Index for the first byte (or range unit) of an entity. - */ - public final static long INDEX_FIRST = 0; - - /** - * Index for the last byte (or range unit) of an entity. - */ - public final static long INDEX_LAST = -1; - - public final static String RANGE_BYTES_UNIT = "bytes"; - - /** - * Maximum size available from the index. - */ - public final static long SIZE_MAX = -1; - - /** - * Indicates if the unit of the given range is "bytes". - * - * @param range The range. - * @return true if the unit of the given range is "bytes". - */ - public static boolean isBytesRange(Range range) { - return RANGE_BYTES_UNIT.equals(range.getUnitName()); - } - - /** - * Index from which to start the range. If the index is superior or equal to - * zero, the index will define the start of the range. If its value is - * {@value #INDEX_LAST} (-1), then it defines the end of the range. The default - * value is {@link #INDEX_FIRST} (0), starting at the first byte. - */ - private volatile long index; - - /** - * Total size of the instance in number of bytes (or range unit). In the case of - * "bytes" range, this attribute is ignored, as the instance size is taken from - * the entity. - */ - private volatile long instanceSize; - - /** - * Size of the range in number of bytes (or range unit). If the size is the - * maximum available from the index, then use the {@value #SIZE_MAX} constant. - */ - private volatile long size; - - /** - * Specifies the unit of the range. The HTTP/1.1 protocol specifies only - * 'bytes', but other ranges are allowed {@link RFC 2616} - */ - private volatile String unitName; - - /** - * Default constructor defining a range starting on the first byte and with a - * maximum size, i.e., covering the whole entity. - */ - public Range() { - this(INDEX_FIRST, SIZE_MAX); - } - - /** - * Constructor defining a range starting on the first byte and with the given - * size. - * - * @param size Size of the range in number of bytes. - */ - public Range(long size) { - this(INDEX_FIRST, size); - } - - /** - * Constructor. Sets the name of the range unit as "bytes" by default. - * - * @param index Index from which to start the range - * @param size Size of the range in number of bytes. - */ - public Range(long index, long size) { - this.index = index; - this.size = size; - this.unitName = RANGE_BYTES_UNIT; - } - - /** - * Constructor. Sets the name of the range unit as "bytes" by default. - * - * @param index Index from which to start the range - * @param size Size of the range in number of bytes. - * @param instanceSize Size of the instance in number of bytes. - * @param unitName Unit of the range. - */ - public Range(long index, long size, long instanceSize, String unitName) { - this.index = index; - this.instanceSize = instanceSize; - this.size = size; - this.unitName = unitName; - } - - @Override - public boolean equals(Object object) { - return (object instanceof Range) && ((Range) object).getIndex() == getIndex() - && ((Range) object).getSize() == getSize(); - } - - /** - * Returns the index from which to start the range. If the index is superior or - * equal to zero, the index will define the start of the range. If its value is - * {@value #INDEX_LAST} (-1), then it defines the end of the range. The default - * value is {@link #INDEX_FIRST} (0), starting at the first byte. - * - * @return The index from which to start the range. - */ - public long getIndex() { - return index; - } - - /** - * Returns the total size of the instance in number of bytes (or range unit). In - * the case of "bytes" range, this attribute is ignored, as the instance size is - * taken from the entity. - * - * @return The total size of the instance. - */ - public long getInstanceSize() { - return instanceSize; - } - - /** - * Returns the size of the range in number of bytes. If the size is the maximum - * available from the index, then use the {@value #SIZE_MAX} constant. - * - * @return The size of the range in number of bytes. - */ - public long getSize() { - return size; - } - - /** - * Returns the name of the range unit. - * - * @return The name of the range unit. - */ - public String getUnitName() { - return unitName; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(index, instanceSize, size, unitName); - } - - /** - * Indicates if the given index is included in the range. - * - * @param position The position to test. - * @param totalSize - * - * @return True if the given index is included in the range, false otherwise. - */ - public boolean isIncluded(long position, long totalSize) { - boolean result = false; - - if (getIndex() == INDEX_LAST) { - // The range starts from the end - result = (0 <= position) && (position < totalSize); - - if (result) { - result = position >= (totalSize - getSize()); - } - } else { - // The range starts from the beginning - result = position >= getIndex(); - - if (result && (getSize() != SIZE_MAX)) { - result = position < getIndex() + getSize(); - } - } - - return result; - } - - /** - * Sets the index from which to start the range. If the index is superior or - * equal to zero, the index will define the start of the range. If its value is - * {@value #INDEX_LAST} (-1), then it defines the end of the range. The default - * value is {@link #INDEX_FIRST} (0), starting at the first byte - * - * @param index The index from which to start the range. - */ - public void setIndex(long index) { - this.index = index; - } - - /** - * Sets the total size of the instance in number of bytes (or range unit). - * - * @param instanceSize The total size of the instance. - */ - public void setInstanceSize(long instanceSize) { - this.instanceSize = instanceSize; - } - - /** - * Sets the size of the range in number of bytes. If the size is the maximum - * available from the index, then use the {@value #SIZE_MAX} constant. - * - * @param size The size of the range in number of bytes. - */ - public void setSize(long size) { - this.size = size; - } - - /** - * Sets the name of the range unit. - * - * @param unitName The name of the range unit. - */ - public void setUnitName(String unitName) { - this.unitName = unitName; - } + /** Index for the first byte (or range unit) of an entity. */ + public static final long INDEX_FIRST = 0; + + /** Index for the last byte (or range unit) of an entity. */ + public static final long INDEX_LAST = -1; + + public static final String RANGE_BYTES_UNIT = "bytes"; + + /** Maximum size available from the index. */ + public static final long SIZE_MAX = -1; + + /** + * Indicates if the unit of the given range is "bytes". + * + * @param range The range. + * @return true if the unit of the given range is "bytes". + */ + public static boolean isBytesRange(Range range) { + return RANGE_BYTES_UNIT.equals(range.getUnitName()); + } + + /** + * Index from which to start the range. If the index is superior or equal to zero, the index + * will define the start of the range. If its value is {@value #INDEX_LAST} (-1), then it + * defines the end of the range. The default value is {@link #INDEX_FIRST} (0), starting at the + * first byte. + */ + private volatile long index; + + /** + * Total size of the instance in number of bytes (or range unit). In the case of "bytes" range, + * this attribute is ignored, as the instance size is taken from the entity. + */ + private volatile long instanceSize; + + /** + * Size of the range in number of bytes (or range unit). If the size is the maximum available + * from the index, then use the {@value #SIZE_MAX} constant. + */ + private volatile long size; + + /** + * Specifies the unit of the range. The HTTP/1.1 protocol specifies only 'bytes', but other + * ranges are allowed {@link RFC 2616} + */ + private volatile String unitName; + + /** + * Default constructor defining a range starting on the first byte and with a maximum size, + * i.e., covering the whole entity. + */ + public Range() { + this(INDEX_FIRST, SIZE_MAX); + } + + /** + * Constructor defining a range starting on the first byte and with the given size. + * + * @param size Size of the range in number of bytes. + */ + public Range(long size) { + this(INDEX_FIRST, size); + } + + /** + * Constructor. Sets the name of the range unit as "bytes" by default. + * + * @param index Index from which to start the range + * @param size Size of the range in number of bytes. + */ + public Range(long index, long size) { + this.index = index; + this.size = size; + this.unitName = RANGE_BYTES_UNIT; + } + + /** + * Constructor. Sets the name of the range unit as "bytes" by default. + * + * @param index Index from which to start the range + * @param size Size of the range in number of bytes. + * @param instanceSize Size of the instance in number of bytes. + * @param unitName Unit of the range. + */ + public Range(long index, long size, long instanceSize, String unitName) { + this.index = index; + this.instanceSize = instanceSize; + this.size = size; + this.unitName = unitName; + } + + @Override + public boolean equals(Object object) { + return (object instanceof Range) + && ((Range) object).getIndex() == getIndex() + && ((Range) object).getSize() == getSize(); + } + + /** + * Returns the index from which to start the range. If the index is superior or equal to zero, + * the index will define the start of the range. If its value is {@value #INDEX_LAST} (-1), then + * it defines the end of the range. The default value is {@link #INDEX_FIRST} (0), starting at + * the first byte. + * + * @return The index from which to start the range. + */ + public long getIndex() { + return index; + } + + /** + * Returns the total size of the instance in number of bytes (or range unit). In the case of the + * "bytes" range, this attribute is ignored, as the instance size is taken from the entity. + * + * @return The total size of the instance. + */ + public long getInstanceSize() { + return instanceSize; + } + + /** + * Returns the size of the range in number of bytes. If the size is the maximum available from + * the index, then use the {@value #SIZE_MAX} constant. + * + * @return The size of the range in number of bytes. + */ + public long getSize() { + return size; + } + + /** + * Returns the name of the range unit. + * + * @return The name of the range unit. + */ + public String getUnitName() { + return unitName; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode(index, instanceSize, size, unitName); + } + + /** + * Indicates if the given index is included in the range. + * + * @param position The position to test. + * @param totalSize + * @return True if the given index is included in the range, false otherwise. + */ + public boolean isIncluded(long position, long totalSize) { + boolean result = false; + + if (getIndex() == INDEX_LAST) { + // The range starts from the end + result = (0 <= position) && (position < totalSize); + + if (result) { + result = position >= (totalSize - getSize()); + } + } else { + // The range starts from the beginning + result = position >= getIndex(); + + if (result && (getSize() != SIZE_MAX)) { + result = position < getIndex() + getSize(); + } + } + + return result; + } + + /** + * Sets the index from which to start the range. If the index is superior or equal to zero, the + * index will define the start of the range. If its value is {@value #INDEX_LAST} (-1), then it + * defines the end of the range. The default value is {@link #INDEX_FIRST} (0), starting at the + * first byte + * + * @param index The index from which to start the range. + */ + public void setIndex(long index) { + this.index = index; + } + + /** + * Sets the total size of the instance in number of bytes (or range unit). + * + * @param instanceSize The total size of the instance. + */ + public void setInstanceSize(long instanceSize) { + this.instanceSize = instanceSize; + } + + /** + * Sets the size of the range in number of bytes. If the size is the maximum available from the + * index, then use the {@value #SIZE_MAX} constant. + * + * @param size The size of the range in number of bytes. + */ + public void setSize(long size) { + this.size = size; + } + + /** + * Sets the name of the range unit. + * + * @param unitName The name of the range unit. + */ + public void setUnitName(String unitName) { + this.unitName = unitName; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java b/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java index 942c5d3ef3..085a788bcb 100644 --- a/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java @@ -1,105 +1,99 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; /** - * Describes an intermediary via which the call went through. The intermediary - * is typically a proxy or a cache.
+ * Describes an intermediary via which the call went through. The intermediary is typically a proxy + * or a cache.
*
- * Note that when used with HTTP connectors, this class maps to the "Via" - * header. - * + * Note that when used with HTTP connectors, this class maps to the "Via" header. + * * @author Jerome Louvel */ public class RecipientInfo { - /** The protocol used to communicate with the recipient. */ - private volatile Protocol protocol; - - /** The optional comment, typically the software agent name. */ - private volatile String comment; + /** The protocol used to communicate with the recipient. */ + private volatile Protocol protocol; - /** The host name and port number or a pseudonym. */ - private volatile String name; + /** The optional comment, typically the software agent name. */ + private volatile String comment; - /** - * Default constructor. - */ - public RecipientInfo() { - } + /** The host name and port number or a pseudonym. */ + private volatile String name; - /** - * Constructor. - * - * @param protocol The protocol used to communicate with the recipient. - * @param name The host name and port number or a pseudonym. - * @param agent The software agent. - */ - public RecipientInfo(Protocol protocol, String name, String agent) { - this.protocol = protocol; - this.name = name; - this.comment = agent; - } + /** Default constructor. */ + public RecipientInfo() {} - /** - * Returns the optional comment, typically the software agent name. - * - * @return The optional comment. - */ - public String getComment() { - return comment; - } + /** + * Constructor. + * + * @param protocol The protocol used to communicate with the recipient. + * @param name The host name and port number or a pseudonym. + * @param agent The software agent. + */ + public RecipientInfo(Protocol protocol, String name, String agent) { + this.protocol = protocol; + this.name = name; + this.comment = agent; + } - /** - * Returns the host name and port number or a pseudonym. - * - * @return The host name and port number or a pseudonym. - */ - public String getName() { - return name; - } + /** + * Returns the optional comment, typically the software agent name. + * + * @return The optional comment. + */ + public String getComment() { + return comment; + } - /** - * Returns the protocol used to communicate with the recipient. - * - * @return The protocol used to communicate with the recipient. - */ - public Protocol getProtocol() { - return protocol; - } + /** + * Returns the host name and port number or a pseudonym. + * + * @return The host name and port number or a pseudonym. + */ + public String getName() { + return name; + } - /** - * Sets the optional comment, typically the software agent name. - * - * @param comment The optional comment. - */ - public void setComment(String comment) { - this.comment = comment; - } + /** + * Returns the protocol used to communicate with the recipient. + * + * @return The protocol used to communicate with the recipient. + */ + public Protocol getProtocol() { + return protocol; + } - /** - * Sets the host name and port number or a pseudonym. - * - * @param name The host name and port number or a pseudonym. - */ - public void setName(String name) { - this.name = name; - } + /** + * Sets the optional comment, typically the software agent name. + * + * @param comment The optional comment. + */ + public void setComment(String comment) { + this.comment = comment; + } - /** - * Sets the protocol used to communicate with the recipient. - * - * @param protocol The protocol used to communicate with the recipient. - */ - public void setProtocol(Protocol protocol) { - this.protocol = protocol; - } + /** + * Sets the host name and port number or a pseudonym. + * + * @param name The host name and port number or a pseudonym. + */ + public void setName(String name) { + this.name = name; + } + /** + * Sets the protocol used to communicate with the recipient. + * + * @param protocol The protocol used to communicate with the recipient. + */ + public void setProtocol(Protocol protocol) { + this.protocol = protocol; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Reference.java b/org.restlet/src/main/java/org/restlet/data/Reference.java index d245508721..10a98441cd 100644 --- a/org.restlet/src/main/java/org/restlet/data/Reference.java +++ b/org.restlet/src/main/java/org/restlet/data/Reference.java @@ -1,2815 +1,2834 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.Context; - import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; +import org.restlet.Context; /** - * Reference to a Uniform Resource Identifier (URI). Contrary to the - * java.net.URI class, this interface represents mutable references. It strictly - * conforms to the RFC 3986 specifying URIs and follow its naming - * conventions.
- * + * Reference to a Uniform Resource Identifier (URI). Contrary to the java.net.URI class, this + * interface represents mutable references. It strictly conforms to the RFC 3986 specifying URIs and + * follow its naming conventions.
+ * *

  * URI reference        = absolute-reference | relative-reference
- * 
+ *
  * absolute-reference   = scheme ":" scheme-specific-part [ "#" fragment ]
  * scheme-specific-part = ( hierarchical-part [ "?" query ] ) | opaque-part
  * hierarchical-part    = ( "//" authority path-abempty ) | path-absolute | path-rootless | path-empty
  * authority            = [ user-info "@" ] host-domain [ ":" host-port ]
- * 
+ *
  * relative-reference   = relative-part [ "?" query ] [ "#" fragment ]
  * relative-part        = ( "//" authority path-abempty ) | path-absolute | path-noscheme | path-empty
- * 
+ *
  * path-abempty         = begins with "/" or is empty
  * path-absolute        = begins with "/" but not "//"
  * path-noscheme        = begins with a non-colon segment
  * path-rootless        = begins with a segment
  * path-empty           = zero characters
  * 
- * - *

- * Note that this class doesn't encode or decode the reserved characters. It - * assumes that the URIs or the URI parts passed in are properly encoded using - * the standard URI encoding mechanism. You can use the static "encode()" and - * "decode()" methods for this purpose. Note that if an invalid URI character is - * detected by the constructor or one of the setters, a trace will be logged and - * the character will be automatically encoded. - *

- *

- * The fundamental point to underline is the difference between an URI - * "reference" and an URI. Contrary to an URI (the target identifier of a REST - * resource), an URI reference can be relative (with or without query and - * fragment part). This relative URI reference can then be resolved against a - * base reference via the getTargetRef() method which will return a new resolved - * Reference instance, an absolute URI reference with no base reference and with - * no dot-segments (the path segments "." and ".."). - *

- *

- * You can also apply the getTargetRef() method on absolute references in order - * to solve the dot-segments. Note that applying the getRelativeRef() method on - * an absolute reference returns the current reference relatively to a base - * reference, if any, and solves the dot-segments. - *

- *

- * The Reference stores its data as a single string, the one passed to the - * constructor. This string can always be obtained using the toString() method. - * A couple of integer indexes are maintained to improve the extraction time of - * various reference properties (URI components). - *

- *

- * When you modify a specific component of the URI reference, via the setPath() - * method for example, the internal string is simply regenerated by updating - * only the relevant part. We try as much as possible to protect the bytes given - * to the Reference class instead of transparently parsing and normalizing the - * URI data. Our idea is to protect encodings and special characters in all case - * and reduce the memory size taken by this class while making Reference - * instances mutable. - *

- *

- * Because the base reference is only a property of the Reference ("baseRef"). - * When you use the "Reference(base, path)" constructor, it is equivalent to - * doing:
+ * + *

Note that this class doesn't encode or decode the reserved characters. It assumes that the + * URIs or the URI parts passed in are properly encoded using the standard URI encoding mechanism. + * You can use the static "encode()" and "decode()" methods for this purpose. Note that if an + * invalid URI character is detected by the constructor or one of the setters, a trace will be + * logged and the character will be automatically encoded. + * + *

The fundamental point to underline is the difference between a URI "reference" and a URI. + * Contrary to a URI (the target identifier of a REST resource), a URI reference can be relative + * (with or without query and fragment part). This relative URI reference can then be resolved + * against a base reference via the getTargetRef() method which will return a new resolved Reference + * instance, an absolute URI reference with no base reference and with no dot-segments (the path + * segments "." and ".."). + * + *

You can also apply the getTargetRef() method on absolute references to solve the dot-segments. + * Note that applying the getRelativeRef() method on an absolute reference returns the current + * reference relatively to a base reference, if any, and solves the dot-segments. + * + *

The Reference stores its data as a single string, the one passed to the constructor. This + * string can always be obtained using the toString() method. A couple of integer indexes are + * maintained to improve the extraction time of various reference properties (URI components). + * + *

When you modify a specific component of the URI reference, via the setPath() method, for + * example, the internal string is simply regenerated by updating only the relevant part. We try as + * much as possible to protect the bytes given to the Reference class instead of transparently + * parsing and normalizing the URI data. Our idea is to protect encodings and special characters in + * all cases and reduce the memory size taken by this class while making Reference instances + * mutable. + * + *

Because the base reference is only a property of the Reference ("baseRef"). When you use the + * "Reference(base, path)" constructor, it is equivalent to doing:
* ref = new Reference(path);
* ref.setBaseRef(base); - *

- *

- * The base ref is not automatically resolved or "merged" with the rest of the - * reference information (the path here). For example, this let's you reuse a - * single reference as the base of several relative references. If you modify - * the base reference, all relative references are still accurate. - *

- * Note that the name and value properties are thread safe, stored in volatile - * members. - * + * + *

The base ref is not automatically resolved or "merged" with the rest of the reference + * information (the path here). For example, this lets you reuse a single reference as the base of + * several relative references. If you modify the base reference, all relative references are still + * accurate. Note that the name and value properties are thread-safe, stored in volatile members. + * * @author Jerome Louvel * @see RFC 3986 */ public class Reference { - /** Helps to map characters and their validity as URI characters. */ - private static final boolean[] charValidityMap = new boolean[127]; - - static { - // Initialize the map of valid characters. - for (int character = 0; character < 127; character++) { - charValidityMap[character] = isReserved(character) || isUnreserved(character) || (character == '%') - || (character == '{') || (character == '}'); - } - } - - /** - * Decodes a given string using the standard URI encoding mechanism and the - * UTF-8 character set. - * - * @param toDecode The string to decode. - * @return The decoded string. - */ - public static String decode(String toDecode) { - return decode(toDecode, CharacterSet.UTF_8); - } - - /** - * Decodes a given string using the standard URI encoding mechanism. If the - * provided character set is null, the string is returned but not decoded. - * Note: The - * - * World Wide Web Consortium Recommendation states that UTF-8 should be - * used. Not doing so may introduce incompatibilities. - * - * @param toDecode The string to decode. - * @param characterSet The name of a supported character encoding. - * @return The decoded string or null if the named character encoding is not - * supported. - */ - public static String decode(String toDecode, CharacterSet characterSet) { - String result = null; - try { - result = (characterSet == null) ? toDecode : java.net.URLDecoder.decode(toDecode, characterSet.getName()); - } catch (UnsupportedEncodingException uee) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to decode the string with the UTF-8 character set.", - uee); - } - - return result; - } - - /** - * Encodes a given string using the standard URI encoding mechanism and the - * UTF-8 character set. - * - * @param toEncode The string to encode. - * @return The encoded string. - */ - public static String encode(String toEncode) { - return encode(toEncode, true, CharacterSet.UTF_8); - } - - /** - * Encodes a given string using the standard URI encoding mechanism and the - * UTF-8 character set. Useful to prevent the usage of '+' to encode spaces (%20 - * instead). The '*' characters are encoded as %2A and %7E are replaced by '~'. - * - * @param toEncode The string to encode. - * @param queryString True if the string to encode is part of a query string - * instead of a HTML form post. - * @return The encoded string. - */ - public static String encode(String toEncode, boolean queryString) { - return encode(toEncode, queryString, CharacterSet.UTF_8); - } - - /** - * Encodes a given string using the standard URI encoding mechanism and the - * UTF-8 character set. Useful to prevent the usage of '+' to encode spaces (%20 - * instead). The '*' characters are encoded as %2A and %7E are replaced by '~'. - * - * @param toEncode The string to encode. - * @param queryString True if the string to encode is part of a query string - * instead of a HTML form post. - * @param characterSet The supported character encoding. - * @return The encoded string. - */ - public static String encode(String toEncode, boolean queryString, CharacterSet characterSet) { - - String result = null; - - try { - result = (characterSet == null) ? toEncode : java.net.URLEncoder.encode(toEncode, characterSet.getName()); - } catch (UnsupportedEncodingException uee) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to encode the string with the UTF-8 character set.", - uee); - } - - if (result != null && queryString) { - result = result.replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); - } - - return result; - } - - /** - * Encodes a given string using the standard URI encoding mechanism. If the - * provided character set is null, the string is returned but not encoded. - * - * Note: The - * - * World Wide Web Consortium Recommendation states that UTF-8 should be - * used. Not doing so may introduce incompatibilities. - * - * @param toEncode The string to encode. - * @param characterSet The supported character encoding. - * @return The encoded string or null if the named character encoding is not - * supported. - */ - public static String encode(String toEncode, CharacterSet characterSet) { - return encode(toEncode, true, characterSet); - } - - /** - * Indicates if the given character is alphabetical (a-z or A-Z). - * - * @param character The character to test. - * @return True if the given character is alphabetical (a-z or A-Z). - */ - private static boolean isAlpha(int character) { - return isUpperCase(character) || isLowerCase(character); - } - - /** - * Indicates if the given character is a digit (0-9). - * - * @param character The character to test. - * @return True if the given character is a digit (0-9). - */ - private static boolean isDigit(int character) { - return (character >= '0') && (character <= '9'); - } - - /** - * Indicates if the given character is a generic URI component delimiter - * character. - * - * @param character The character to test. - * @return True if the given character is a generic URI component delimiter - * character. - */ - public static boolean isGenericDelimiter(int character) { - return (character == ':') || (character == '/') || (character == '?') || (character == '#') - || (character == '[') || (character == ']') || (character == '@'); - } - - /** - * Indicates if the given character is lower case (a-z). - * - * @param character The character to test. - * @return True if the given character is lower case (a-z). - */ - private static boolean isLowerCase(int character) { - return (character >= 'a') && (character <= 'z'); - } - - /** - * Indicates if the given character is a reserved URI character. - * - * @param character The character to test. - * @return True if the given character is a reserved URI character. - */ - public static boolean isReserved(int character) { - return isGenericDelimiter(character) || isSubDelimiter(character); - } - - /** - * Indicates if the given character is an URI subcomponent delimiter character. - * - * @param character The character to test. - * @return True if the given character is an URI subcomponent delimiter - * character. - */ - public static boolean isSubDelimiter(int character) { - return (character == '!') || (character == '$') || (character == '&') || (character == '\'') - || (character == '(') || (character == ')') || (character == '*') || (character == '+') - || (character == ',') || (character == ';') || (character == '='); - } - - /** - * Indicates if the given character is an unreserved URI character. - * - * @param character The character to test. - * @return True if the given character is an unreserved URI character. - */ - public static boolean isUnreserved(int character) { - return isAlpha(character) || isDigit(character) || (character == '-') || (character == '.') - || (character == '_') || (character == '~'); - } - - /** - * Indicates if the given character is upper case (A-Z). - * - * @param character The character to test. - * @return True if the given character is upper case (A-Z). - */ - private static boolean isUpperCase(int character) { - return (character >= 'A') && (character <= 'Z'); - } - - /** - * Indicates if the given character is a valid URI character. - * - * @param character The character to test. - * @return True if the given character is a valid URI character. - */ - public static boolean isValid(int character) { - return character >= 0 && character < 127 && charValidityMap[character]; - } - - /** - * Creates a reference string from its parts. - * - * @param scheme The scheme ("http", "https" or "ftp"). - * @param hostName The host name or IP address. - * @param hostPort The host port (default ports are correctly ignored). - * @param path The path component for hierarchical identifiers. - * @param query The optional query component for hierarchical identifiers. - * @param fragment The optional fragment identifier. - * @return The reference as String. - */ - public static String toString(String scheme, String hostName, Integer hostPort, String path, String query, - String fragment) { - String host = hostName; - - // Appends the host port number - if (hostPort != null) { - final int defaultPort = Protocol.valueOf(scheme).getDefaultPort(); - if (hostPort != defaultPort) { - host = hostName + ':' + hostPort; - } - } - - return toString(scheme, host, path, query, fragment); - } - - /** - * Creates a relative reference string from its parts. - * - * @param relativePart The relative part component. - * @param query The optional query component for hierarchical - * identifiers. - * @param fragment The optional fragment identifier. - * @return The relative reference as a String. - */ - public static String toString(String relativePart, String query, String fragment) { - final StringBuilder sb = new StringBuilder(); - - // Append the path - if (relativePart != null) { - sb.append(relativePart); - } - - // Append the query string - if (query != null) { - sb.append('?').append(query); - } - - // Append the fragment identifier - if (fragment != null) { - sb.append('#').append(fragment); - } - - // Actually construct the reference - return sb.toString(); - } - - /** - * Creates a reference string from its parts. - * - * @param scheme The scheme ("http", "https" or "ftp"). - * @param host The host name or IP address plus the optional port number. - * @param path The path component for hierarchical identifiers. - * @param query The optional query component for hierarchical identifiers. - * @param fragment The optional fragment identifier. - * @return The reference a String. - */ - public static String toString(String scheme, String host, String path, String query, String fragment) { - final StringBuilder sb = new StringBuilder(); - - if (scheme != null) { - // Append the scheme and host name - sb.append(scheme.toLowerCase()).append("://").append(host); - } - - // Append the path - if (path != null) { - sb.append(path); - } - - // Append the query string - if (query != null) { - sb.append('?').append(query); - } - - // Append the fragment identifier - if (fragment != null) { - sb.append('#').append(fragment); - } - - // Actually construct the reference - return sb.toString(); - } - - /** The base reference for relative references. */ - private volatile Reference baseRef; - - /** The fragment separator index. */ - private volatile int fragmentIndex; - - /** The internal reference. */ - private volatile String internalRef; - - /** The query separator index. */ - private volatile int queryIndex; - - /** The scheme separator index. */ - private volatile int schemeIndex; - - /** - * Empty constructor. - */ - public Reference() { - this((Reference) null, (String) null); - } - - /** - * Constructor from an {@link java.net.URI} instance. - * - * @param uri The {@link java.net.URI} instance. - */ - public Reference(java.net.URI uri) { - this(uri.toString()); - } - - /** - * Constructor from an {@link java.net.URI} instance. - * - * @param baseUri The base {@link java.net.URI} instance. - * @param uri The {@link java.net.URI} instance. - */ - public Reference(java.net.URI baseUri, java.net.URI uri) { - this(baseUri.toString(), uri.toString()); - } - - /** - * Constructor from an {@link java.net.URL} instance. - * - * @param url The {@link java.net.URL} instance. - */ - public Reference(java.net.URL url) { - this(url.toString()); - } - - /** - * Constructor for a protocol and host name. Uses the default port for the given - * protocol. - * - * @param protocol Protocol/scheme to use - * @param hostName The host name or IP address. - */ - public Reference(Protocol protocol, String hostName) { - this(protocol, hostName, protocol.getDefaultPort()); - } - - /** - * Constructor for a protocol, host name and host port - * - * @param protocol Protocol/scheme to use - * @param hostName The host name or IP address. - * @param hostPort The host port (default ports are correctly ignored). - */ - public Reference(Protocol protocol, String hostName, int hostPort) { - this(protocol.getSchemeName(), hostName, hostPort, null, null, null); - } - - /** - * Clone constructor. - * - * @param ref The reference to clone. - */ - public Reference(Reference ref) { - this(ref.baseRef, ref.internalRef); - } - - /** - * Constructor from an URI reference (most likely relative). - * - * @param baseRef The base reference. - * @param uriReference The URI reference, either absolute or relative. - */ - public Reference(Reference baseRef, Reference uriReference) { - this(baseRef, uriReference.toString()); - } - - /** - * Constructor from a URI reference (most likely relative). - * - * @param baseRef The base reference. - * @param uriRef The URI reference, either absolute or relative. - */ - public Reference(Reference baseRef, String uriRef) { - uriRef = encodeInvalidCharacters(uriRef); - this.baseRef = baseRef; - this.internalRef = uriRef; - updateIndexes(); - } - - /** - * Constructor of relative reference from its parts. - * - * @param baseRef The base reference. - * @param relativePart The relative part component (most of the time it is the - * path component). - * @param query The optional query component for hierarchical - * identifiers. - * @param fragment The optional fragment identifier. - */ - public Reference(Reference baseRef, String relativePart, String query, String fragment) { - this(baseRef, toString(relativePart, query, fragment)); - } - - /** - * Constructor from a URI reference. - * - * @param uriReference The URI reference, either absolute or relative. - */ - public Reference(String uriReference) { - this((Reference) null, uriReference); - } - - /** - * Constructor from an identifier and a fragment. - * - * @param identifier The resource identifier. - * @param fragment The fragment identifier. - */ - public Reference(String identifier, String fragment) { - this((fragment == null) ? identifier : identifier + '#' + fragment); - } - - /** - * Constructor of absolute reference from its parts. - * - * @param scheme The scheme ("http", "https" or "ftp"). - * @param hostName The host name or IP address. - * @param hostPort The host port (default ports are correctly ignored). - * @param path The path component for hierarchical identifiers. - * @param query The optional query component for hierarchical identifiers. - * @param fragment The optional fragment identifier. - */ - public Reference(String scheme, String hostName, int hostPort, String path, String query, String fragment) { - this(toString(scheme, hostName, hostPort, path, query, fragment)); - } - - /** - * Adds a parameter to the query component. The name and value are automatically - * URL encoded if necessary. - * - * @param parameter The parameter to add. - * @return The updated reference. - */ - public Reference addQueryParameter(Parameter parameter) { - return addQueryParameter(parameter.getName(), parameter.getValue()); - } - - /** - * Adds a parameter to the query component. The name and value are automatically - * URL encoded if necessary. - * - * @param name The parameter name. - * @param value The optional parameter value. - * @return The updated reference. - */ - public Reference addQueryParameter(String name, String value) { - String query = getQuery(); - - if (query == null) { - if (value == null) { - setQuery(encode(name)); - } else { - setQuery(encode(name) + '=' + encode(value)); - } - } else { - if (value == null) { - setQuery(query + '&' + encode(name)); - } else { - setQuery(query + '&' + encode(name) + '=' + encode(value)); - } - } - - return this; - } - - /** - * Adds several parameters to the query component. The name and value are - * automatically URL encoded if necessary. - * - * @param parameters The parameters to add. - * @return The updated reference. - */ - public Reference addQueryParameters(Iterable parameters) { - for (Parameter param : parameters) { - addQueryParameter(param); - } - - return this; - } - - /** - * Adds a segment at the end of the path. If the current path doesn't end with a - * slash character, one is inserted before the new segment value. The value is - * automatically encoded if necessary. - * - * @param value The segment value to add. - * @return The updated reference. - */ - public Reference addSegment(String value) { - final String path = getPath(); - - if (value != null) { - if (path == null) { - setPath("/" + value); - } else if (path.endsWith("/")) { - setPath(path + encode(value)); - } else { - setPath(path + "/" + encode(value)); - } - } - - return this; - } - - public Reference copy() { - final Reference newRef = new Reference(); - - if (this.baseRef == null) { - newRef.baseRef = null; - } else if (equals(this.baseRef)) { - newRef.baseRef = newRef; - } else { - newRef.baseRef = this.baseRef.copy(); - } - - newRef.fragmentIndex = this.fragmentIndex; - newRef.internalRef = this.internalRef; - newRef.queryIndex = this.queryIndex; - newRef.schemeIndex = this.schemeIndex; - return newRef; - } - - /** - * Checks if all characters are valid and encodes invalid characters if - * necessary. - * - * @param uriRef The URI reference to check. - * @return The original reference, eventually with invalid URI characters - * encoded. - */ - private String encodeInvalidCharacters(String uriRef) throws IllegalArgumentException { - String result = uriRef; - - if (uriRef != null) { - boolean valid = true; - - // Ensure that all characters are valid, otherwise encode them - for (int i = 0; valid && (i < uriRef.length()); i++) { - if (!isValid(uriRef.charAt(i))) { - valid = false; - Context.getCurrentLogger().fine("Invalid character detected in URI reference at index '" + i - + "': \"" + uriRef.charAt(i) + "\". It will be automatically encoded."); - } else if ((uriRef.charAt(i) == '%') && (i > uriRef.length() - 2)) { - // A percent encoding character has been detected but - // without the necessary two hexadecimal digits following - valid = false; - Context.getCurrentLogger().fine("Invalid percent encoding detected in URI reference at index '" + i - + "': \"" + uriRef.charAt(i) + "\". It will be automatically encoded."); - } - } - - if (!valid) { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; (i < uriRef.length()); i++) { - if (isValid(uriRef.charAt(i))) { - if ((uriRef.charAt(i) == '%') && (i > uriRef.length() - 2)) { - sb.append("%25"); - } else { - sb.append(uriRef.charAt(i)); - } - } else { - sb.append(encode(String.valueOf(uriRef.charAt(i)))); - } - } - - result = sb.toString(); - } - } - - return result; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param object The object to compare to. - * @return True if this object is the same as the obj argument. - */ - @Override - public boolean equals(Object object) { - if (object instanceof Reference) { - final Reference ref = (Reference) object; - if (this.internalRef == null) { - return ref.internalRef == null; - } - return this.internalRef.equals(ref.internalRef); - - } - - return false; - } - - /** - * Returns the authority component for hierarchical identifiers. Includes the - * user info, host name and the host port number.
- * Note that no URI decoding is done by this method. - * - * @return The authority component for hierarchical identifiers. - */ - public String getAuthority() { - final String part = isRelative() ? getRelativePart() : getSchemeSpecificPart(); - - if ((part != null) && part.startsWith("//")) { - int index = part.indexOf('/', 2); - - if (index != -1) { - return part.substring(2, index); - } - - index = part.indexOf('?'); - if (index != -1) { - return part.substring(2, index); - } - - return part.substring(2); - - } - - return null; - } - - /** - * Returns the optionally decoded authority component. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded authority component. - * @see #getAuthority() - */ - public String getAuthority(boolean decode) { - return decode ? decode(getAuthority()) : getAuthority(); - } - - /** - * Returns the base reference for relative references. - * - * @return The base reference for relative references. - */ - public Reference getBaseRef() { - return this.baseRef; - } - - /** - * Returns the optional extensions for hierarchical identifiers. An extensions - * part starts after the first '.' character of the last path segment and ends - * with either the end of the segment of with the first ';' character (matrix - * start). It is a token similar to file extensions separated by '.' characters. - * The value can be ommited.
- * Note that no URI decoding is done by this method. - * - * @return The extensions or null. - * @see #getExtensionsAsArray() - * @see #setExtensions(String) - */ - public String getExtensions() { - String result = null; - final String lastSegment = getLastSegment(); - - if (lastSegment != null) { - final int extensionIndex = lastSegment.indexOf('.'); - final int matrixIndex = lastSegment.indexOf(';'); - - if (extensionIndex != -1) { - // Extensions found - if (matrixIndex != -1) { - result = lastSegment.substring(extensionIndex + 1, matrixIndex); - } else { - // No matrix found - result = lastSegment.substring(extensionIndex + 1); - } - } - } - - return result; - } - - /** - * Returns the extensions as an array or null if no extension is found. - * - * @return The extensions as an array or null if no extension is found. - * @see #getExtensions() - */ - public String[] getExtensionsAsArray() { - String[] result = null; - final String extensions = getExtensions(); - - if (extensions != null) { - result = extensions.split("\\."); - } - - return result; - } - - /** - * Returns the fragment identifier.
- * Note that no URI decoding is done by this method. - * - * @return The fragment identifier. - */ - public String getFragment() { - if (hasFragment()) { - return this.internalRef.substring(this.fragmentIndex + 1); - } - - return null; - } - - /** - * Returns the optionally decoded fragment identifier. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded fragment identifier. - * @see #getFragment() - */ - public String getFragment(boolean decode) { - return decode ? decode(getFragment()) : getFragment(); - } - - /** - * Returns the hierarchical part which is equivalent to the scheme specific part - * less the query component.
- * Note that no URI decoding is done by this method. - * - * @return The hierarchical part . - */ - public String getHierarchicalPart() { - if (hasScheme()) { - // Scheme found - if (hasQuery()) { - // Query found - return this.internalRef.substring(this.schemeIndex + 1, this.queryIndex); - } - - // No query found - if (hasFragment()) { - // Fragment found - return this.internalRef.substring(this.schemeIndex + 1, this.fragmentIndex); - } - - // No fragment found - return this.internalRef.substring(this.schemeIndex + 1); - } - - // No scheme found - if (hasQuery()) { - // Query found - return this.internalRef.substring(0, this.queryIndex); - } - if (hasFragment()) { - // Fragment found - return this.internalRef.substring(0, this.fragmentIndex); - } - - // No fragment found - return this.internalRef; - } - - /** - * Returns the optionally decoded hierarchical part. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded hierarchical part. - * @see #getHierarchicalPart() - */ - public String getHierarchicalPart(boolean decode) { - return decode ? decode(getHierarchicalPart()) : getHierarchicalPart(); - } - - /** - * Returns the host domain name component for server based hierarchical - * identifiers. It can also be replaced by an IP address when no domain name was - * registered.
- * Note that no URI decoding is done by this method. - * - * @return The host domain name component for server based hierarchical - * identifiers. - */ - public String getHostDomain() { - String result = null; - final String authority = getAuthority(); - - if (authority != null) { - // We must prevent the case where the userinfo part contains ':' - // and the case of IPV6 addresses - int indexUI = authority.indexOf('@'); // user info - int indexIPV6 = authority.indexOf(']'); // IPV6 - int indexP = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); - - if (indexUI != -1) { - // User info found - if (indexP != -1) { - // Port found - result = authority.substring(indexUI + 1, indexP); - } else { - // No port found - result = authority.substring(indexUI + 1); - } - } else { - // No user info found - if (indexP != -1) { - // Port found - result = authority.substring(0, indexP); - } else { - // No port found - result = authority; - } - } - } - - return result; - } - - /** - * Returns the optionally decoded host domain name component. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded host domain name component. - * @see #getHostDomain() - */ - public String getHostDomain(boolean decode) { - return decode ? decode(getHostDomain()) : getHostDomain(); - } - - /** - * Returns the host identifier. Includes the scheme, the host name and the host - * port number.
- * Note that no URI decoding is done by this method. - * - * @return The host identifier. - */ - public String getHostIdentifier() { - final StringBuilder result = new StringBuilder(); - result.append(getScheme()).append("://").append(getAuthority()); - return result.toString(); - } - - /** - * Returns the optionally decoded host identifier. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded host identifier. - * @see #getHostIdentifier() - */ - public String getHostIdentifier(boolean decode) { - return decode ? decode(getHostIdentifier()) : getHostIdentifier(); - } - - /** - * Returns the optional port number for server based hierarchical identifiers. - * - * @return The optional port number for server based hierarchical identifiers or - * -1 if the port number does not exist. - */ - public int getHostPort() { - int result = -1; - final String authority = getAuthority(); - - if (authority != null) { - // We must prevent the case where the userinfo part contains ':' - // and the case of IPV6 addresses - int indexUI = authority.indexOf('@'); // user info - int indexIPV6 = authority.indexOf(']'); // IPV6 - int index = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); - - if (index != -1) { - try { - result = Integer.parseInt(authority.substring(index + 1)); - } catch (NumberFormatException nfe) { - Context.getCurrentLogger().log(Level.WARNING, "Can't parse hostPort : [hostRef,requestUri]=[" - + getBaseRef() + "," + this.internalRef + "]"); - } - } - } - - return result; - } - - /** - * Returns the absolute resource identifier, without the fragment.
- * Note that no URI decoding is done by this method. - * - * @return The absolute resource identifier, without the fragment. - */ - public String getIdentifier() { - if (hasFragment()) { - // Fragment found - return this.internalRef.substring(0, this.fragmentIndex); - } - - // No fragment found - return this.internalRef; - } - - /** - * Returns the optionally decoded absolute resource identifier. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded absolute resource identifier. - * @see #getIdentifier() - */ - public String getIdentifier(boolean decode) { - return decode ? decode(getIdentifier()) : getIdentifier(); - } - - /** - * Returns the last segment of a hierarchical path.
- * For example the "/a/b/c" and "/a/b/c/" paths have the same segments: "a", - * "b", "c.
- * Note that no URI decoding is done by this method. - * - * @return The last segment of a hierarchical path. - */ - public String getLastSegment() { - String result = null; - String path = getPath(); - - if (path != null) { - if (path.endsWith("/")) { - path = path.substring(0, path.length() - 1); - } - - final int lastSlash = path.lastIndexOf('/'); - - if (lastSlash != -1) { - result = path.substring(lastSlash + 1); - } - } - - return result; - } - - /** - * Returns the optionally decoded last segment. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded last segment. - * @see #getLastSegment() - */ - public String getLastSegment(boolean decode) { - return getLastSegment(decode, false); - } - - /** - * Returns the optionally decoded last segment. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @param excludeMatrix True if the matrix parameters are dropped from the - * segments. - * @return The optionally decoded last segment. - * @see #getLastSegment() - */ - public String getLastSegment(boolean decode, boolean excludeMatrix) { - String result = getLastSegment(); - - if (excludeMatrix && (result != null)) { - final int matrixIndex = result.indexOf(';'); - - if (matrixIndex != -1) { - result = result.substring(0, matrixIndex); - } - } - - return decode ? decode(result) : result; - } - - /** - * Returns the optional matrix for hierarchical identifiers. A matrix part - * starts after the first ';' character of the last path segment. It is a - * sequence of 'name=value' parameters separated by ';' characters. The value - * can be ommitted.
- * Note that no URI decoding is done by this method. - * - * @return The matrix or null. - */ - public String getMatrix() { - String lastSegment = getLastSegment(); - - if (lastSegment != null) { - final int matrixIndex = lastSegment.indexOf(';'); - - if (matrixIndex != -1) { - return lastSegment.substring(matrixIndex + 1); - } - } - - // No matrix found - return null; - } - - /** - * Returns the optionally decoded matrix. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded matrix. - * @see #getMatrix() - */ - public String getMatrix(boolean decode) { - return decode ? decode(getMatrix()) : getMatrix(); - } - - /** - * Returns the optional matrix as a form. - * - * @return The optional matrix component as a form. - */ - public Form getMatrixAsForm() { - return new Form(getMatrix(), ';'); - } - - /** - * Returns the optional matrix as a form submission. - * - * @param characterSet The supported character encoding. - * @return The optional matrix as a form. - */ - public Form getMatrixAsForm(CharacterSet characterSet) { - return new Form(getMatrix(), characterSet, ';'); - } - - /** - * Returns the parent reference of a hierarchical reference. The last slash of - * the path will be considered as the end of the parent path. - * - * @return The parent reference of a hierarchical reference. - */ - public Reference getParentRef() { - Reference result = null; - - if (isHierarchical()) { - String parentRef = null; - String path = getPath(); - - if (!path.equals("/") && !path.isEmpty()) { - if (path.endsWith("/")) { - path = path.substring(0, path.length() - 1); - } - - if (isAbsolute()) { - parentRef = getHostIdentifier() + path.substring(0, path.lastIndexOf('/') + 1); - } else { - parentRef = path.substring(0, path.lastIndexOf('/') + 1); - } - } else { - parentRef = this.internalRef; - } - - result = new Reference(parentRef); - } - - return result; - } - - /** - * Returns the path component for hierarchical identifiers. If not path is - * available it returns null.
- * Note that no URI decoding is done by this method. - * - * @return The path component for hierarchical identifiers. - */ - public String getPath() { - String result = null; - String part = isRelative() ? getRelativePart() : getSchemeSpecificPart(); - - if (part != null) { - if (part.startsWith("//")) { - // Authority found - int index1 = part.indexOf('/', 2); - - if (index1 != -1) { - // Path found - int index2 = part.indexOf('?'); - - if (index2 != -1) { - // Query found - result = part.substring(Math.min(index1, index2), index2); - } else { - // No query found - result = part.substring(index1); - } - } else { - // Path must be empty in this case - } - } else { - // No authority found - int index = part.indexOf('?'); - - if (index != -1) { - // Query found - result = part.substring(0, index); - } else { - // No query found - result = part; - } - } - } - - return result; - } - - /** - * Returns the optionally decoded path component. If not path is available it - * returns null. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded path component. - * @see #getPath() - */ - public String getPath(boolean decode) { - return decode ? decode(getPath()) : getPath(); - } - - /** - * Returns the optional query component for hierarchical identifiers.
- * Note that no URI decoding is done by this method. - * - * @return The query component or null. - */ - public String getQuery() { - if (hasQuery()) { - // Query found - if (hasFragment()) { - if (this.queryIndex < this.fragmentIndex) { - // Fragment found and query sign not inside fragment - return this.internalRef.substring(this.queryIndex + 1, this.fragmentIndex); - } - - return null; - } - - // No fragment found - return this.internalRef.substring(this.queryIndex + 1); - } - - // No query found - return null; - } - - /** - * Returns the optionally decoded query component. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded query component. - * @see #getQuery() - */ - public String getQuery(boolean decode) { - return decode ? decode(getQuery()) : getQuery(); - } - - /** - * Returns the optional query component as a form. - * - * @return The optional query component as a form. - */ - public Form getQueryAsForm() { - return new Form(getQuery()); - } - - /** - * Returns the optional query component as a form. - * - * @param decode Indicates if the names and values should be automatically - * decoded. - * @return The optional query component as a form. - */ - public Form getQueryAsForm(boolean decode) { - return new Form(getQuery(), decode); - } - - /** - * Returns the optional query component as a form submission. - * - * @param characterSet The supported character encoding. - * @return The optional query component as a form submission. - */ - public Form getQueryAsForm(CharacterSet characterSet) { - return new Form(getQuery(), characterSet); - } - - /** - * Returns the relative part of relative references, without the query and - * fragment. If the reference is absolute, then null is returned.
- * Note that no URI decoding is done by this method. - * - * @return The relative part. - */ - public String getRelativePart() { - return isRelative() ? toString(false, false) : null; - } - - /** - * Returns the optionally decoded relative part. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded relative part. - * @see #getRelativePart() - */ - public String getRelativePart(boolean decode) { - return decode ? decode(getRelativePart()) : getRelativePart(); - } - - /** - * Returns the current reference as a relative reference to the current base - * reference. This method should only be invoked for absolute references, - * otherwise an IllegalArgumentException will be raised. - * - * @return The current reference as a relative reference to the current base - * reference. - * @see #getRelativeRef(Reference) - */ - public Reference getRelativeRef() { - return getRelativeRef(getBaseRef()); - } - - /** - * Returns the current reference relatively to a base reference. This method - * should only be invoked for absolute references, otherwise an - * IllegalArgumentException will be raised. - * - * @param base The base reference to use. - * @throws IllegalArgumentException If the relative reference is computed - * although the reference or the base reference - * are not absolute or not hierarchical. - * @return The current reference relatively to a base reference. - */ - public Reference getRelativeRef(Reference base) { - Reference result = null; - - if (base == null) { - result = this; - } else if (!isAbsolute() || !isHierarchical()) { - throw new IllegalArgumentException("The reference must have an absolute hierarchical path component"); - } else if (!base.isAbsolute() || !base.isHierarchical()) { - throw new IllegalArgumentException("The base reference must have an absolute hierarchical path component"); - } else if (!getHostIdentifier().equals(base.getHostIdentifier())) { - result = this; - } else { - final String localPath = getPath(); - final String basePath = base.getPath(); - String relativePath = null; - - if ((basePath == null) || (localPath == null)) { - relativePath = localPath; - } else { - // Find the junction point - boolean diffFound = false; - int lastSlashIndex = -1; - int i = 0; - char current; - while (!diffFound && (i < localPath.length()) && (i < basePath.length())) { - current = localPath.charAt(i); - - if (current != basePath.charAt(i)) { - diffFound = true; - } else { - if (current == '/') { - lastSlashIndex = i; - } - i++; - } - } - - if (!diffFound) { - if (localPath.length() == basePath.length()) { - // Both paths are strictly equivalent - relativePath = "."; - } else if (i == localPath.length()) { - // End of local path reached - if (basePath.charAt(i) == '/') { - if ((i + 1) == basePath.length()) { - // Both paths are strictly equivalent - relativePath = "."; - } else { - // The local path is a direct parent of the base - // path - // We need to add enough ".." in the relative - // path - final StringBuilder sb = new StringBuilder(); - - // Count segments - int segments = 0; - for (int j = basePath.indexOf('/', i); j != -1; j = basePath.indexOf('/', j + 1)) - segments++; - - // Build relative path - for (int j = 0; j < segments; j++) - sb.append("../"); - - int lastLocalSlash = localPath.lastIndexOf('/'); - sb.append(localPath.substring(lastLocalSlash + 1)); - - relativePath = sb.toString(); - } - } else { - // The base path has a segment that starts like - // the last local path segment - // But that is longer. Situation similar to a - // junction - final StringBuilder sb = new StringBuilder(); - - // Count segments - int segments = 0; - for (int j = basePath.indexOf('/', i); j != -1; j = basePath.indexOf('/', j + 1)) - segments++; - - // Build relative path - for (int j = 0; j < segments; j++) - if (j > 0) - sb.append("/.."); - else - sb.append(".."); - - relativePath = sb.toString(); - - if (relativePath.isEmpty()) { - relativePath = "."; - } - } - } else if (i == basePath.length()) { - if (localPath.charAt(i) == '/') { - if ((i + 1) == localPath.length()) { - // Both paths are strictly equivalent - relativePath = "."; - } else { - // The local path is a direct child of the base - // path - relativePath = localPath.substring(i + 1); - } - } else { - if (lastSlashIndex == (i - 1)) { - // The local path is a direct subpath of the - // base path - relativePath = localPath.substring(i); - } else { - relativePath = ".." + localPath.substring(lastSlashIndex); - } - } - } - } else { - // We found a junction point, we need to add enough ".." in - // the relative path and append the rest of the local path - // the local path is a direct subpath of the base path - final StringBuilder sb = new StringBuilder(); - - // Count segments - int segments = 0; - for (int j = basePath.indexOf('/', i); j != -1; j = basePath.indexOf('/', j + 1)) - segments++; - - // Build relative path - for (int j = 0; j < segments; j++) - sb.append("../"); - - sb.append(localPath.substring(lastSlashIndex + 1)); - - relativePath = sb.toString(); - } - } - - // Build the result reference - result = new Reference(); - final String query = getQuery(); - final String fragment = getFragment(); - boolean modified = false; - - if ((query != null) && (!query.equals(base.getQuery()))) { - result.setQuery(query); - modified = true; - } - - if ((fragment != null) && (!fragment.equals(base.getFragment()))) { - result.setFragment(fragment); - modified = true; - } - - if (!modified || !".".equals(relativePath)) { - result.setPath(relativePath); - } - } - - return result; - } - - /** - * Returns the part of the resource identifier remaining after the base - * reference. Note that the optional fragment is not returned by this method. - * Must be used with the following prerequisites: - *

    - *
  • the reference is absolute
  • - *
  • the reference identifier starts with the resource baseRef
  • - *
- *
- * Note that no URI decoding is done by this method. - * - * @return The remaining resource part or null if the prerequisites are not - * satisfied. - * @see #getRemainingPart(boolean) - */ - public String getRemainingPart() { - return getRemainingPart(false, true); - } - - /** - * Returns the optionally decoded remaining part. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded remaining part. - * @see #getRemainingPart() - */ - public String getRemainingPart(boolean decode) { - return getRemainingPart(decode, true); - } - - /** - * Returns the optionally decoded remaining part with or without the query part - * of the reference. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @param query True if the query part should be returned, false otherwise. - * @return The optionally decoded remaining part. - * @see #getRemainingPart() - */ - public String getRemainingPart(boolean decode, boolean query) { - String result = null; - final String all = toString(query, false); - - if (getBaseRef() != null) { - final String base = getBaseRef().toString(query, false); - - if ((base != null) && all.startsWith(base)) { - result = all.substring(base.length()); - } - } else { - result = all; - } - - return decode ? decode(result) : result; - } - - /** - * Returns the scheme component.
- * Note that no URI decoding is done by this method. - * - * @return The scheme component. - */ - public String getScheme() { - if (hasScheme()) { - // Scheme found - return this.internalRef.substring(0, this.schemeIndex); - } - - // No scheme found - return null; - } - - /** - * Returns the optionally decoded scheme component. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded scheme component. - * @see #getScheme() - */ - public String getScheme(boolean decode) { - return decode ? decode(getScheme()) : getScheme(); - } - - /** - * Returns the protocol associated with the scheme component. - * - * @return The protocol associated with the scheme component. - */ - public Protocol getSchemeProtocol() { - return Protocol.valueOf(getScheme()); - } - - /** - * Returns the scheme specific part.
- * Note that no URI decoding is done by this method. - * - * @return The scheme specific part. - */ - public String getSchemeSpecificPart() { - String result = null; - - if (hasScheme()) { - // Scheme found - if (hasFragment()) { - // Fragment found - result = this.internalRef.substring(this.schemeIndex + 1, this.fragmentIndex); - } else { - // No fragment found - result = this.internalRef.substring(this.schemeIndex + 1); - } - } - - return result; - } - - /** - * Returns the optionally decoded scheme specific part. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded scheme specific part. - * @see #getSchemeSpecificPart() - */ - public String getSchemeSpecificPart(boolean decode) { - return decode ? decode(getSchemeSpecificPart()) : getSchemeSpecificPart(); - } - - /** - * Returns the list of segments in a hierarchical path.
- * A new list is created for each call.
- * Note that no URI decoding is done by this method. - * - * @return The segments of a hierarchical path. - */ - public List getSegments() { - final List result = new ArrayList(); - final String path = getPath(); - int start = -2; // The index of the slash starting the segment - char current; - - if (path != null) { - for (int i = 0; i < path.length(); i++) { - current = path.charAt(i); - - if (current == '/') { - if (start == -2) { - // Beginning of an absolute path or sequence of two - // separators - start = i; - } else { - // End of a segment - result.add(path.substring(start + 1, i)); - start = i; - } - } else { - if (start == -2) { - // Starting a new segment for a relative path - start = -1; - } else { - // Looking for the next character - } - } - } - - if (start != -2) { - // Add the last segment - result.add(path.substring(start + 1)); - } - } - - return result; - } - - /** - * Returns the optionally decoded list of segments. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded list of segments. - * @see #getSegments() - */ - public List getSegments(boolean decode) { - final List result = getSegments(); - - if (decode) { - for (int i = 0; i < result.size(); i++) { - result.set(i, decode(result.get(i))); - } - } - - return result; - } - - /** - * Returns the target reference. This method resolves relative references - * against the base reference then normalize them. - * - * @throws IllegalArgumentException If the base reference (after resolution) is - * not absolute. - * @throws IllegalArgumentException If the reference is relative and not base - * reference has been provided. - * - * @return The target reference. - */ - public Reference getTargetRef() { - Reference result = null; - - // Step 1 - Resolve relative reference against their base reference - if (isRelative() && (this.baseRef != null)) { - Reference baseReference = null; - - if (this.baseRef.isAbsolute()) { - baseReference = this.baseRef; - } else { - baseReference = this.baseRef.getTargetRef(); - } - - if (baseReference.isRelative()) { - throw new IllegalArgumentException( - "The base reference must have an absolute hierarchical path component"); - } - - // Relative URI detected - String authority = getAuthority(); - String path = getPath(); - String query = getQuery(); - String fragment = getFragment(); - - // Create an empty reference - result = new Reference(); - result.setScheme(baseReference.getScheme()); - - if (authority != null) { - result.setAuthority(authority); - result.setPath(path); - result.setQuery(query); - } else { - result.setAuthority(baseReference.getAuthority()); - - if ((path == null) || (path.isEmpty())) { - result.setPath(baseReference.getPath()); - - if (query != null) { - result.setQuery(query); - } else { - result.setQuery(baseReference.getQuery()); - } - } else { - if (path.startsWith("/")) { - result.setPath(path); - } else { - final String basePath = baseReference.getPath(); - String mergedPath = null; - - if ((baseReference.getAuthority() != null) && ((basePath == null) || (basePath.isEmpty()))) { - mergedPath = "/" + path; - } else { - // Remove the last segment which may be empty if - // the path is ending with a slash - final int lastSlash = basePath.lastIndexOf('/'); - if (lastSlash == -1) { - mergedPath = path; - } else { - mergedPath = basePath.substring(0, lastSlash + 1) + path; - } - } - - result.setPath(mergedPath); - } - - result.setQuery(query); - } - } - - result.setFragment(fragment); - } else if (isRelative()) { - // Relative reference with no baseRef detected - throw new IllegalArgumentException("Relative references are only usable when a base reference is set."); - } else { - // Absolute URI detected - result = new Reference(this.internalRef); - } - - // Step 2 - Normalize the target reference - result.normalize(); - - return result; - } - - /** - * Returns the user info component for server based hierarchical - * identifiers.
- * Note that no URI decoding is done by this method. - * - * @return The user info component for server based hierarchical identifiers. - */ - public String getUserInfo() { - String result = null; - final String authority = getAuthority(); - - if (authority != null) { - final int index = authority.indexOf('@'); - - if (index != -1) { - result = authority.substring(0, index); - } - } - - return result; - } - - /** - * Returns the optionally decoded user info component. - * - * @param decode Indicates if the result should be decoded using the - * {@link #decode(String)} method. - * @return The optionally decoded user info component. - * @see #getUserInfo() - */ - public String getUserInfo(boolean decode) { - return decode ? decode(getUserInfo()) : getUserInfo(); - } - - /** - * Indicates if this reference has file-like extensions on its last path - * segment. - * - * @return True if there is are extensions. - * @see #getExtensions() - */ - public boolean hasExtensions() { - boolean result = false; - - // If these reference ends with a "/", it cannot be a file. - final String path = getPath(); - if (!((path != null) && path.endsWith("/"))) { - final String lastSegment = getLastSegment(); - - if (lastSegment != null) { - final int extensionsIndex = lastSegment.indexOf('.'); - final int matrixIndex = lastSegment.indexOf(';'); - result = (extensionsIndex != -1) && ((matrixIndex == -1) || (extensionsIndex < matrixIndex)); - } - } - - return result; - } - - /** - * Indicates if this reference has a fragment identifier. - * - * @return True if there is a fragment identifier. - */ - public boolean hasFragment() { - return (this.fragmentIndex != -1); - } - - /** - * Returns a hash code value for the object. - * - * @return A hash code value for the object. - */ - @Override - public int hashCode() { - return (this.internalRef == null) ? 0 : this.internalRef.hashCode(); - } - - /** - * Indicates if this reference has a matrix. - * - * @return True if there is a matrix. - * @see #getMatrix() - */ - public boolean hasMatrix() { - return (getLastSegment().indexOf(';') != -1); - } - - /** - * Indicates if this reference has a query component. - * - * @return True if there is a query. - */ - public boolean hasQuery() { - return (this.queryIndex != -1); - } - - /** - * Indicates if this reference has a scheme component. - * - * @return True if there is a scheme component. - */ - public boolean hasScheme() { - return (this.schemeIndex != -1); - } - - /** - * Indicates if the reference is absolute. - * - * @return True if the reference is absolute. - */ - public boolean isAbsolute() { - return (getScheme() != null); - } - - /** - * Returns true if both reference are equivalent, meaning that they resolve to - * the same target reference. - * - * @param ref The reference to compare. - * @return True if both reference are equivalent. - */ - public boolean isEquivalentTo(Reference ref) { - return getTargetRef().equals(ref.getTargetRef()); - } - - /** - * Indicates if the identifier is hierarchical. - * - * @return True if the identifier is hierarchical, false if it is opaque. - */ - public boolean isHierarchical() { - return isRelative() || (getSchemeSpecificPart().charAt(0) == '/'); - } - - /** - * Indicates if the identifier is opaque. - * - * @return True if the identifier is opaque, false if it is hierarchical. - */ - public boolean isOpaque() { - return isAbsolute() && (getSchemeSpecificPart().charAt(0) != '/'); - } - - /** - * Indicates if the reference is a parent of the hierarchical child reference. - * - * @param childRef The hierarchical reference. - * @return True if the reference is a parent of the hierarchical child - * reference. - */ - public boolean isParent(Reference childRef) { - boolean result = false; - - if ((childRef != null) && (childRef.isHierarchical())) { - result = childRef.toString(false, false).startsWith(toString(false, false)); - } - - return result; - } - - /** - * Indicates if the reference is relative. - * - * @return True if the reference is relative. - */ - public boolean isRelative() { - return (getScheme() == null); - } - - /** - * Normalizes the reference. Useful before comparison between references or when - * building a target reference from a base and a relative references. - * - * @return The current reference. - */ - public Reference normalize() { - // 1. The input buffer is initialized with the now-appended path - // components and the output buffer is initialized to the empty string. - StringBuilder output = new StringBuilder(); - StringBuilder input = new StringBuilder(); - String path = getPath(); - - if (path != null) { - input.append(path); - } - - // 2. While the input buffer is not empty, loop as follows: - while (input.length() > 0) { - // A. If the input buffer begins with a prefix of "../" or "./", - // then remove that prefix from the input buffer; otherwise, - if ((input.length() >= 3) && input.substring(0, 3).equals("../")) { - input.delete(0, 3); - } else if ((input.length() >= 2) && input.substring(0, 2).equals("./")) { - input.delete(0, 2); - } - - // B. if the input buffer begins with a prefix of "/./" or "/.", - // where "." is a complete path segment, then replace that - // prefix with "/" in the input buffer; otherwise, - else if ((input.length() >= 3) && input.substring(0, 3).equals("/./")) { - input.delete(0, 2); - } else if ((input.length() == 2) && input.substring(0, 2).equals("/.")) { - input.delete(1, 2); - } - - // C. if the input buffer begins with a prefix of "/../" or "/..", - // where ".." is a complete path segment, then replace that prefix - // with "/" in the input buffer and remove the last segment and its - // preceding "/" (if any) from the output buffer; otherwise, - else if ((input.length() >= 4) && input.substring(0, 4).equals("/../")) { - input.delete(0, 3); - removeLastSegment(output); - } else if ((input.length() == 3) && input.substring(0, 3).equals("/..")) { - input.delete(1, 3); - removeLastSegment(output); - } - - // D. if the input buffer consists only of "." or "..", then remove - // that from the input buffer; otherwise, - else if ((input.length() == 1) && input.substring(0, 1).equals(".")) { - input.delete(0, 1); - } else if ((input.length() == 2) && input.substring(0, 2).equals("..")) { - input.delete(0, 2); - } - - // E. move the first path segment in the input buffer to the end of - // the output buffer, including the initial "/" character (if any) - // and any subsequent characters up to, but not including, the next - // "/" character or the end of the input buffer. - else { - int max = -1; - for (int i = 1; (max == -1) && (i < input.length()); i++) { - if (input.charAt(i) == '/') { - max = i; - } - } - - if (max != -1) { - // We found the next "/" character. - output.append(input.substring(0, max)); - input.delete(0, max); - } else { - // End of input buffer reached - output.append(input); - input.delete(0, input.length()); - } - } - } - - // Finally, the output buffer is returned as the result - setPath(output.toString()); - - // Ensure that the scheme and host names are reset in lower case - setScheme(getScheme()); - setHostDomain(getHostDomain()); - - // Remove the port if it is equal to the default port of the reference's - // Protocol. - final int hostPort = getHostPort(); - if (hostPort != -1) { - final int defaultPort = Protocol.valueOf(getScheme()).getDefaultPort(); - if (hostPort == defaultPort) { - setHostPort(null); - } - } - - return this; - } - - /** - * Removes the last segement from the output builder. - * - * @param output The output builder to update. - */ - private void removeLastSegment(StringBuilder output) { - int min = -1; - for (int i = output.length() - 1; (min == -1) && (i >= 0); i--) { - if (output.charAt(i) == '/') { - min = i; - } - } - - if (min != -1) { - // We found the previous "/" character. - output.delete(min, output.length()); - } else { - // End of output buffer reached - output.delete(0, output.length()); - } - - } - - /** - * Sets the authority component for hierarchical identifiers. - * - * @param authority The authority component for hierarchical identifiers. - */ - public void setAuthority(String authority) { - final String oldPart = isRelative() ? getRelativePart() : getSchemeSpecificPart(); - String newPart; - final String newAuthority = (authority == null) ? "" : "//" + authority; - - if (oldPart == null) { - newPart = newAuthority; - } else if (oldPart.startsWith("//")) { - int index = oldPart.indexOf('/', 2); - - if (index != -1) { - newPart = newAuthority + oldPart.substring(index); - } else { - index = oldPart.indexOf('?'); - if (index != -1) { - newPart = newAuthority + oldPart.substring(index); - } else { - newPart = newAuthority; - } - } - } else { - newPart = newAuthority + oldPart; - } - - if (isAbsolute()) { - setSchemeSpecificPart(newPart); - } else { - setRelativePart(newPart); - } - } - - /** - * Sets the base reference for relative references. - * - * @param baseRef The base reference for relative references. - */ - public void setBaseRef(Reference baseRef) { - this.baseRef = baseRef; - } - - /** - * Sets the base reference for relative references. - * - * @param baseUri The base URI for relative references. - */ - public void setBaseRef(String baseUri) { - setBaseRef(new Reference(baseUri)); - } - - /** - * Sets the extensions for hierarchical identifiers. An extensions part starts - * after the first '.' character of the last path segment and ends with either - * the end of the segment of with the first ';' character (matrix start). It is - * a token similar to file extensions separated by '.' characters. The value can - * be ommited.
- * Note that no URI decoding is done by this method. - * - * @param extensions The extensions to set or null (without leading or trailing - * dots). - * @see #getExtensions() - * @see #getExtensionsAsArray() - * @see #setExtensions(String[]) - */ - public void setExtensions(String extensions) { - final String lastSegment = getLastSegment(); - - if (lastSegment != null) { - final int extensionIndex = lastSegment.indexOf('.'); - final int matrixIndex = lastSegment.indexOf(';'); - final StringBuilder sb = new StringBuilder(); - - if (extensionIndex != -1) { - // Extensions found - sb.append(lastSegment, 0, extensionIndex); - - if ((extensions != null) && (!extensions.isEmpty())) { - sb.append('.').append(extensions); - } - - if (matrixIndex != -1) { - sb.append(lastSegment.substring(matrixIndex)); - } - } else { - // Extensions not found - if ((extensions != null) && (!extensions.isEmpty())) { - if (matrixIndex != -1) { - // Matrix found, make sure we append it - // after the extensions - sb.append(lastSegment, 0, matrixIndex).append('.').append(extensions) - .append(lastSegment.substring(matrixIndex)); - } else { - // No matrix found, just append the extensions - sb.append(lastSegment).append('.').append(extensions); - } - } else { - // No change necessary - sb.append(lastSegment); - } - } - - // Finally update the last segment - setLastSegment(sb.toString()); - } else { - setLastSegment('.' + extensions); - } - } - - /** - * Sets the extensions based on an array of extension tokens (without dots). - * - * @param extensions The array of extensions. - * @see #getExtensions() - * @see #getExtensionsAsArray() - * @see #setExtensions(String) - */ - public void setExtensions(String[] extensions) { - String exts = null; - - if (extensions != null) { - final StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < extensions.length; i++) { - if (i > 0) { - sb.append('.'); - } - - sb.append(extensions[i]); - } - - exts = sb.toString(); - } - - setExtensions(exts); - } - - /** - * Sets the fragment identifier. - * - * @param fragment The fragment identifier. - * @throws IllegalArgumentException if the fragment parameter contains the - * fragment delimiter ('#'). - */ - public void setFragment(String fragment) { - fragment = encodeInvalidCharacters(fragment); - - if ((fragment != null) && (fragment.indexOf('#') != -1)) { - throw new IllegalArgumentException("Illegal '#' character detected in parameter"); - } - - if (hasFragment()) { - // Existing fragment - if (fragment != null) { - this.internalRef = this.internalRef.substring(0, this.fragmentIndex + 1) + fragment; - } else { - this.internalRef = this.internalRef.substring(0, this.fragmentIndex); - } - } else { - // No existing fragment - if (fragment != null) { - if (this.internalRef != null) { - this.internalRef = this.internalRef + '#' + fragment; - } else { - this.internalRef = '#' + fragment; - } - } else { - // Do nothing - } - } - - updateIndexes(); - } - - /** - * Sets the host domain component for server based hierarchical identifiers. - * - * @param domain The host component for server based hierarchical identifiers. - */ - public void setHostDomain(String domain) { - final String authority = getAuthority(); - - if (authority == null) { - setAuthority(domain); - } else { - if (domain == null) { - domain = ""; - } else { - // URI specification indicates that host names should be - // produced in lower case - domain = domain.toLowerCase(); - } - - // We must prevent the case where the userinfo part contains ':' - // and the case of IPV6 addresses - int indexUI = authority.indexOf('@'); // user info - int indexIPV6 = authority.indexOf(']'); // IPV6 - int indexP = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); - - if (indexUI != -1) { - // User info found - if (indexP != -1) { - // Port found - setAuthority(authority.substring(0, indexUI + 1) + domain + authority.substring(indexP)); - } else { - // No port found - setAuthority(authority.substring(0, indexUI + 1) + domain); - } - } else { - // No user info found - if (indexP != -1) { - // Port found - setAuthority(domain + authority.substring(indexP)); - } else { - // No port found - setAuthority(domain); - } - } - } - } - - /** - * Sets the optional port number for server based hierarchical identifiers. - * - * @param port The optional port number for server based hierarchical - * identifiers. - * @throws IllegalArgumentException If the autority has not been defined. - */ - public void setHostPort(Integer port) { - final String authority = getAuthority(); - - if (authority != null) { - // We must prevent the case where the userinfo part contains ':' - // and the case of IPV6 addresses - int indexUI = authority.indexOf('@'); // user info - int indexIPV6 = authority.indexOf(']'); // IPV6 - int index = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); - String newPort = (port == null) ? "" : ":" + port; - - if (index != -1) { - setAuthority(authority.substring(0, index) + newPort); - } else { - setAuthority(authority + newPort); - } - } else { - throw new IllegalArgumentException("No authority defined, please define a host name first"); - } - } - - /** - * Sets the absolute resource identifier. - * - * @param identifier The absolute resource identifier. - * @throws IllegalArgumentException If the identifier parameter contains the - * fragment delimiter ('#'). - */ - public void setIdentifier(String identifier) { - identifier = encodeInvalidCharacters(identifier); - - if (identifier == null) { - identifier = ""; - } - - if (identifier.indexOf('#') != -1) { - throw new IllegalArgumentException("Illegal '#' character detected in parameter"); - } - - if (hasFragment()) { - // Fragment found - this.internalRef = identifier + this.internalRef.substring(this.fragmentIndex); - } else { - // No fragment found - this.internalRef = identifier; - } - - updateIndexes(); - } - - /** - * Sets the last segment of the path. If no path is available, then it creates - * one and adds a slash in front of the given last segmetn.
- * Note that no URI decoding is done by this method. - * - * @param lastSegment The last segment of a hierarchical path. - */ - public void setLastSegment(String lastSegment) { - String path = getPath(); - - if (path != null) { - int lastSlashIndex = path.lastIndexOf('/'); - - if (lastSlashIndex != -1) { - setPath(path.substring(0, lastSlashIndex + 1) + lastSegment); - return; - } - } - - setPath('/' + lastSegment); - } - - /** - * Sets the path component for hierarchical identifiers. - * - * @param path The path component for hierarchical identifiers. - */ - public void setPath(String path) { - final String oldPart = isRelative() ? getRelativePart() : getSchemeSpecificPart(); - String newPart = null; - - if (oldPart != null) { - if (path == null) { - path = ""; - } - - if (oldPart.startsWith("//")) { - // Authority found - final int index1 = oldPart.indexOf('/', 2); - - if (index1 != -1) { - // Path found - final int index2 = oldPart.indexOf('?'); - - if (index2 != -1) { - // Query found - newPart = oldPart.substring(0, index1) + path + oldPart.substring(index2); - } else { - // No query found - newPart = oldPart.substring(0, index1) + path; - } - } else { - // No path found - final int index2 = oldPart.indexOf('?'); - - if (index2 != -1) { - // Query found - newPart = oldPart.substring(0, index2) + path + oldPart.substring(index2); - } else { - // No query found - newPart = oldPart + path; - } - } - } else { - // No authority found - final int index = oldPart.indexOf('?'); - - if (index != -1) { - // Query found - newPart = path + oldPart.substring(index); - } else { - // No query found - newPart = path; - } - } - } else { - newPart = path; - } - - if (isAbsolute()) { - setSchemeSpecificPart(newPart); - } else { - setRelativePart(newPart); - } - } - - /** - * Sets the scheme component based on this protocol. - * - * @param protocol The protocol of the scheme component. - */ - public void setProtocol(Protocol protocol) { - setScheme(protocol.getSchemeName()); - } - - /** - * Sets the query component for hierarchical identifiers. - * - * @param query The query component for hierarchical identifiers. - */ - public void setQuery(String query) { - query = encodeInvalidCharacters(query); - final boolean emptyQueryString = ((query == null) || query.isEmpty()); - - if (hasQuery()) { - // Query found - if (hasFragment()) { - // Fragment found - if (!emptyQueryString) { - this.internalRef = this.internalRef.substring(0, this.queryIndex + 1) + query - + this.internalRef.substring(this.fragmentIndex); - } else { - this.internalRef = this.internalRef.substring(0, this.queryIndex) - + this.internalRef.substring(this.fragmentIndex); - } - } else { - // No fragment found - if (!emptyQueryString) { - this.internalRef = this.internalRef.substring(0, this.queryIndex + 1) + query; - } else { - this.internalRef = this.internalRef.substring(0, this.queryIndex); - } - } - } else { - // No query found - if (hasFragment()) { - // Fragment found - if (!emptyQueryString) { - this.internalRef = this.internalRef.substring(0, this.fragmentIndex) + '?' + query - + this.internalRef.substring(this.fragmentIndex); - } else { - // Do nothing; - } - } else { - // No fragment found - if (!emptyQueryString) { - if (this.internalRef != null) { - this.internalRef = this.internalRef + '?' + query; - } else { - this.internalRef = '?' + query; - } - } else { - // Do nothing; - } - } - } - - updateIndexes(); - } - - /** - * Sets the relative part for relative references only. - * - * @param relativePart The relative part to set. - */ - public void setRelativePart(String relativePart) { - relativePart = encodeInvalidCharacters(relativePart); - - if (relativePart == null) { - relativePart = ""; - } - - if (!hasScheme()) { - // This is a relative reference, no scheme found - if (hasQuery()) { - // Query found - this.internalRef = relativePart + this.internalRef.substring(this.queryIndex); - } else if (hasFragment()) { - // Fragment found - this.internalRef = relativePart + this.internalRef.substring(this.fragmentIndex); - } else { - // No fragment found - this.internalRef = relativePart; - } - } - - updateIndexes(); - } - - /** - * Sets the scheme component. - * - * @param scheme The scheme component. - */ - public void setScheme(String scheme) { - scheme = encodeInvalidCharacters(scheme); - - if (scheme != null) { - // URI specification indicates that scheme names should be - // produced in lower case - scheme = scheme.toLowerCase(); - } - - if (hasScheme()) { - // Scheme found - if (scheme != null) { - this.internalRef = scheme + this.internalRef.substring(this.schemeIndex); - } else { - this.internalRef = this.internalRef.substring(this.schemeIndex + 1); - } - } else { - // No scheme found - if (scheme != null) { - if (this.internalRef == null) { - this.internalRef = scheme + ':'; - } else { - this.internalRef = scheme + ':' + this.internalRef; - } - } - } - - updateIndexes(); - } - - /** - * Sets the scheme specific part. - * - * @param schemeSpecificPart The scheme specific part. - */ - public void setSchemeSpecificPart(String schemeSpecificPart) { - schemeSpecificPart = encodeInvalidCharacters(schemeSpecificPart); - - if (schemeSpecificPart == null) { - schemeSpecificPart = ""; - } - - if (hasScheme()) { - // Scheme found - if (hasFragment()) { - // Fragment found - this.internalRef = this.internalRef.substring(0, this.schemeIndex + 1) + schemeSpecificPart - + this.internalRef.substring(this.fragmentIndex); - } else { - // No fragment found - this.internalRef = this.internalRef.substring(0, this.schemeIndex + 1) + schemeSpecificPart; - } - } else { - // No scheme found - if (hasFragment()) { - // Fragment found - this.internalRef = schemeSpecificPart + this.internalRef.substring(this.fragmentIndex); - } else { - // No fragment found - this.internalRef = schemeSpecificPart; - } - } - - updateIndexes(); - } - - /** - * Sets the segments of a hierarchical path.
- * A new absolute path will replace any existing one. - * - * @param segments The segments of the hierarchical path. - */ - public void setSegments(List segments) { - final StringBuilder sb = new StringBuilder(); - - for (final String segment : segments) { - sb.append('/').append(segment); - } - - setPath(sb.toString()); - } - - /** - * Sets the user info component for server based hierarchical identifiers. - * - * @param userInfo The user info component for server based hierarchical - * identifiers. - * @throws IllegalArgumentException If the autority part has not been defined. - */ - public void setUserInfo(String userInfo) { - final String authority = getAuthority(); - - if (authority != null) { - final int index = authority.indexOf('@'); - final String newUserInfo = (userInfo == null) ? "" : userInfo + '@'; - - if (index != -1) { - setAuthority(newUserInfo + authority.substring(index + 1)); - } else { - setAuthority(newUserInfo + authority); - } - } else { - throw new IllegalArgumentException("No authority defined, please define a host name first"); - } - } - - /** - * Returns the reference as an URI string. - * - * @return The reference as an URI string. - */ - @Override - public String toString() { - return this.internalRef; - } - - /** - * Returns the URI reference string. - * - * @param query Indicates if the query should be included; - * @param fragment Indicates if the fragment should be included; - * @return The URI reference string. - */ - public String toString(boolean query, boolean fragment) { - if (query) { - if (fragment) { - return this.internalRef; - } - - if (hasFragment()) { - return this.internalRef.substring(0, this.fragmentIndex); - } - return this.internalRef; - } - - if (fragment) { - // Fragment should be included - if (hasQuery()) { - // Query found - if (hasFragment()) { - // Fragment found - return this.internalRef.substring(0, this.queryIndex) + "#" + getFragment(); - } - - // No fragment found - return this.internalRef.substring(0, this.queryIndex); - } - - // No query found - return this.internalRef; - } - - // Fragment should not be included - if (hasQuery()) { - // Query found - return this.internalRef.substring(0, this.queryIndex); - } - if (hasFragment()) { - // Fragment found - return this.internalRef.substring(0, this.fragmentIndex); - } - - return this.internalRef; - } - - /** - * Converts to a {@link java.net.URI} instance. Note that relative references - * are resolved before conversion using the {@link #getTargetRef()} method. - * - * @return A {@link java.net.URI} instance. - */ - public java.net.URI toUri() { - return java.net.URI.create(getTargetRef().toString()); - } - - /** - * Converts to a {@link java.net.URL} instance. Note that relative references - * are resolved before conversion using the {@link #getTargetRef()} method. - * - * @return A {@link java.net.URL} instance. - */ - public java.net.URL toUrl() { - java.net.URL result = null; - - try { - result = new java.net.URL(getTargetRef().toString()); - } catch (java.net.MalformedURLException e) { - throw new IllegalArgumentException("Malformed URL exception", e); - } - - return result; - } - - /** - * Updates internal indexes. - */ - private void updateIndexes() { - if (this.internalRef != null) { - // Compute the indexes - final int firstSlashIndex = this.internalRef.indexOf('/'); - this.schemeIndex = this.internalRef.indexOf(':'); - - if ((firstSlashIndex != -1) && (this.schemeIndex > firstSlashIndex)) { - // We are in the rare case of a relative reference where one of - // the path segments contains a colon character. In this case, - // we ignore the colon as a valid scheme index. - // Note that this colon can't be in the first segment as it is - // forbidden by the URI RFC. - this.schemeIndex = -1; - } - - this.queryIndex = this.internalRef.indexOf('?'); - this.fragmentIndex = this.internalRef.indexOf('#'); - - if (hasQuery() && hasFragment() && (this.queryIndex > this.fragmentIndex)) { - // Query sign inside fragment - this.queryIndex = -1; - } - - if (hasQuery() && this.schemeIndex > this.queryIndex) { - // Colon sign inside query - this.schemeIndex = -1; - } - - if (hasFragment() && this.schemeIndex > this.fragmentIndex) { - // Colon sign inside fragment - this.schemeIndex = -1; - } - } else { - this.schemeIndex = -1; - this.queryIndex = -1; - this.fragmentIndex = -1; - } - } + /** Helps to map characters and their validity as URI characters. */ + private static final boolean[] charValidityMap = new boolean[127]; + + static { + // Initialize the map of valid characters. + for (int character = 0; character < 127; character++) { + charValidityMap[character] = + isReserved(character) + || isUnreserved(character) + || (character == '%') + || (character == '{') + || (character == '}'); + } + } + + /** + * Decodes a given string using the standard URI encoding mechanism and the UTF-8 character set. + * + * @param toDecode The string to decode. + * @return The decoded string. + */ + public static String decode(String toDecode) { + return decode(toDecode, CharacterSet.UTF_8); + } + + /** + * Decodes a given string using the standard URI encoding mechanism. If the provided character + * set is null, the string is returned but not decoded. Note: The World Wide Web + * Consortium Recommendation states that UTF-8 should be used. Not doing so may introduce + * incompatibilities. + * + * @param toDecode The string to decode. + * @param characterSet The name of a supported character encoding. + * @return The decoded string or null if the named character encoding is not supported. + */ + public static String decode(String toDecode, CharacterSet characterSet) { + String result = null; + try { + result = + (characterSet == null) + ? toDecode + : java.net.URLDecoder.decode(toDecode, characterSet.getName()); + } catch (UnsupportedEncodingException uee) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to decode the string with the UTF-8 character set.", + uee); + } + + return result; + } + + /** + * Encodes a given string using the standard URI encoding mechanism and the UTF-8 character set. + * + * @param toEncode The string to encode. + * @return The encoded string. + */ + public static String encode(String toEncode) { + return encode(toEncode, true, CharacterSet.UTF_8); + } + + /** + * Encodes a given string using the standard URI encoding mechanism and the UTF-8 character set. + * Useful to prevent the usage of '+' to encode spaces (%20 instead). The '*' characters are + * encoded as %2A and %7E are replaced by '~'. + * + * @param toEncode The string to encode. + * @param queryString True if the string to encode is part of a query string instead of a HTML + * form post. + * @return The encoded string. + */ + public static String encode(String toEncode, boolean queryString) { + return encode(toEncode, queryString, CharacterSet.UTF_8); + } + + /** + * Encodes a given string using the standard URI encoding mechanism and the UTF-8 character set. + * Useful to prevent the usage of '+' to encode spaces (%20 instead). The '*' characters are + * encoded as %2A and %7E are replaced by '~'. + * + * @param toEncode The string to encode. + * @param queryString True if the string to encode is part of a query string instead of a HTML + * form post. + * @param characterSet The supported character encoding. + * @return The encoded string. + */ + public static String encode(String toEncode, boolean queryString, CharacterSet characterSet) { + + String result = null; + + try { + result = + (characterSet == null) + ? toEncode + : java.net.URLEncoder.encode(toEncode, characterSet.getName()); + } catch (UnsupportedEncodingException uee) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to encode the string with the UTF-8 character set.", + uee); + } + + if (result != null && queryString) { + result = result.replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); + } + + return result; + } + + /** + * Encodes a given string using the standard URI encoding mechanism. If the provided character + * set is null, the string is returned but not encoded. + * + *

Note: The World Wide Web + * Consortium Recommendation states that UTF-8 should be used. Not doing so may introduce + * incompatibilities. + * + * @param toEncode The string to encode. + * @param characterSet The supported character encoding. + * @return The encoded string or null if the named character encoding is not supported. + */ + public static String encode(String toEncode, CharacterSet characterSet) { + return encode(toEncode, true, characterSet); + } + + /** + * Indicates if the given character is alphabetical (a-z or A-Z). + * + * @param character The character to test. + * @return True if the given character is alphabetical (a-z or A-Z). + */ + private static boolean isAlpha(int character) { + return isUpperCase(character) || isLowerCase(character); + } + + /** + * Indicates if the given character is a digit (0-9). + * + * @param character The character to test. + * @return True if the given character is a digit (0-9). + */ + private static boolean isDigit(int character) { + return (character >= '0') && (character <= '9'); + } + + /** + * Indicates if the given character is a generic URI component delimiter character. + * + * @param character The character to test. + * @return True if the given character is a generic URI component delimiter character. + */ + public static boolean isGenericDelimiter(int character) { + return (character == ':') + || (character == '/') + || (character == '?') + || (character == '#') + || (character == '[') + || (character == ']') + || (character == '@'); + } + + /** + * Indicates if the given character is lower case (a-z). + * + * @param character The character to test. + * @return True if the given character is lower case (a-z). + */ + private static boolean isLowerCase(int character) { + return (character >= 'a') && (character <= 'z'); + } + + /** + * Indicates if the given character is a reserved URI character. + * + * @param character The character to test. + * @return True if the given character is a reserved URI character. + */ + public static boolean isReserved(int character) { + return isGenericDelimiter(character) || isSubDelimiter(character); + } + + /** + * Indicates if the given character is a URI subcomponent delimiter character. + * + * @param character The character to test. + * @return True if the given character is a URI subcomponent delimiter character. + */ + public static boolean isSubDelimiter(int character) { + return (character == '!') + || (character == '$') + || (character == '&') + || (character == '\'') + || (character == '(') + || (character == ')') + || (character == '*') + || (character == '+') + || (character == ',') + || (character == ';') + || (character == '='); + } + + /** + * Indicates if the given character is an unreserved URI character. + * + * @param character The character to test. + * @return True if the given character is an unreserved URI character. + */ + public static boolean isUnreserved(int character) { + return isAlpha(character) + || isDigit(character) + || (character == '-') + || (character == '.') + || (character == '_') + || (character == '~'); + } + + /** + * Indicates if the given character is upper case (A-Z). + * + * @param character The character to test. + * @return True if the given character is upper case (A-Z). + */ + private static boolean isUpperCase(int character) { + return (character >= 'A') && (character <= 'Z'); + } + + /** + * Indicates if the given character is a valid URI character. + * + * @param character The character to test. + * @return True if the given character is a valid URI character. + */ + public static boolean isValid(int character) { + return character >= 0 && character < 127 && charValidityMap[character]; + } + + /** + * Creates a reference string from its parts. + * + * @param scheme The scheme ("http", "https" or "ftp"). + * @param hostName The host name or IP address. + * @param hostPort The host port (default ports are correctly ignored). + * @param path The path component for hierarchical identifiers. + * @param query The optional query component for hierarchical identifiers. + * @param fragment The optional fragment identifier. + * @return The reference as String. + */ + public static String toString( + String scheme, + String hostName, + Integer hostPort, + String path, + String query, + String fragment) { + String host = hostName; + + // Appends the host port number + if (hostPort != null) { + final int defaultPort = Protocol.valueOf(scheme).getDefaultPort(); + if (hostPort != defaultPort) { + host = hostName + ':' + hostPort; + } + } + + return toString(scheme, host, path, query, fragment); + } + + /** + * Creates a relative reference string from its parts. + * + * @param relativePart The relative part component. + * @param query The optional query component for hierarchical identifiers. + * @param fragment The optional fragment identifier. + * @return The relative reference as a String. + */ + public static String toString(String relativePart, String query, String fragment) { + final StringBuilder sb = new StringBuilder(); + + // Append the path + if (relativePart != null) { + sb.append(relativePart); + } + + // Append the query string + if (query != null) { + sb.append('?').append(query); + } + + // Append the fragment identifier + if (fragment != null) { + sb.append('#').append(fragment); + } + + // Actually construct the reference + return sb.toString(); + } + + /** + * Creates a reference string from its parts. + * + * @param scheme The scheme ("http", "https" or "ftp"). + * @param host The host name or IP address plus the optional port number. + * @param path The path component for hierarchical identifiers. + * @param query The optional query component for hierarchical identifiers. + * @param fragment The optional fragment identifier. + * @return The reference a String. + */ + public static String toString( + String scheme, String host, String path, String query, String fragment) { + final StringBuilder sb = new StringBuilder(); + + if (scheme != null) { + // Append the scheme and host name + sb.append(scheme.toLowerCase()).append("://").append(host); + } + + // Append the path + if (path != null) { + sb.append(path); + } + + // Append the query string + if (query != null) { + sb.append('?').append(query); + } + + // Append the fragment identifier + if (fragment != null) { + sb.append('#').append(fragment); + } + + // Actually construct the reference + return sb.toString(); + } + + /** The base reference for relative references. */ + private volatile Reference baseRef; + + /** The fragment separator index. */ + private volatile int fragmentIndex; + + /** The internal reference. */ + private volatile String internalRef; + + /** The query separator index. */ + private volatile int queryIndex; + + /** The scheme separator index. */ + private volatile int schemeIndex; + + /** Empty constructor. */ + public Reference() { + this((Reference) null, (String) null); + } + + /** + * Constructor from an {@link java.net.URI} instance. + * + * @param uri The {@link java.net.URI} instance. + */ + public Reference(java.net.URI uri) { + this(uri.toString()); + } + + /** + * Constructor from an {@link java.net.URI} instance. + * + * @param baseUri The base {@link java.net.URI} instance. + * @param uri The {@link java.net.URI} instance. + */ + public Reference(java.net.URI baseUri, java.net.URI uri) { + this(baseUri.toString(), uri.toString()); + } + + /** + * Constructor from an {@link java.net.URL} instance. + * + * @param url The {@link java.net.URL} instance. + */ + public Reference(java.net.URL url) { + this(url.toString()); + } + + /** + * Constructor for a protocol and host name. Uses the default port for the given protocol. + * + * @param protocol Protocol/scheme to use + * @param hostName The host name or IP address. + */ + public Reference(Protocol protocol, String hostName) { + this(protocol, hostName, protocol.getDefaultPort()); + } + + /** + * Constructor for a protocol, host name and host port + * + * @param protocol Protocol/scheme to use + * @param hostName The host name or IP address. + * @param hostPort The host port (default ports are correctly ignored). + */ + public Reference(Protocol protocol, String hostName, int hostPort) { + this(protocol.getSchemeName(), hostName, hostPort, null, null, null); + } + + /** + * Clone constructor. + * + * @param ref The reference to clone. + */ + public Reference(Reference ref) { + this(ref.baseRef, ref.internalRef); + } + + /** + * Constructor from a URI reference (most likely relative). + * + * @param baseRef The base reference. + * @param uriReference The URI reference, either absolute or relative. + */ + public Reference(Reference baseRef, Reference uriReference) { + this(baseRef, uriReference.toString()); + } + + /** + * Constructor from a URI reference (most likely relative). + * + * @param baseRef The base reference. + * @param uriRef The URI reference, either absolute or relative. + */ + public Reference(Reference baseRef, String uriRef) { + uriRef = encodeInvalidCharacters(uriRef); + this.baseRef = baseRef; + this.internalRef = uriRef; + updateIndexes(); + } + + /** + * Constructor of relative reference from its parts. + * + * @param baseRef The base reference. + * @param relativePart The relative part component (most of the time it is the path component). + * @param query The optional query component for hierarchical identifiers. + * @param fragment The optional fragment identifier. + */ + public Reference(Reference baseRef, String relativePart, String query, String fragment) { + this(baseRef, toString(relativePart, query, fragment)); + } + + /** + * Constructor from a URI reference. + * + * @param uriReference The URI reference, either absolute or relative. + */ + public Reference(String uriReference) { + this((Reference) null, uriReference); + } + + /** + * Constructor from an identifier and a fragment. + * + * @param identifier The resource identifier. + * @param fragment The fragment identifier. + */ + public Reference(String identifier, String fragment) { + this((fragment == null) ? identifier : identifier + '#' + fragment); + } + + /** + * Constructor of absolute reference from its parts. + * + * @param scheme The scheme ("http", "https" or "ftp"). + * @param hostName The host name or IP address. + * @param hostPort The host port (default ports are correctly ignored). + * @param path The path component for hierarchical identifiers. + * @param query The optional query component for hierarchical identifiers. + * @param fragment The optional fragment identifier. + */ + public Reference( + String scheme, + String hostName, + int hostPort, + String path, + String query, + String fragment) { + this(toString(scheme, hostName, hostPort, path, query, fragment)); + } + + /** + * Adds a parameter to the query component. The name and value are automatically URL encoded if + * necessary. + * + * @param parameter The parameter to add. + * @return The updated reference. + */ + public Reference addQueryParameter(Parameter parameter) { + return addQueryParameter(parameter.getName(), parameter.getValue()); + } + + /** + * Adds a parameter to the query component. The name and value are automatically URL encoded if + * necessary. + * + * @param name The parameter name. + * @param value The optional parameter value. + * @return The updated reference. + */ + public Reference addQueryParameter(String name, String value) { + String query = getQuery(); + + if (query == null) { + if (value == null) { + setQuery(encode(name)); + } else { + setQuery(encode(name) + '=' + encode(value)); + } + } else { + if (value == null) { + setQuery(query + '&' + encode(name)); + } else { + setQuery(query + '&' + encode(name) + '=' + encode(value)); + } + } + + return this; + } + + /** + * Adds several parameters to the query component. The name and value are automatically URL + * encoded if necessary. + * + * @param parameters The parameters to add. + * @return The updated reference. + */ + public Reference addQueryParameters(Iterable parameters) { + for (Parameter param : parameters) { + addQueryParameter(param); + } + + return this; + } + + /** + * Adds a segment at the end of the path. If the current path doesn't end with a slash + * character, one is inserted before the new segment value. The value is automatically encoded + * if necessary. + * + * @param value The segment value to add. + * @return The updated reference. + */ + public Reference addSegment(String value) { + final String path = getPath(); + + if (value != null) { + if (path == null) { + setPath("/" + value); + } else if (path.endsWith("/")) { + setPath(path + encode(value)); + } else { + setPath(path + "/" + encode(value)); + } + } + + return this; + } + + public Reference copy() { + final Reference newRef = new Reference(); + + if (this.baseRef == null) { + newRef.baseRef = null; + } else if (equals(this.baseRef)) { + newRef.baseRef = newRef; + } else { + newRef.baseRef = this.baseRef.copy(); + } + + newRef.fragmentIndex = this.fragmentIndex; + newRef.internalRef = this.internalRef; + newRef.queryIndex = this.queryIndex; + newRef.schemeIndex = this.schemeIndex; + return newRef; + } + + /** + * Checks if all characters are valid and encodes invalid characters if necessary. + * + * @param uriRef The URI reference to check. + * @return The original reference, eventually with invalid URI characters encoded. + */ + private String encodeInvalidCharacters(String uriRef) throws IllegalArgumentException { + String result = uriRef; + + if (uriRef != null) { + boolean valid = true; + + // Ensure that all characters are valid, otherwise encode them + for (int i = 0; valid && (i < uriRef.length()); i++) { + if (!isValid(uriRef.charAt(i))) { + valid = false; + Context.getCurrentLogger() + .fine( + "Invalid character detected in URI reference at index '" + + i + + "': \"" + + uriRef.charAt(i) + + "\". It will be automatically encoded."); + } else if ((uriRef.charAt(i) == '%') && (i > uriRef.length() - 2)) { + // A percent encoding character has been detected but + // without the necessary two hexadecimal digits following + valid = false; + Context.getCurrentLogger() + .fine( + "Invalid percent encoding detected in URI reference at index '" + + i + + "': \"" + + uriRef.charAt(i) + + "\". It will be automatically encoded."); + } + } + + if (!valid) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; (i < uriRef.length()); i++) { + if (isValid(uriRef.charAt(i))) { + if ((uriRef.charAt(i) == '%') && (i > uriRef.length() - 2)) { + sb.append("%25"); + } else { + sb.append(uriRef.charAt(i)); + } + } else { + sb.append(encode(String.valueOf(uriRef.charAt(i)))); + } + } + + result = sb.toString(); + } + } + + return result; + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * @param object The object to compare to. + * @return True if this object is the same as the obj argument. + */ + @Override + public boolean equals(Object object) { + if (object instanceof final Reference ref) { + if (this.internalRef == null) { + return ref.internalRef == null; + } + return this.internalRef.equals(ref.internalRef); + } + + return false; + } + + /** + * Returns the authority component for hierarchical identifiers. Includes the user info, host + * name, and the host port number.
+ * Note that this method does no URI decoding. + * + * @return The authority component for hierarchical identifiers. + */ + public String getAuthority() { + final String part = isRelative() ? getRelativePart() : getSchemeSpecificPart(); + + if ((part != null) && part.startsWith("//")) { + int index = part.indexOf('/', 2); + + if (index != -1) { + return part.substring(2, index); + } + + index = part.indexOf('?'); + if (index != -1) { + return part.substring(2, index); + } + + return part.substring(2); + } + + return null; + } + + /** + * Returns the optionally decoded authority component. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded authority component. + * @see #getAuthority() + */ + public String getAuthority(boolean decode) { + return decode ? decode(getAuthority()) : getAuthority(); + } + + /** + * Returns the base reference for relative references. + * + * @return The base reference for relative references. + */ + public Reference getBaseRef() { + return this.baseRef; + } + + /** + * Returns the optional extensions for hierarchical identifiers. An extensions part starts after + * the first '.' character of the last path segment and ends with either the end of the segment + * of with the first ';' character (matrix start). It is a token similar to file extensions + * separated by '.' characters. The value can be omitted.
+ * Note that this method does no URI decoding. + * + * @return The extensions or null. + * @see #getExtensionsAsArray() + * @see #setExtensions(String) + */ + public String getExtensions() { + String result = null; + final String lastSegment = getLastSegment(); + + if (lastSegment != null) { + final int extensionIndex = lastSegment.indexOf('.'); + final int matrixIndex = lastSegment.indexOf(';'); + + if (extensionIndex != -1) { + // Extensions found + if (matrixIndex != -1) { + result = lastSegment.substring(extensionIndex + 1, matrixIndex); + } else { + // No matrix found + result = lastSegment.substring(extensionIndex + 1); + } + } + } + + return result; + } + + /** + * Returns the extensions as an array or null if no extension is found. + * + * @return The extensions as an array or null if no extension is found. + * @see #getExtensions() + */ + public String[] getExtensionsAsArray() { + String[] result = null; + final String extensions = getExtensions(); + + if (extensions != null) { + result = extensions.split("\\."); + } + + return result; + } + + /** + * Returns the fragment identifier.
+ * Note that this method does no URI decoding. + * + * @return The fragment identifier. + */ + public String getFragment() { + if (hasFragment()) { + return this.internalRef.substring(this.fragmentIndex + 1); + } + + return null; + } + + /** + * Returns the optionally decoded fragment identifier. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded fragment identifier. + * @see #getFragment() + */ + public String getFragment(boolean decode) { + return decode ? decode(getFragment()) : getFragment(); + } + + /** + * Returns the hierarchical part which is equivalent to the scheme-specific part less the query + * component.
+ * Note that this method does no URI decoding. + * + * @return The hierarchical part. + */ + public String getHierarchicalPart() { + if (hasScheme()) { + // Scheme found + if (hasQuery()) { + // Query found + return this.internalRef.substring(this.schemeIndex + 1, this.queryIndex); + } + + // No query found + if (hasFragment()) { + // Fragment found + return this.internalRef.substring(this.schemeIndex + 1, this.fragmentIndex); + } + + // No fragment found + return this.internalRef.substring(this.schemeIndex + 1); + } + + // No scheme found + if (hasQuery()) { + // Query found + return this.internalRef.substring(0, this.queryIndex); + } + if (hasFragment()) { + // Fragment found + return this.internalRef.substring(0, this.fragmentIndex); + } + + // No fragment found + return this.internalRef; + } + + /** + * Returns the optionally decoded hierarchical part. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded hierarchical part. + * @see #getHierarchicalPart() + */ + public String getHierarchicalPart(boolean decode) { + return decode ? decode(getHierarchicalPart()) : getHierarchicalPart(); + } + + /** + * Returns the host domain name component for server-based hierarchical identifiers. It can also + * be replaced by an IP address when no domain name was registered.
+ * Note that this method does no URI decoding. + * + * @return The host domain name component for server-based hierarchical identifiers. + */ + public String getHostDomain() { + String result = null; + final String authority = getAuthority(); + + if (authority != null) { + // We must prevent the case where the userinfo part contains ':' + // and the case of IPV6 addresses + int indexUI = authority.indexOf('@'); // user info + int indexIPV6 = authority.indexOf(']'); // IPV6 + int indexP = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); + + if (indexUI != -1) { + // User info found + if (indexP != -1) { + // Port found + result = authority.substring(indexUI + 1, indexP); + } else { + // No port found + result = authority.substring(indexUI + 1); + } + } else { + // No user info found + if (indexP != -1) { + // Port found + result = authority.substring(0, indexP); + } else { + // No port found + result = authority; + } + } + } + + return result; + } + + /** + * Returns the optionally decoded host domain name component. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded host domain name component. + * @see #getHostDomain() + */ + public String getHostDomain(boolean decode) { + return decode ? decode(getHostDomain()) : getHostDomain(); + } + + /** + * Returns the host identifier. Includes the scheme, the host name, and the host port number. + *
+ * Note that this method does no URI decoding. + * + * @return The host identifier. + */ + public String getHostIdentifier() { + return getScheme() + "://" + getAuthority(); + } + + /** + * Returns the optionally decoded host identifier. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded host identifier. + * @see #getHostIdentifier() + */ + public String getHostIdentifier(boolean decode) { + return decode ? decode(getHostIdentifier()) : getHostIdentifier(); + } + + /** + * Returns the optional port number for server-based hierarchical identifiers. + * + * @return The optional port number for server-based hierarchical identifiers or -1 if the port + * number does not exist. + */ + public int getHostPort() { + int result = -1; + final String authority = getAuthority(); + + if (authority != null) { + // We must prevent the case where the userinfo part contains ':' + // and the case of IPV6 addresses + int indexUI = authority.indexOf('@'); // user info + int indexIPV6 = authority.indexOf(']'); // IPV6 + int index = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); + + if (index != -1) { + try { + result = Integer.parseInt(authority.substring(index + 1)); + } catch (NumberFormatException nfe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Can't parse hostPort : [hostRef,requestUri]=[" + + getBaseRef() + + "," + + this.internalRef + + "]"); + } + } + } + + return result; + } + + /** + * Returns the absolute resource identifier, without the fragment.
+ * Note that this method does no URI decoding. + * + * @return The absolute resource identifier, without the fragment. + */ + public String getIdentifier() { + if (hasFragment()) { + // Fragment found + return this.internalRef.substring(0, this.fragmentIndex); + } + + // No fragment found + return this.internalRef; + } + + /** + * Returns the optionally decoded absolute resource identifier. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded absolute resource identifier. + * @see #getIdentifier() + */ + public String getIdentifier(boolean decode) { + return decode ? decode(getIdentifier()) : getIdentifier(); + } + + /** + * Returns the last segment of a hierarchical path.
+ * For example, the "/a/b/c" and "/a/b/c/" paths have the same segments: "a", "b", "c.
+ * Note that this method does no URI decoding. + * + * @return The last segment of a hierarchical path. + */ + public String getLastSegment() { + String result = null; + String path = getPath(); + + if (path != null) { + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + + final int lastSlash = path.lastIndexOf('/'); + + if (lastSlash != -1) { + result = path.substring(lastSlash + 1); + } + } + + return result; + } + + /** + * Returns the optionally decoded last segment. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded last segment. + * @see #getLastSegment() + */ + public String getLastSegment(boolean decode) { + return getLastSegment(decode, false); + } + + /** + * Returns the optionally decoded last segment. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @param excludeMatrix True if the matrix parameters are dropped from the segments. + * @return The optionally decoded last segment. + * @see #getLastSegment() + */ + public String getLastSegment(boolean decode, boolean excludeMatrix) { + String result = getLastSegment(); + + if (excludeMatrix && (result != null)) { + final int matrixIndex = result.indexOf(';'); + + if (matrixIndex != -1) { + result = result.substring(0, matrixIndex); + } + } + + return decode ? decode(result) : result; + } + + /** + * Returns the optional matrix for hierarchical identifiers. A matrix part starts after the + * first ';' character of the last path segment. It is a sequence of 'name=value' parameters + * separated by ';' characters. The value can be omitted.
+ * Note that this method does no URI decoding. + * + * @return The matrix or null. + */ + public String getMatrix() { + String lastSegment = getLastSegment(); + + if (lastSegment != null) { + final int matrixIndex = lastSegment.indexOf(';'); + + if (matrixIndex != -1) { + return lastSegment.substring(matrixIndex + 1); + } + } + + // No matrix found + return null; + } + + /** + * Returns the optionally decoded matrix. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded matrix. + * @see #getMatrix() + */ + public String getMatrix(boolean decode) { + return decode ? decode(getMatrix()) : getMatrix(); + } + + /** + * Returns the optional matrix as a form. + * + * @return The optional matrix component as a form. + */ + public Form getMatrixAsForm() { + return new Form(getMatrix(), ';'); + } + + /** + * Returns the optional matrix as a form submission. + * + * @param characterSet The supported character encoding. + * @return The optional matrix as a form. + */ + public Form getMatrixAsForm(CharacterSet characterSet) { + return new Form(getMatrix(), characterSet, ';'); + } + + /** + * Returns the parent reference of a hierarchical reference. The last slash of the path will be + * considered as the end of the parent path. + * + * @return The parent reference of a hierarchical reference. + */ + public Reference getParentRef() { + Reference result = null; + + if (isHierarchical()) { + String parentRef = null; + String path = getPath(); + + if (!path.equals("/") && !path.isEmpty()) { + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + + if (isAbsolute()) { + parentRef = getHostIdentifier() + path.substring(0, path.lastIndexOf('/') + 1); + } else { + parentRef = path.substring(0, path.lastIndexOf('/') + 1); + } + } else { + parentRef = this.internalRef; + } + + result = new Reference(parentRef); + } + + return result; + } + + /** + * Returns the path component for hierarchical identifiers. If no path is available, it returns + * null.
+ * Note that this method does no URI decoding. + * + * @return The path component for hierarchical identifiers. + */ + public String getPath() { + String result = null; + String part = isRelative() ? getRelativePart() : getSchemeSpecificPart(); + + if (part != null) { + if (part.startsWith("//")) { + // Authority found + int index1 = part.indexOf('/', 2); + + if (index1 != -1) { + // Path found + int index2 = part.indexOf('?'); + + if (index2 != -1) { + // Query found + result = part.substring(Math.min(index1, index2), index2); + } else { + // No query found + result = part.substring(index1); + } + } else { + // Path must be empty in this case + } + } else { + // No authority found + int index = part.indexOf('?'); + + if (index != -1) { + // Query found + result = part.substring(0, index); + } else { + // No query found + result = part; + } + } + } + + return result; + } + + /** + * Returns the optionally decoded path component. If no path is available, it returns null. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded path component. + * @see #getPath() + */ + public String getPath(boolean decode) { + return decode ? decode(getPath()) : getPath(); + } + + /** + * Returns the optional query component for hierarchical identifiers.
+ * Note that this method does no URI decoding. + * + * @return The query component or null. + */ + public String getQuery() { + if (hasQuery()) { + // Query found + if (hasFragment()) { + if (this.queryIndex < this.fragmentIndex) { + // Fragment found and query sign not inside fragment + return this.internalRef.substring(this.queryIndex + 1, this.fragmentIndex); + } + + return null; + } + + // No fragment found + return this.internalRef.substring(this.queryIndex + 1); + } + + // No query found + return null; + } + + /** + * Returns the optionally decoded query component. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded query component. + * @see #getQuery() + */ + public String getQuery(boolean decode) { + return decode ? decode(getQuery()) : getQuery(); + } + + /** + * Returns the optional query component as a form. + * + * @return The optional query component as a form. + */ + public Form getQueryAsForm() { + return new Form(getQuery()); + } + + /** + * Returns the optional query component as a form. + * + * @param decode Indicates if the names and values should be automatically decoded. + * @return The optional query component as a form. + */ + public Form getQueryAsForm(boolean decode) { + return new Form(getQuery(), decode); + } + + /** + * Returns the optional query component as a form submission. + * + * @param characterSet The supported character encoding. + * @return The optional query component as a form submission. + */ + public Form getQueryAsForm(CharacterSet characterSet) { + return new Form(getQuery(), characterSet); + } + + /** + * Returns the relative part of relative references, without the query and fragment. If the + * reference is absolute, then null is returned.
+ * Note that this method does no URI decoding. + * + * @return The relative part. + */ + public String getRelativePart() { + return isRelative() ? toString(false, false) : null; + } + + /** + * Returns the optionally decoded relative part. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded relative part. + * @see #getRelativePart() + */ + public String getRelativePart(boolean decode) { + return decode ? decode(getRelativePart()) : getRelativePart(); + } + + /** + * Returns the current reference as a relative reference to the current base reference. This + * method should only be invoked for absolute references, otherwise an IllegalArgumentException + * will be raised. + * + * @return The current reference as a relative reference to the current base reference. + * @see #getRelativeRef(Reference) + */ + public Reference getRelativeRef() { + return getRelativeRef(getBaseRef()); + } + + /** + * Returns the current reference relatively to a base reference. This method should only be + * invoked for absolute references, otherwise an IllegalArgumentException will be raised. + * + * @param base The base reference to use. + * @throws IllegalArgumentException If the relative reference is computed, although the + * reference or the base reference are not absolute or not hierarchical. + * @return The current reference relatively to a base reference. + */ + public Reference getRelativeRef(Reference base) { + Reference result = null; + + if (base == null) { + result = this; + } else if (!isAbsolute() || !isHierarchical()) { + throw new IllegalArgumentException( + "The reference must have an absolute hierarchical path component"); + } else if (!base.isAbsolute() || !base.isHierarchical()) { + throw new IllegalArgumentException( + "The base reference must have an absolute hierarchical path component"); + } else if (!getHostIdentifier().equals(base.getHostIdentifier())) { + result = this; + } else { + final String localPath = getPath(); + final String basePath = base.getPath(); + String relativePath = null; + + if ((basePath == null) || (localPath == null)) { + relativePath = localPath; + } else { + // Find the junction point + boolean diffFound = false; + int lastSlashIndex = -1; + int i = 0; + char current; + while (!diffFound && (i < localPath.length()) && (i < basePath.length())) { + current = localPath.charAt(i); + + if (current != basePath.charAt(i)) { + diffFound = true; + } else { + if (current == '/') { + lastSlashIndex = i; + } + i++; + } + } + + if (!diffFound) { + if (localPath.length() == basePath.length()) { + // Both paths are strictly equivalent + relativePath = "."; + } else if (i == localPath.length()) { + // End of local path reached + if (basePath.charAt(i) == '/') { + if ((i + 1) == basePath.length()) { + // Both paths are strictly equivalent + relativePath = "."; + } else { + // The local path is a direct parent of the base + // path + // We need to add enough ".." in the relative + // path + final StringBuilder sb = new StringBuilder(); + + // Count segments + int segments = 0; + for (int j = basePath.indexOf('/', i); + j != -1; + j = basePath.indexOf('/', j + 1)) segments++; + + // Build relative path + sb.append("../".repeat(Math.max(0, segments))); + + int lastLocalSlash = localPath.lastIndexOf('/'); + sb.append(localPath.substring(lastLocalSlash + 1)); + + relativePath = sb.toString(); + } + } else { + // The base path has a segment that starts like the last local path + // segment, but that is longer. Situation similar to a junction + final StringBuilder sb = new StringBuilder(); + + // Count segments + int segments = 0; + for (int j = basePath.indexOf('/', i); + j != -1; + j = basePath.indexOf('/', j + 1)) segments++; + + // Build relative path + for (int j = 0; j < segments; j++) + if (j > 0) sb.append("/.."); + else sb.append(".."); + + relativePath = sb.toString(); + + if (relativePath.isEmpty()) { + relativePath = "."; + } + } + } else if (i == basePath.length()) { + if (localPath.charAt(i) == '/') { + if ((i + 1) == localPath.length()) { + // Both paths are strictly equivalent + relativePath = "."; + } else { + // The local path is a direct child of the base + // path + relativePath = localPath.substring(i + 1); + } + } else { + if (lastSlashIndex == (i - 1)) { + // The local path is a direct subpath of the + // base path + relativePath = localPath.substring(i); + } else { + relativePath = ".." + localPath.substring(lastSlashIndex); + } + } + } + } else { + // We found a junction point, we need to add enough ".." in + // the relative path and append the rest of the local path + // the local path is a direct subpath of the base path + final StringBuilder sb = new StringBuilder(); + + // Count segments + int segments = 0; + for (int j = basePath.indexOf('/', i); + j != -1; + j = basePath.indexOf('/', j + 1)) segments++; + + // Build relative path + sb.append("../".repeat(Math.max(0, segments))); + + sb.append(localPath.substring(lastSlashIndex + 1)); + + relativePath = sb.toString(); + } + } + + // Build the result reference + result = new Reference(); + final String query = getQuery(); + final String fragment = getFragment(); + boolean modified = false; + + if ((query != null) && (!query.equals(base.getQuery()))) { + result.setQuery(query); + modified = true; + } + + if ((fragment != null) && (!fragment.equals(base.getFragment()))) { + result.setFragment(fragment); + modified = true; + } + + if (!modified || !".".equals(relativePath)) { + result.setPath(relativePath); + } + } + + return result; + } + + /** + * Returns the part of the resource identifier remaining after the base reference. Note that + * this method does not return the optional fragment. Must be used with the following + * prerequisites: + * + *

    + *
  • the reference is absolute + *
  • the reference identifier starts with the resource baseRef + *
+ * + *
+ * Note that this method does no URI decoding. + * + * @return The remaining resource parts or null if the prerequisites are not satisfied. + * @see #getRemainingPart(boolean) + */ + public String getRemainingPart() { + return getRemainingPart(false, true); + } + + /** + * Returns the optionally decoded remaining part. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded remaining part. + * @see #getRemainingPart() + */ + public String getRemainingPart(boolean decode) { + return getRemainingPart(decode, true); + } + + /** + * Returns the optionally decoded remaining part with or without the query part of the + * reference. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @param query True if the query part should be returned, false otherwise. + * @return The optionally decoded remaining part. + * @see #getRemainingPart() + */ + public String getRemainingPart(boolean decode, boolean query) { + String result = null; + final String all = toString(query, false); + + if (getBaseRef() != null) { + final String base = getBaseRef().toString(query, false); + + if ((base != null) && all.startsWith(base)) { + result = all.substring(base.length()); + } + } else { + result = all; + } + + return decode ? decode(result) : result; + } + + /** + * Returns the scheme component.
+ * Note that this method does no URI decoding. + * + * @return The scheme component. + */ + public String getScheme() { + if (hasScheme()) { + // Scheme found + return this.internalRef.substring(0, this.schemeIndex); + } + + // No scheme found + return null; + } + + /** + * Returns the optionally decoded scheme component. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded scheme component. + * @see #getScheme() + */ + public String getScheme(boolean decode) { + return decode ? decode(getScheme()) : getScheme(); + } + + /** + * Returns the protocol associated with the scheme component. + * + * @return The protocol associated with the scheme component. + */ + public Protocol getSchemeProtocol() { + return Protocol.valueOf(getScheme()); + } + + /** + * Returns the scheme-specific part.
+ * Note that no URI decoding is done by this method. + * + * @return The scheme-specific part. + */ + public String getSchemeSpecificPart() { + String result = null; + + if (hasScheme()) { + // Scheme found + if (hasFragment()) { + // Fragment found + result = this.internalRef.substring(this.schemeIndex + 1, this.fragmentIndex); + } else { + // No fragment found + result = this.internalRef.substring(this.schemeIndex + 1); + } + } + + return result; + } + + /** + * Returns the optionally decoded scheme-specific part. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded scheme-specific part. + * @see #getSchemeSpecificPart() + */ + public String getSchemeSpecificPart(boolean decode) { + return decode ? decode(getSchemeSpecificPart()) : getSchemeSpecificPart(); + } + + /** + * Returns the list of segments in a hierarchical path.
+ * A new list is created for each call.
+ * Note that this method does no URI decoding. + * + * @return The segments of a hierarchical path. + */ + public List getSegments() { + final List result = new ArrayList(); + final String path = getPath(); + int start = -2; // The index of the slash starting the segment + char current; + + if (path != null) { + for (int i = 0; i < path.length(); i++) { + current = path.charAt(i); + + if (current == '/') { + if (start == -2) { + // Beginning of an absolute path or sequence of two + // separators + start = i; + } else { + // End of a segment + result.add(path.substring(start + 1, i)); + start = i; + } + } else { + if (start == -2) { + // Starting a new segment for a relative path + start = -1; + } else { + // Looking for the next character + } + } + } + + if (start != -2) { + // Add the last segment + result.add(path.substring(start + 1)); + } + } + + return result; + } + + /** + * Returns the optionally decoded list of segments. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded list of segments. + * @see #getSegments() + */ + public List getSegments(boolean decode) { + final List result = getSegments(); + + if (decode) { + for (int i = 0; i < result.size(); i++) { + result.set(i, decode(result.get(i))); + } + } + + return result; + } + + /** + * Returns the target reference. This method resolves relative references against the base + * reference, then normalizes them. + * + * @throws IllegalArgumentException If the base reference (after resolution) is not absolute. + * @throws IllegalArgumentException If the reference is relative and not base reference has been + * provided. + * @return The target reference. + */ + public Reference getTargetRef() { + Reference result = null; + + // Step 1 - Resolve relative reference against their base reference + if (isRelative() && (this.baseRef != null)) { + Reference baseReference = null; + + if (this.baseRef.isAbsolute()) { + baseReference = this.baseRef; + } else { + baseReference = this.baseRef.getTargetRef(); + } + + if (baseReference.isRelative()) { + throw new IllegalArgumentException( + "The base reference must have an absolute hierarchical path component"); + } + + // Relative URI detected + String authority = getAuthority(); + String path = getPath(); + String query = getQuery(); + String fragment = getFragment(); + + // Create an empty reference + result = new Reference(); + result.setScheme(baseReference.getScheme()); + + if (authority != null) { + result.setAuthority(authority); + result.setPath(path); + result.setQuery(query); + } else { + result.setAuthority(baseReference.getAuthority()); + + if ((path == null) || (path.isEmpty())) { + result.setPath(baseReference.getPath()); + + if (query != null) { + result.setQuery(query); + } else { + result.setQuery(baseReference.getQuery()); + } + } else { + if (path.startsWith("/")) { + result.setPath(path); + } else { + final String basePath = baseReference.getPath(); + String mergedPath = null; + + if ((baseReference.getAuthority() != null) + && ((basePath == null) || (basePath.isEmpty()))) { + mergedPath = "/" + path; + } else { + // Remove the last segment which may be empty if + // the path is ending with a slash + final int lastSlash = basePath.lastIndexOf('/'); + if (lastSlash == -1) { + mergedPath = path; + } else { + mergedPath = basePath.substring(0, lastSlash + 1) + path; + } + } + + result.setPath(mergedPath); + } + + result.setQuery(query); + } + } + + result.setFragment(fragment); + } else if (isRelative()) { + // Relative reference with no baseRef detected + throw new IllegalArgumentException( + "Relative references are only usable when a base reference is set."); + } else { + // Absolute URI detected + result = new Reference(this.internalRef); + } + + // Step 2 - Normalize the target reference + result.normalize(); + + return result; + } + + /** + * Returns the user info component for server-based hierarchical identifiers.
+ * Note that this method does no URI decoding. + * + * @return The user info component for server-based hierarchical identifiers. + */ + public String getUserInfo() { + String result = null; + final String authority = getAuthority(); + + if (authority != null) { + final int index = authority.indexOf('@'); + + if (index != -1) { + result = authority.substring(0, index); + } + } + + return result; + } + + /** + * Returns the optionally decoded user info component. + * + * @param decode Indicates if the result should be decoded using the {@link #decode(String)} + * method. + * @return The optionally decoded user info component. + * @see #getUserInfo() + */ + public String getUserInfo(boolean decode) { + return decode ? decode(getUserInfo()) : getUserInfo(); + } + + /** + * Indicates if this reference has file-like extensions on its last path segment. + * + * @return True if there is are extensions. + * @see #getExtensions() + */ + public boolean hasExtensions() { + boolean result = false; + + // If this reference ends with a "/", it cannot be a file. + final String path = getPath(); + if (!((path != null) && path.endsWith("/"))) { + final String lastSegment = getLastSegment(); + + if (lastSegment != null) { + final int extensionsIndex = lastSegment.indexOf('.'); + final int matrixIndex = lastSegment.indexOf(';'); + result = + (extensionsIndex != -1) + && ((matrixIndex == -1) || (extensionsIndex < matrixIndex)); + } + } + + return result; + } + + /** + * Indicates if this reference has a fragment identifier. + * + * @return True if there is a fragment identifier. + */ + public boolean hasFragment() { + return (this.fragmentIndex != -1); + } + + /** + * Returns a hash code value for the object. + * + * @return A hash code value for the object. + */ + @Override + public int hashCode() { + return (this.internalRef == null) ? 0 : this.internalRef.hashCode(); + } + + /** + * Indicates if this reference has a matrix. + * + * @return True if there is a matrix. + * @see #getMatrix() + */ + public boolean hasMatrix() { + return (getLastSegment().indexOf(';') != -1); + } + + /** + * Indicates if this reference has a query component. + * + * @return True if there is a query. + */ + public boolean hasQuery() { + return (this.queryIndex != -1); + } + + /** + * Indicates if this reference has a scheme component. + * + * @return True if there is a scheme component. + */ + public boolean hasScheme() { + return (this.schemeIndex != -1); + } + + /** + * Indicates if the reference is absolute. + * + * @return True if the reference is absolute. + */ + public boolean isAbsolute() { + return (getScheme() != null); + } + + /** + * Returns true if both references are equivalent, meaning that they resolve to the same target + * reference. + * + * @param ref The reference to compare. + * @return True if both references are equivalent. + */ + public boolean isEquivalentTo(Reference ref) { + return getTargetRef().equals(ref.getTargetRef()); + } + + /** + * Indicates if the identifier is hierarchical. + * + * @return True if the identifier is hierarchical, false if it is opaque. + */ + public boolean isHierarchical() { + return isRelative() || (getSchemeSpecificPart().charAt(0) == '/'); + } + + /** + * Indicates if the identifier is opaque. + * + * @return True if the identifier is opaque, false if it is hierarchical. + */ + public boolean isOpaque() { + return isAbsolute() && (getSchemeSpecificPart().charAt(0) != '/'); + } + + /** + * Indicates if the reference is a parent of the hierarchical child reference. + * + * @param childRef The hierarchical reference. + * @return True if the reference is a parent of the hierarchical child reference. + */ + public boolean isParent(Reference childRef) { + boolean result = false; + + if ((childRef != null) && (childRef.isHierarchical())) { + result = childRef.toString(false, false).startsWith(toString(false, false)); + } + + return result; + } + + /** + * Indicates if the reference is relative. + * + * @return True if the reference is relative. + */ + public boolean isRelative() { + return (getScheme() == null); + } + + /** + * Normalizes the reference. Useful before comparison between references or when building a + * target reference from a base and a relative reference. + * + * @return The current reference. + */ + public Reference normalize() { + // 1. The input buffer is initialized with the now-appended path + // components, and the output buffer is initialized to the empty string. + StringBuilder output = new StringBuilder(); + StringBuilder input = new StringBuilder(); + String path = getPath(); + + if (path != null) { + input.append(path); + } + + // 2. While the input buffer is not empty, the loop is as follows: + while (!input.isEmpty()) { + // A. If the input buffer begins with a prefix of "../" or "./", + // then remove that prefix from the input buffer. + if ((input.length() >= 3) && input.substring(0, 3).equals("../")) { + input.delete(0, 3); + } else if ((input.length() >= 2) && input.substring(0, 2).equals("./")) { + input.delete(0, 2); + } + + // B. if the input buffer begins with a prefix of "/./" or "/.", + // where "." is a complete path segment, then replace that + // prefix with "/" in the input buffer; otherwise, + else if ((input.length() >= 3) && input.substring(0, 3).equals("/./")) { + input.delete(0, 2); + } else if ((input.length() == 2) && input.substring(0, 2).equals("/.")) { + input.delete(1, 2); + } + + // C. if the input buffer begins with a prefix of "/../" or "/..", + // where ".." is a complete path segment, then replace that prefix + // with "/" in the input buffer and remove the last segment and its + // preceding "/" (if any) from the output buffer; otherwise, + else if ((input.length() >= 4) && input.substring(0, 4).equals("/../")) { + input.delete(0, 3); + removeLastSegment(output); + } else if ((input.length() == 3) && input.substring(0, 3).equals("/..")) { + input.delete(1, 3); + removeLastSegment(output); + } + + // D. if the input buffer consists only of "." or "..", then remove + // that from the input buffer; otherwise, + else if ((input.length() == 1) && input.substring(0, 1).equals(".")) { + input.delete(0, 1); + } else if ((input.length() == 2) && input.substring(0, 2).equals("..")) { + input.delete(0, 2); + } + + // E. move the first path segment in the input buffer to the end of + // the output buffer, including the initial "/" character (if any) + // and any later characters up to, but not including, the next + // "/" character or the end of the input buffer. + else { + int max = -1; + for (int i = 1; (max == -1) && (i < input.length()); i++) { + if (input.charAt(i) == '/') { + max = i; + } + } + + if (max != -1) { + // We found the next "/" character. + output.append(input, 0, max); + input.delete(0, max); + } else { + // End of input buffer reached + output.append(input); + input.delete(0, input.length()); + } + } + } + + // Finally, the output buffer is returned as the result + setPath(output.toString()); + + // Ensure that the scheme and host names are reset in lower case + setScheme(getScheme()); + setHostDomain(getHostDomain()); + + // Remove the port if it is equal to the default port of the reference's + // Protocol. + final int hostPort = getHostPort(); + if (hostPort != -1) { + final int defaultPort = Protocol.valueOf(getScheme()).getDefaultPort(); + if (hostPort == defaultPort) { + setHostPort(null); + } + } + + return this; + } + + /** + * Removes the last segment from the output builder. + * + * @param output The output builder to update. + */ + private void removeLastSegment(StringBuilder output) { + int min = -1; + for (int i = output.length() - 1; (min == -1) && (i >= 0); i--) { + if (output.charAt(i) == '/') { + min = i; + } + } + + if (min != -1) { + // We found the previous "/" character. + output.delete(min, output.length()); + } else { + // End of output buffer reached + output.delete(0, output.length()); + } + } + + /** + * Sets the authority component for hierarchical identifiers. + * + * @param authority The authority component for hierarchical identifiers. + */ + public void setAuthority(String authority) { + final String oldPart = isRelative() ? getRelativePart() : getSchemeSpecificPart(); + String newPart; + final String newAuthority = (authority == null) ? "" : "//" + authority; + + if (oldPart == null) { + newPart = newAuthority; + } else if (oldPart.startsWith("//")) { + int index = oldPart.indexOf('/', 2); + + if (index != -1) { + newPart = newAuthority + oldPart.substring(index); + } else { + index = oldPart.indexOf('?'); + if (index != -1) { + newPart = newAuthority + oldPart.substring(index); + } else { + newPart = newAuthority; + } + } + } else { + newPart = newAuthority + oldPart; + } + + if (isAbsolute()) { + setSchemeSpecificPart(newPart); + } else { + setRelativePart(newPart); + } + } + + /** + * Sets the base reference for relative references. + * + * @param baseRef The base reference for relative references. + */ + public void setBaseRef(Reference baseRef) { + this.baseRef = baseRef; + } + + /** + * Sets the base reference for relative references. + * + * @param baseUri The base URI for relative references. + */ + public void setBaseRef(String baseUri) { + setBaseRef(new Reference(baseUri)); + } + + /** + * Sets the extensions for hierarchical identifiers. An extensions part starts after the first + * '.' character of the last path segment and ends with either the end of the segment of with + * the first ';' character (matrix start). It is a token similar to file extensions separated by + * '.' characters. The value can be omitted.
+ * Note that this method does no URI decoding. + * + * @param extensions The extensions to set or null (without leading or trailing dots). + * @see #getExtensions() + * @see #getExtensionsAsArray() + * @see #setExtensions(String[]) + */ + public void setExtensions(String extensions) { + final String lastSegment = getLastSegment(); + + if (lastSegment != null) { + final int extensionIndex = lastSegment.indexOf('.'); + final int matrixIndex = lastSegment.indexOf(';'); + final StringBuilder sb = new StringBuilder(); + + if (extensionIndex != -1) { + // Extensions found + sb.append(lastSegment, 0, extensionIndex); + + if ((extensions != null) && (!extensions.isEmpty())) { + sb.append('.').append(extensions); + } + + if (matrixIndex != -1) { + sb.append(lastSegment.substring(matrixIndex)); + } + } else { + // Extensions not found + if ((extensions != null) && (!extensions.isEmpty())) { + if (matrixIndex != -1) { + // Matrix found, make sure we append it + // after the extensions + sb.append(lastSegment, 0, matrixIndex) + .append('.') + .append(extensions) + .append(lastSegment.substring(matrixIndex)); + } else { + // No matrix found, just append the extensions + sb.append(lastSegment).append('.').append(extensions); + } + } else { + // No change necessary + sb.append(lastSegment); + } + } + + // Finally update the last segment + setLastSegment(sb.toString()); + } else { + setLastSegment('.' + extensions); + } + } + + /** + * Sets the extensions based on an array of extension tokens (without dots). + * + * @param extensions The array of extensions. + * @see #getExtensions() + * @see #getExtensionsAsArray() + * @see #setExtensions(String) + */ + public void setExtensions(String[] extensions) { + String exts = null; + + if (extensions != null) { + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < extensions.length; i++) { + if (i > 0) { + sb.append('.'); + } + + sb.append(extensions[i]); + } + + exts = sb.toString(); + } + + setExtensions(exts); + } + + /** + * Sets the fragment identifier. + * + * @param fragment The fragment identifier. + * @throws IllegalArgumentException if the fragment parameter contains the fragment delimiter + * ('#'). + */ + public void setFragment(String fragment) { + fragment = encodeInvalidCharacters(fragment); + + if ((fragment != null) && (fragment.indexOf('#') != -1)) { + throw new IllegalArgumentException("Illegal '#' character detected in parameter"); + } + + if (hasFragment()) { + // Existing fragment + if (fragment != null) { + this.internalRef = this.internalRef.substring(0, this.fragmentIndex + 1) + fragment; + } else { + this.internalRef = this.internalRef.substring(0, this.fragmentIndex); + } + } else { + // No existing fragment + if (fragment != null) { + if (this.internalRef != null) { + this.internalRef = this.internalRef + '#' + fragment; + } else { + this.internalRef = '#' + fragment; + } + } else { + // Do nothing + } + } + + updateIndexes(); + } + + /** + * Sets the host domain component for server-based hierarchical identifiers. + * + * @param domain The host component for server-based hierarchical identifiers. + */ + public void setHostDomain(String domain) { + final String authority = getAuthority(); + + if (authority == null) { + setAuthority(domain); + } else { + if (domain == null) { + domain = ""; + } else { + // URI specification indicates that host names should be + // produced in lower case + domain = domain.toLowerCase(); + } + + // We must prevent the case where the userinfo part contains ':' + // and the case of IPV6 addresses + int indexUI = authority.indexOf('@'); // user info + int indexIPV6 = authority.indexOf(']'); // IPV6 + int indexP = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); + + if (indexUI != -1) { + // User info found + if (indexP != -1) { + // Port found + setAuthority( + authority.substring(0, indexUI + 1) + + domain + + authority.substring(indexP)); + } else { + // No port found + setAuthority(authority.substring(0, indexUI + 1) + domain); + } + } else { + // No user info found + if (indexP != -1) { + // Port found + setAuthority(domain + authority.substring(indexP)); + } else { + // No port found + setAuthority(domain); + } + } + } + } + + /** + * Sets the optional port number for server-based hierarchical identifiers. + * + * @param port The optional port number for server-based hierarchical identifiers. + * @throws IllegalArgumentException If the authority has not been defined. + */ + public void setHostPort(Integer port) { + final String authority = getAuthority(); + + if (authority != null) { + // We must prevent the case where the userinfo part contains ':' + // and the case of IPV6 addresses + int indexUI = authority.indexOf('@'); // user info + int indexIPV6 = authority.indexOf(']'); // IPV6 + int index = authority.indexOf(':', (indexIPV6 == -1) ? indexUI : indexIPV6); + String newPort = (port == null) ? "" : ":" + port; + + if (index != -1) { + setAuthority(authority.substring(0, index) + newPort); + } else { + setAuthority(authority + newPort); + } + } else { + throw new IllegalArgumentException( + "No authority defined, please define a host name first"); + } + } + + /** + * Sets the absolute resource identifier. + * + * @param identifier The absolute resource identifier. + * @throws IllegalArgumentException If the identifier parameter contains the fragment delimiter + * ('#'). + */ + public void setIdentifier(String identifier) { + identifier = encodeInvalidCharacters(identifier); + + if (identifier == null) { + identifier = ""; + } + + if (identifier.indexOf('#') != -1) { + throw new IllegalArgumentException("Illegal '#' character detected in parameter"); + } + + if (hasFragment()) { + // Fragment found + this.internalRef = identifier + this.internalRef.substring(this.fragmentIndex); + } else { + // No fragment found + this.internalRef = identifier; + } + + updateIndexes(); + } + + /** + * Sets the last segment of the path. If no path is available, then it creates one and adds a + * slash in front of the given last segment.
+ * Note that this method does no URI decoding. + * + * @param lastSegment The last segment of a hierarchical path. + */ + public void setLastSegment(String lastSegment) { + String path = getPath(); + + if (path != null) { + int lastSlashIndex = path.lastIndexOf('/'); + + if (lastSlashIndex != -1) { + setPath(path.substring(0, lastSlashIndex + 1) + lastSegment); + return; + } + } + + setPath('/' + lastSegment); + } + + /** + * Sets the path component for hierarchical identifiers. + * + * @param path The path component for hierarchical identifiers. + */ + public void setPath(String path) { + final String oldPart = isRelative() ? getRelativePart() : getSchemeSpecificPart(); + String newPart = null; + + if (oldPart != null) { + if (path == null) { + path = ""; + } + + if (oldPart.startsWith("//")) { + // Authority found + final int index1 = oldPart.indexOf('/', 2); + + if (index1 != -1) { + // Path found + final int index2 = oldPart.indexOf('?'); + + if (index2 != -1) { + // Query found + newPart = oldPart.substring(0, index1) + path + oldPart.substring(index2); + } else { + // No query found + newPart = oldPart.substring(0, index1) + path; + } + } else { + // No path found + final int index2 = oldPart.indexOf('?'); + + if (index2 != -1) { + // Query found + newPart = oldPart.substring(0, index2) + path + oldPart.substring(index2); + } else { + // No query found + newPart = oldPart + path; + } + } + } else { + // No authority found + final int index = oldPart.indexOf('?'); + + if (index != -1) { + // Query found + newPart = path + oldPart.substring(index); + } else { + // No query found + newPart = path; + } + } + } else { + newPart = path; + } + + if (isAbsolute()) { + setSchemeSpecificPart(newPart); + } else { + setRelativePart(newPart); + } + } + + /** + * Sets the scheme component based on this protocol. + * + * @param protocol The protocol of the scheme component. + */ + public void setProtocol(Protocol protocol) { + setScheme(protocol.getSchemeName()); + } + + /** + * Sets the query component for hierarchical identifiers. + * + * @param query The query component for hierarchical identifiers. + */ + public void setQuery(String query) { + query = encodeInvalidCharacters(query); + final boolean emptyQueryString = ((query == null) || query.isEmpty()); + + if (hasQuery()) { + // Query found + if (hasFragment()) { + // Fragment found + if (!emptyQueryString) { + this.internalRef = + this.internalRef.substring(0, this.queryIndex + 1) + + query + + this.internalRef.substring(this.fragmentIndex); + } else { + this.internalRef = + this.internalRef.substring(0, this.queryIndex) + + this.internalRef.substring(this.fragmentIndex); + } + } else { + // No fragment found + if (!emptyQueryString) { + this.internalRef = this.internalRef.substring(0, this.queryIndex + 1) + query; + } else { + this.internalRef = this.internalRef.substring(0, this.queryIndex); + } + } + } else { + // No query found + if (hasFragment()) { + // Fragment found + if (!emptyQueryString) { + this.internalRef = + this.internalRef.substring(0, this.fragmentIndex) + + '?' + + query + + this.internalRef.substring(this.fragmentIndex); + } else { + // Do nothing; + } + } else { + // No fragment found + if (!emptyQueryString) { + if (this.internalRef != null) { + this.internalRef = this.internalRef + '?' + query; + } else { + this.internalRef = '?' + query; + } + } else { + // Do nothing; + } + } + } + + updateIndexes(); + } + + /** + * Sets the relative part for relative references only. + * + * @param relativePart The relative part to set. + */ + public void setRelativePart(String relativePart) { + relativePart = encodeInvalidCharacters(relativePart); + + if (relativePart == null) { + relativePart = ""; + } + + if (!hasScheme()) { + // This is a relative reference, no scheme found + if (hasQuery()) { + // Query found + this.internalRef = relativePart + this.internalRef.substring(this.queryIndex); + } else if (hasFragment()) { + // Fragment found + this.internalRef = relativePart + this.internalRef.substring(this.fragmentIndex); + } else { + // No fragment found + this.internalRef = relativePart; + } + } + + updateIndexes(); + } + + /** + * Sets the scheme component. + * + * @param scheme The scheme component. + */ + public void setScheme(String scheme) { + scheme = encodeInvalidCharacters(scheme); + + if (scheme != null) { + // URI specification indicates that scheme names should be + // produced in the lower case + scheme = scheme.toLowerCase(); + } + + if (hasScheme()) { + // Scheme found + if (scheme != null) { + this.internalRef = scheme + this.internalRef.substring(this.schemeIndex); + } else { + this.internalRef = this.internalRef.substring(this.schemeIndex + 1); + } + } else { + // No scheme found + if (scheme != null) { + if (this.internalRef == null) { + this.internalRef = scheme + ':'; + } else { + this.internalRef = scheme + ':' + this.internalRef; + } + } + } + + updateIndexes(); + } + + /** + * Sets the scheme-specific part. + * + * @param schemeSpecificPart The scheme-specific part. + */ + public void setSchemeSpecificPart(String schemeSpecificPart) { + schemeSpecificPart = encodeInvalidCharacters(schemeSpecificPart); + + if (schemeSpecificPart == null) { + schemeSpecificPart = ""; + } + + if (hasScheme()) { + // Scheme found + if (hasFragment()) { + // Fragment found + this.internalRef = + this.internalRef.substring(0, this.schemeIndex + 1) + + schemeSpecificPart + + this.internalRef.substring(this.fragmentIndex); + } else { + // No fragment found + this.internalRef = + this.internalRef.substring(0, this.schemeIndex + 1) + schemeSpecificPart; + } + } else { + // No scheme found + if (hasFragment()) { + // Fragment found + this.internalRef = + schemeSpecificPart + this.internalRef.substring(this.fragmentIndex); + } else { + // No fragment found + this.internalRef = schemeSpecificPart; + } + } + + updateIndexes(); + } + + /** + * Sets the segments of a hierarchical path.
+ * A new absolute path will replace any existing one. + * + * @param segments The segments of the hierarchical path. + */ + public void setSegments(List segments) { + final StringBuilder sb = new StringBuilder(); + + for (final String segment : segments) { + sb.append('/').append(segment); + } + + setPath(sb.toString()); + } + + /** + * Sets the user info component for server-based hierarchical identifiers. + * + * @param userInfo The user info component for server-based hierarchical identifiers. + * @throws IllegalArgumentException If the authority part has not been defined. + */ + public void setUserInfo(String userInfo) { + final String authority = getAuthority(); + + if (authority != null) { + final int index = authority.indexOf('@'); + final String newUserInfo = (userInfo == null) ? "" : userInfo + '@'; + + if (index != -1) { + setAuthority(newUserInfo + authority.substring(index + 1)); + } else { + setAuthority(newUserInfo + authority); + } + } else { + throw new IllegalArgumentException( + "No authority defined, please define a host name first"); + } + } + + /** + * Returns the reference as a URI string. + * + * @return The reference as a URI string. + */ + @Override + public String toString() { + return this.internalRef; + } + + /** + * Returns the URI reference string. + * + * @param query Indicates if the query should be included; + * @param fragment Indicates if the fragment should be included; + * @return The URI reference string. + */ + public String toString(boolean query, boolean fragment) { + if (query) { + if (fragment) { + return this.internalRef; + } + + if (hasFragment()) { + return this.internalRef.substring(0, this.fragmentIndex); + } + return this.internalRef; + } + + if (fragment) { + // Fragment should be included + if (hasQuery()) { + // Query found + if (hasFragment()) { + // Fragment found + return this.internalRef.substring(0, this.queryIndex) + "#" + getFragment(); + } + + // No fragment found + return this.internalRef.substring(0, this.queryIndex); + } + + // No query found + return this.internalRef; + } + + // Fragment should not be included + if (hasQuery()) { + // Query found + return this.internalRef.substring(0, this.queryIndex); + } + if (hasFragment()) { + // Fragment found + return this.internalRef.substring(0, this.fragmentIndex); + } + + return this.internalRef; + } + + /** + * Converts to a {@link java.net.URI} instance. Note that relative references are resolved + * before conversion using the {@link #getTargetRef()} method. + * + * @return A {@link java.net.URI} instance. + */ + public java.net.URI toUri() { + return java.net.URI.create(getTargetRef().toString()); + } + + /** + * Converts to a {@link java.net.URL} instance. Note that relative references are resolved + * before conversion using the {@link #getTargetRef()} method. + * + * @return A {@link java.net.URL} instance. + */ + public java.net.URL toUrl() { + java.net.URL result = null; + + try { + result = new java.net.URL(getTargetRef().toString()); + } catch (java.net.MalformedURLException e) { + throw new IllegalArgumentException("Malformed URL exception", e); + } + + return result; + } + + /** Updates internal indexes. */ + private void updateIndexes() { + if (this.internalRef != null) { + // Compute the indexes + final int firstSlashIndex = this.internalRef.indexOf('/'); + this.schemeIndex = this.internalRef.indexOf(':'); + + if ((firstSlashIndex != -1) && (this.schemeIndex > firstSlashIndex)) { + // We are in the rare case of a relative reference where one of + // the path segments contains a colon character. In this case, + // we ignore the colon as a valid scheme index. + // Note that this colon can't be in the first segment as it is + // forbidden by the URI RFC. + this.schemeIndex = -1; + } + + this.queryIndex = this.internalRef.indexOf('?'); + this.fragmentIndex = this.internalRef.indexOf('#'); + + if (hasQuery() && hasFragment() && (this.queryIndex > this.fragmentIndex)) { + // Query sign inside the fragment + this.queryIndex = -1; + } + + if (hasQuery() && this.schemeIndex > this.queryIndex) { + // Colon sign inside the query + this.schemeIndex = -1; + } + + if (hasFragment() && this.schemeIndex > this.fragmentIndex) { + // Colon sign inside fragment + this.schemeIndex = -1; + } + } else { + this.schemeIndex = -1; + this.queryIndex = -1; + this.fragmentIndex = -1; + } + } } diff --git a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java index 4aae2679ce..7e59690ab9 100644 --- a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java +++ b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java @@ -1,65 +1,61 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.engine.io.IoUtils; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.util.WrapperList; - import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.util.WrapperList; /** * List of URI references. - * + * * @author Jerome Louvel */ public class ReferenceList extends WrapperList { - /** The list's identifier. */ - private volatile Reference identifier; - - /** - * Constructor. - */ - public ReferenceList() { - super(); - } - - /** - * Constructor. - * - * @param initialCapacity The initial list capacity. - */ - public ReferenceList(int initialCapacity) { - super(new ArrayList(initialCapacity)); - } - - /** - * Constructor. - * - * @param delegate The delegate list. - */ - public ReferenceList(List delegate) { - super(delegate); - } - - /** - * Constructor from a "text/uri-list" representation. - * - * @param uriList The "text/uri-list" representation to parse. - * @throws IOException - */ - public ReferenceList(Representation uriList) throws IOException { + /** The list's identifier. */ + private volatile Reference identifier; + + /** Constructor. */ + public ReferenceList() { + super(); + } + + /** + * Constructor. + * + * @param initialCapacity The initial list capacity. + */ + public ReferenceList(int initialCapacity) { + super(new ArrayList(initialCapacity)); + } + + /** + * Constructor. + * + * @param delegate The delegate list. + */ + public ReferenceList(List delegate) { + super(delegate); + } + + /** + * Constructor from a "text/uri-list" representation. + * + * @param uriList The "text/uri-list" representation to parse. + * @throws IOException + */ + public ReferenceList(Representation uriList) throws IOException { try (BufferedReader br = new BufferedReader(uriList.getReader(), IoUtils.BUFFER_SIZE)) { String line = br.readLine(); @@ -78,104 +74,107 @@ public ReferenceList(Representation uriList) throws IOException { line = br.readLine(); } } - } - - /** - * Creates then adds a reference at the end of the list. - * - * @param uri The uri of the reference to add. - * @return True (as per the general contract of the Collection.add method). - */ - public boolean add(String uri) { - return add(new Reference(uri)); - } - - /** - * Returns the list identifier. - * - * @return The list identifier. - */ - public Reference getIdentifier() { - return this.identifier; - } - - /** - * Returns a representation of the list in the "text/uri-list" format. - * - * @return A representation of the list in the "text/uri-list" format. - */ - public Representation getTextRepresentation() { - final StringBuilder sb = new StringBuilder(); - - if (getIdentifier() != null) { - sb.append("# ").append(getIdentifier().toString()).append("\r\n"); - } - - for (final Reference ref : this) { - sb.append(ref.toString()).append("\r\n"); - } - - return new StringRepresentation(sb.toString(), MediaType.TEXT_URI_LIST); - } - - /** - * Returns a representation of the list in "text/html" format. - * - * @return A representation of the list in "text/html" format. - */ - public Representation getWebRepresentation() { - // Create a simple HTML list - final StringBuilder sb = new StringBuilder(); - sb.append("\n"); - - if (getIdentifier() != null) { - sb.append("

Listing of \"").append(getIdentifier().getPath()).append("\"

\n"); - final Reference parentRef = getIdentifier().getParentRef(); - - if (!parentRef.equals(getIdentifier())) { - sb.append("..
\n"); - } - } else { - sb.append("

List of references

\n"); - } - - for (final Reference ref : this) { - sb.append("").append(ref.getRelativeRef(getIdentifier())).append("
\n"); - } - sb.append("\n"); - - return new StringRepresentation(sb.toString(), MediaType.TEXT_HTML); - } - - /** - * Sets the list reference. - * - * @param identifier The list identifier. - */ - public void setIdentifier(Reference identifier) { - this.identifier = identifier; - } - - /** - * Sets the list reference. - * - * @param identifier The list identifier as a URI. - */ - public void setIdentifier(String identifier) { - setIdentifier(new Reference(identifier)); - } - - /** - * Returns a view of the portion of this list between the specified fromIndex, - * inclusive, and toIndex, exclusive. - * - * @param fromIndex The start position. - * @param toIndex The end position (exclusive). - * @return The sub-list. - */ - @Override - public ReferenceList subList(int fromIndex, int toIndex) { - return new ReferenceList(getDelegate().subList(fromIndex, toIndex)); - } + } + + /** + * Creates then adds a reference at the end of the list. + * + * @param uri The uri of the reference to add. + * @return True (as per the general contract of the Collection.add method). + */ + public boolean add(String uri) { + return add(new Reference(uri)); + } + + /** + * Returns the list identifier. + * + * @return The list identifier. + */ + public Reference getIdentifier() { + return this.identifier; + } + + /** + * Returns a representation of the list in the "text/uri-list" format. + * + * @return A representation of the list in the "text/uri-list" format. + */ + public Representation getTextRepresentation() { + final StringBuilder sb = new StringBuilder(); + + if (getIdentifier() != null) { + sb.append("# ").append(getIdentifier().toString()).append("\r\n"); + } + + for (final Reference ref : this) { + sb.append(ref.toString()).append("\r\n"); + } + + return new StringRepresentation(sb.toString(), MediaType.TEXT_URI_LIST); + } + + /** + * Returns a representation of the list in "text/html" format. + * + * @return A representation of the list in "text/html" format. + */ + public Representation getWebRepresentation() { + // Create a simple HTML list + final StringBuilder sb = new StringBuilder(); + sb.append("\n"); + + if (getIdentifier() != null) { + sb.append("

Listing of \"").append(getIdentifier().getPath()).append("\"

\n"); + final Reference parentRef = getIdentifier().getParentRef(); + + if (!parentRef.equals(getIdentifier())) { + sb.append("..
\n"); + } + } else { + sb.append("

List of references

\n"); + } + for (final Reference ref : this) { + sb.append("") + .append(ref.getRelativeRef(getIdentifier())) + .append("
\n"); + } + sb.append("\n"); + + return new StringRepresentation(sb.toString(), MediaType.TEXT_HTML); + } + + /** + * Sets the list reference. + * + * @param identifier The list identifier. + */ + public void setIdentifier(Reference identifier) { + this.identifier = identifier; + } + + /** + * Sets the list reference. + * + * @param identifier The list identifier as a URI. + */ + public void setIdentifier(String identifier) { + setIdentifier(new Reference(identifier)); + } + + /** + * Returns a view of the portion of this list between the specified fromIndex, inclusive, and + * toIndex, exclusive. + * + * @param fromIndex The start position. + * @param toIndex The end position (exclusive). + * @return The sub-list. + */ + @Override + public ReferenceList subList(int fromIndex, int toIndex) { + return new ReferenceList(getDelegate().subList(fromIndex, toIndex)); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/ServerInfo.java b/org.restlet/src/main/java/org/restlet/data/ServerInfo.java index cb08823bcd..9653d56bac 100644 --- a/org.restlet/src/main/java/org/restlet/data/ServerInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/ServerInfo.java @@ -1,120 +1,112 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; /** * Server specific data related to a call. - * + * * @author Jerome Louvel */ public final class ServerInfo { - /** Indicates if the server accepts range requests for a resource. */ - private volatile boolean acceptingRanges; - - /** The IP address. */ - private volatile String address; + /** Indicates if the server accepts range requests for a resource. */ + private volatile boolean acceptingRanges; - /** The agent name. */ - private volatile String agent; + /** The IP address. */ + private volatile String address; - /** The port number. */ - private volatile int port; + /** The agent name. */ + private volatile String agent; - /** - * Constructor. - */ - public ServerInfo() { - this.address = null; - this.agent = null; - this.port = -1; - this.acceptingRanges = false; - } + /** The port number. */ + private volatile int port; - /** - * Returns the IP address. - * - * @return The IP address. - */ - public String getAddress() { - return this.address; - } + /** Constructor. */ + public ServerInfo() { + this.address = null; + this.agent = null; + this.port = -1; + this.acceptingRanges = false; + } - /** - * Returns the agent name (ex: "Restlet-Framework/2.0"). Note that when used - * with HTTP connectors, this property maps to the "Server" header. - * - * @return The agent name. - */ - public String getAgent() { - return this.agent; - } + /** + * Returns the IP address. + * + * @return The IP address. + */ + public String getAddress() { + return this.address; + } - /** - * Returns the port number which received the call. If no port is specified, -1 - * is returned. - * - * @return The port number which received the call. - */ - public int getPort() { - return this.port; - } + /** + * Returns the agent name (ex: "Restlet-Framework/2.0"). Note that when used with HTTP + * connectors, this property maps to the "Server" header. + * + * @return The agent name. + */ + public String getAgent() { + return this.agent; + } - /** - * Return true if the server accepts range requests for a resource, with the - * "byte" range unit. Note that when used with HTTP connectors, this property - * maps to the "Accept-Ranges" header. - * - * @return True if the server accepts range requests for a resource. - */ - public boolean isAcceptingRanges() { - return acceptingRanges; - } + /** + * Returns the port number which received the call. If no port is specified, -1 is returned. + * + * @return The port number which received the call. + */ + public int getPort() { + return this.port; + } - /** - * Indicates if the server accepts range requests for a resource, with the - * "byte" range unit. Note that when used with HTTP connectors, this property - * maps to the "Accept-Ranges" header. - * - * @param acceptingRanges True if the server accepts range requests for a - * resource. - */ - public void setAcceptingRanges(boolean acceptingRanges) { - this.acceptingRanges = acceptingRanges; - } + /** + * Return true if the server accepts range requests for a resource, with the "byte" range unit. + * Note that when used with HTTP connectors, this property maps to the "Accept-Ranges" header. + * + * @return True if the server accepts range requests for a resource. + */ + public boolean isAcceptingRanges() { + return acceptingRanges; + } - /** - * Sets the IP address which received the call. - * - * @param address The IP address which received the call. - */ - public void setAddress(String address) { - this.address = address; - } + /** + * Indicates if the server accepts range requests for a resource, with the "byte" range unit. + * Note that when used with HTTP connectors, this property maps to the "Accept-Ranges" header. + * + * @param acceptingRanges True if the server accepts range requests for a resource. + */ + public void setAcceptingRanges(boolean acceptingRanges) { + this.acceptingRanges = acceptingRanges; + } - /** - * Sets the agent name (ex: "Restlet-Framework/2.0"). Note that when used with - * HTTP connectors, this property maps to the "Server" header. - * - * @param agent The agent name. - */ - public void setAgent(String agent) { - this.agent = agent; - } + /** + * Sets the IP address which received the call. + * + * @param address The IP address which received the call. + */ + public void setAddress(String address) { + this.address = address; + } - /** - * Sets the port number which received the call. - * - * @param port The port number which received the call. - */ - public void setPort(int port) { - this.port = port; - } + /** + * Sets the agent name (ex: "Restlet-Framework/2.0"). Note that when used with HTTP connectors, + * this property maps to the "Server" header. + * + * @param agent The agent name. + */ + public void setAgent(String agent) { + this.agent = agent; + } + /** + * Sets the port number which received the call. + * + * @param port The port number which received the call. + */ + public void setPort(int port) { + this.port = port; + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Status.java b/org.restlet/src/main/java/org/restlet/data/Status.java index afdb361208..7d3b71a7ba 100644 --- a/org.restlet/src/main/java/org/restlet/data/Status.java +++ b/org.restlet/src/main/java/org/restlet/data/Status.java @@ -1,647 +1,587 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import org.restlet.engine.Engine; /** * Status to return after handling a call. - * + * * @author Jerome Louvel */ public final class Status { - private static final String BASE_ADDED_HTTP = "http://tools.ietf.org/html/rfc6585"; - - private static final String BASE_HTTP = "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"; - - private static final String BASE_RESTLET = "https://javadoc.io/static/org.restlet/org.restlet/" + Engine.VERSION + "/"; - - /** - * The server could not understand the request due to malformed syntax. - * - * @see HTTP - * RFC - 10.4.1 400 Bad Request - */ - public static final Status CLIENT_ERROR_BAD_REQUEST = new Status(400); - - /** - * The request could not be completed due to a conflict with the current state - * of the resource (as experienced in a version control system). - * - * @see HTTP - * RFC - 10.4.10 409 Conflict - */ - public static final Status CLIENT_ERROR_CONFLICT = new Status(409); - - /** - * The user agent expects some behavior of the server (given in an Expect - * request-header field), but this expectation could not be met by this server. - * - * @see HTTP - * RFC - 10.4.18 417 Expectation Failed - */ - public static final Status CLIENT_ERROR_EXPECTATION_FAILED = new Status(417); - - /** - * The server understood the request, but is refusing to fulfill it as it could - * be explained in the entity. - * - * @see HTTP - * RFC - 10.4.4 403 Forbidden - */ - public static final Status CLIENT_ERROR_FORBIDDEN = new Status(403); - - /** - * The requested resource is no longer available at the server and no forwarding - * address is known. - * - * @see HTTP - * RFC - 10.4.11 410 Gone - */ - public static final Status CLIENT_ERROR_GONE = new Status(410); - - /** - * The server refuses to accept the request without a defined Content-Length. - * - * @see HTTP - * RFC - 10.4.12 411 Length Required - */ - public static final Status CLIENT_ERROR_LENGTH_REQUIRED = new Status(411); - - /** - * The method specified in the Request-Line is not allowed for the resource - * identified by the Request-URI. - * - * @see HTTP - * RFC - 10.4.6 405 Method Not Allowed - */ - public static final Status CLIENT_ERROR_METHOD_NOT_ALLOWED = new Status(405); - - /** - * The resource identified by the request is only capable of generating response - * entities whose content characteristics do not match the user's requirements - * (in Accept* headers). - * - * @see HTTP - * RFC - 10.4.7 406 Not Acceptable - */ - public static final Status CLIENT_ERROR_NOT_ACCEPTABLE = new Status(406); - - /** - * The server has not found anything matching the Request-URI or the server does - * not wish to reveal exactly why the request has been refused, or no other - * response is applicable. - * - * @see HTTP - * RFC - 10.4.5 404 Not Found - */ - public static final Status CLIENT_ERROR_NOT_FOUND = new Status(404); - - /** - * This code is reserved for future use. - * - * @see HTTP - * RFC - 10.4.3 402 Payment Required - */ - public static final Status CLIENT_ERROR_PAYMENT_REQUIRED = new Status(402); - - /** - * Sent by the server when the user agent asks the server to carry out a request - * under certain conditions that are not met. - * - * @see HTTP - * RFC - 10.4.13 412 Precondition Failed - */ - public static final Status CLIENT_ERROR_PRECONDITION_FAILED = new Status(412); - - /** - * This code is similar to 401 (Unauthorized), but indicates that the client - * must first authenticate itself with the proxy. - * - * @see HTTP - * RFC - 10.4.8 407 Proxy Authentication Required - */ - public static final Status CLIENT_ERROR_PROXY_AUTHENTIFICATION_REQUIRED = new Status(407); - - /** - * The server is refusing to process a request because the request entity is - * larger than the server is willing or able to process. - * - * @see HTTP - * RFC - 10.4.14 413 Request Entity Too Large - */ - public static final Status CLIENT_ERROR_REQUEST_ENTITY_TOO_LARGE = new Status(413); - - /** - * Sent by the server when an HTTP client opens a connection, but has never sent - * a request (or never sent the blank line that signals the end of the request). - * - * @see HTTP - * RFC - 10.4.9 408 Request Timeout - */ - public static final Status CLIENT_ERROR_REQUEST_TIMEOUT = new Status(408); - - /** - * The server is refusing to service the request because the Request-URI is - * longer than the server is willing to interpret. - * - * @see HTTP - * RFC - 10.4.15 414 Request-URI Too Long - */ - public static final Status CLIENT_ERROR_REQUEST_URI_TOO_LONG = new Status(414); - - /** - * The request includes a Range request-header field and the selected resource - * is too small for any of the byte-ranges to apply. - * - * @see HTTP - * RFC - 10.4.17 416 Requested Range Not Satisfiable - */ - public static final Status CLIENT_ERROR_REQUESTED_RANGE_NOT_SATISFIABLE = new Status(416); - - /** - * The server refuses to accept the request because the user has sent too many - * requests in a given amount of time. - * - * @see HTTP RFC - - * 10.4.12 429 Too Many Requests - */ - public static final Status CLIENT_ERROR_TOO_MANY_REQUESTS = new Status(429); - - /** - * The request requires user authentication. - * - * @see HTTP - * RFC - 10.4.2 401 Unauthorized - */ - public static final Status CLIENT_ERROR_UNAUTHORIZED = new Status(401); - - /** - * The server is refusing to service the request because the entity of the - * request is in a format not supported by the requested resource for the - * requested method. - * - * @see HTTP - * RFC - 10.4.16 415 Unsupported Media Type - */ - public static final Status CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE = new Status(415); - - /** - * The client connector faced an error during the communication with the remote - * server (interruption, timeout, etc.). The status code is 1001. - */ - public static final Status CONNECTOR_ERROR_COMMUNICATION = new Status(1001); - - /** - * The client connector could not connect to the remote server. The status code - * is 1000. - */ - public static final Status CONNECTOR_ERROR_CONNECTION = new Status(1000); - - /** - * The client connector faced an internal error during the process of a request - * to its server or the process of a response to its client. The status code is - * 1002. - */ - public static final Status CONNECTOR_ERROR_INTERNAL = new Status(1002); - - /** - * This interim response (the client has to wait for the final response) is used - * to inform the client that the initial part of the request has been received - * and has not yet been rejected or completed by the server. - * - * @see HTTP - * RFC - 10.1.1 100 Continue - */ - public static final Status INFO_CONTINUE = new Status(100); - - /** - * Warning status code, typically returned by a cache, indicating that it is - * intentionally disconnected from the rest of the network for a period of time. - * - * @see HTTP - * RFC - 14.46 Warning - */ - public static final Status INFO_DISCONNECTED_OPERATION = new Status(112); - - /** - * Warning status code, typically returned by a cache, indicating that it - * heuristically chose a freshness lifetime greater than 24 hours and the - * response's age is greater than 24 hours. - * - * @see HTTP - * RFC - 14.46 Warning - */ - public static final Status INFO_HEURISTIC_EXPIRATION = new Status(113); - - /** - * Warning status code, optionally including arbitrary information to be - * presented to a human user, typically returned by a cache. - * - * @see HTTP - * RFC - 14.46 Warning - */ - public static final Status INFO_MISC_WARNING = new Status(199); - - /** - * Warning status code, typically returned by a cache, indicating that the - * response is stale because an attempt to revalidate the response failed, due - * to an inability to reach the server. - * - * @see HTTP - * RFC - 14.46 Warning - */ - public static final Status INFO_REVALIDATION_FAILED = new Status(111); - - /** - * Warning status code, typically returned by a cache, indicating that the - * response is stale. - * - * @see HTTP - * RFC - 14.46 Warning - */ - public static final Status INFO_STALE_RESPONSE = new Status(110); - - /** - * The server understands and is willing to comply with the client's request, - * via the Upgrade message header field, for a change in the application - * protocol being used on this connection. - * - * @see HTTP - * RFC - 10.1.1 101 Switching Protocols - */ - public static final Status INFO_SWITCHING_PROTOCOL = new Status(101); - - /** - * The requested resource resides temporarily under a different URI which should - * not be used for future requests by the client (use status codes 303 or 307 - * instead since this status has been manifestly misused). - * - * @see HTTP - * RFC - 10.3.3 302 Found - */ - public static final Status REDIRECTION_FOUND = new Status(302); - - /** - * The server lets the user agent choosing one of the multiple representations - * of the requested resource, each representation having its own specific - * location provided in the response entity. - * - * @see HTTP - * RFC - 10.3.1 300 Multiple Choices - */ - public static final Status REDIRECTION_MULTIPLE_CHOICES = new Status(300); - - /** - * Status code sent by the server in response to a conditional GET request in - * case the document has not been modified. - * - * @see HTTP - * RFC - 10.3.5 304 Not Modified - */ - public static final Status REDIRECTION_NOT_MODIFIED = new Status(304); - - /** - * The requested resource has been assigned a new permanent URI and any future - * references to this resource SHOULD use one of the returned URIs. - * - * @see HTTP - * RFC - 10.3.2 301 Moved Permanently - */ - public static final Status REDIRECTION_PERMANENT = new Status(301); - - /** - * The response to the request can be found under a different URI and SHOULD be - * retrieved using a GET method on that resource. - * - * @see HTTP - * RFC - 10.3.4 303 See Other - */ - public static final Status REDIRECTION_SEE_OTHER = new Status(303); - - /** - * The requested resource resides temporarily under a different URI which should - * not be used for future requests by the client. - * - * @see HTTP - * RFC - 10.3.8 307 Temporary Redirect - */ - public static final Status REDIRECTION_TEMPORARY = new Status(307); - - /** - * The requested resource MUST be accessed through the proxy given by the - * Location field. - * - * @see HTTP - * RFC - 10.3.6 305 Use Proxy - */ - public static final Status REDIRECTION_USE_PROXY = new Status(305); - - /** - * The server, while acting as a gateway or proxy, received an invalid response - * from the upstream server it accessed in attempting to fulfill the request. - * - * @see HTTP - * RFC - 10.5.3 502 Bad Gateway - */ - public static final Status SERVER_ERROR_BAD_GATEWAY = new Status(502); - - /** - * The server, while acting as a gateway or proxy, could not connect to the - * upstream server. - * - * @see HTTP - * RFC - 10.5.5 504 Gateway Timeout - */ - public static final Status SERVER_ERROR_GATEWAY_TIMEOUT = new Status(504); - - /** - * The server encountered an unexpected condition which prevented it from - * fulfilling the request. - * - * @see HTTP - * RFC - 10.5.1 500 Internal Server Error - */ - public static final Status SERVER_ERROR_INTERNAL = new Status(500); - - /** - * The server does not support the functionality required to fulfill the - * request. - * - * @see HTTP - * RFC - 10.5.2 501 Not Implemented - */ - public static final Status SERVER_ERROR_NOT_IMPLEMENTED = new Status(501); - - /** - * The server is currently unable to handle the request due to a temporary - * overloading or maintenance of the server. - * - * @see HTTP - * RFC - 10.5.4 503 Service Unavailable - */ - public static final Status SERVER_ERROR_SERVICE_UNAVAILABLE = new Status(503); - - /** - * The server does not support, or refuses to support, the HTTP protocol version - * that was used in the request message. - * - * @see HTTP - * RFC - 10.5.6 505 HTTP Version Not Supported - */ - public static final Status SERVER_ERROR_VERSION_NOT_SUPPORTED = new Status(505); - - /** - * The request has been accepted for processing, but the processing has not been - * completed. - * - * @see HTTP - * RFC - 10.2.3 202 Accepted - */ - public static final Status SUCCESS_ACCEPTED = new Status(202); - - /** - * The request has been fulfilled and resulted in a new resource being created. - * - * @see HTTP - * RFC - 10.2.2 201 Created - */ - public static final Status SUCCESS_CREATED = new Status(201); - - /** - * Warning status code, optionally including arbitrary information to be - * presented to a human user, typically returned by a cache. - * - * @see HTTP - * RFC - 14.46 Warning - */ - public static final Status SUCCESS_MISC_PERSISTENT_WARNING = new Status(299); - - /** - * The server has fulfilled the request but does not need to return an - * entity-body (for example after a DELETE), and might want to return updated - * meta-information. - * - * @see HTTP - * RFC - 10.2.5 204 No Content - */ - public static final Status SUCCESS_NO_CONTENT = new Status(204); - - /** - * The request has succeeded but the returned meta-information in the - * entity-header does not come from the origin server, but is gathered from a - * local or a third-party copy. - * - * @see HTTP - * RFC - 10.2.4 203 Non-Authoritative Information - */ - public static final Status SUCCESS_NON_AUTHORITATIVE = new Status(203); - - /** - * The request has succeeded. - * - * @see HTTP - * RFC - 10.2.1 200 OK - */ - public static final Status SUCCESS_OK = new Status(200); - - /** - * The server has fulfilled the partial GET request for the resource assuming - * the request has included a Range header field indicating the desired range. - * - * @see HTTP - * RFC - 10.2.7 206 Partial Content - */ - public static final Status SUCCESS_PARTIAL_CONTENT = new Status(206); - - /** - * The server has fulfilled the request, and the user agent SHOULD reset the - * document view which caused the request to be sent. - * - * @see HTTP - * RFC - 10.2.6 205 Reset Content - */ - public static final Status SUCCESS_RESET_CONTENT = new Status(205); - - /** - * Warning status code, typically returned by a cache or a proxy, indicating - * that the response has been transformed. - * - * @see HTTP - * RFC - 14.46 Warning - */ - public static final Status SUCCESS_TRANSFORMATION_APPLIED = new Status(214); - - /** - * Check if the provided reason phrase of the status contains forbidden - * characters such as CR and LF. An IllegalArgumentException is thrown in this - * case. - * - * @see Status - * Code and Reason Phrase - * @param reasonPhrase The reason phrase to check. - * @return The name if it is correct. - */ - private static String checkReasonPhrase(String reasonPhrase) { - if (reasonPhrase != null) { - if (reasonPhrase.contains("\n") || reasonPhrase.contains("\r")) { - throw new IllegalArgumentException("Reason phrase of the status must not contain CR or LF characters."); - } - } - - return reasonPhrase; - } - - /** - * Indicates if the status is a client error status, meaning "The request - * contains bad syntax or cannot be fulfilled". - * - * @param code The code of the status. - * @return True if the status is a client error status. - */ - public static boolean isClientError(int code) { - return (code >= 400) && (code <= 499); - } - - /** - * Indicates if the status is a connector error status, meaning "The connector - * failed to send or receive an apparently valid message". - * - * @param code The code of the status. - * @return True if the status is a server error status. - */ - public static boolean isConnectorError(int code) { - return (code >= 1000) && (code <= 1099); - } - - /** - * Indicates if the status is an error (client or server) status. - * - * @param code The code of the status. - * @return True if the status is an error (client or server) status. - */ - public static boolean isError(int code) { - return isClientError(code) || isServerError(code) || isConnectorError(code) || isGlobalError(code); - } - - /** - * Indicates if the status is a client error status, meaning "The request - * contains bad syntax or cannot be fulfilled". - * - * @param code The code of the status. - * @return True if the status is a client error status. - */ - public static boolean isGlobalError(int code) { - return (code >= 600) && (code <= 699); - } - - /** - * Indicates if the status is an information status, meaning "request received, - * continuing process". - * - * @param code The code of the status. - * @return True if the status is an information status. - */ - public static boolean isInformational(int code) { - return (code >= 100) && (code <= 199); - } - - /** - * Indicates if the status is a redirection status, meaning "Further action must - * be taken in order to complete the request". - * - * @param code The code of the status. - * @return True if the status is a redirection status. - */ - public static boolean isRedirection(int code) { - return (code >= 300) && (code <= 399); - } - - /** - * Indicates if the status is a server error status, meaning "The server failed - * to fulfill an apparently valid request". - * - * @param code The code of the status. - * @return True if the status is a server error status. - */ - public static boolean isServerError(int code) { - return (code >= 500) && (code <= 599); - } - - /** - * Indicates if the status is a success status, meaning "The action was - * successfully received, understood, and accepted". - * - * @param code The code of the status. - * @return True if the status is a success status. - */ - public static boolean isSuccess(int code) { - return (code >= 200) && (code <= 299); - } - - /** - * Returns the status associated to a code. If an existing constant exists then - * it is returned, otherwise a new instance is created. - * - * @param code The code. - * @return The associated status. - */ - public static Status valueOf(int code) { + private static final String BASE_ADDED_HTTP = "http://tools.ietf.org/html/rfc6585"; + + private static final String BASE_HTTP = + "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"; + + private static final String BASE_RESTLET = + "https://javadoc.io/static/org.restlet/org.restlet/" + Engine.VERSION + "/"; + + /** + * The server could not understand the request due to malformed syntax. + * + * @see HTTP RFC - + * 10.4.1 400 Bad Request + */ + public static final Status CLIENT_ERROR_BAD_REQUEST = new Status(400); + + /** + * The request could not be completed due to a conflict with the current state of the resource + * (as experienced in a version control system). + * + * @see HTTP RFC - + * 10.4.10 409 Conflict + */ + public static final Status CLIENT_ERROR_CONFLICT = new Status(409); + + /** + * The user agent expects some behavior of the server (given in an Expect request-header field), + * but this expectation could not be met by this server. + * + * @see HTTP RFC - + * 10.4.18 417 Expectation Failed + */ + public static final Status CLIENT_ERROR_EXPECTATION_FAILED = new Status(417); + + /** + * The server understood the request, but is refusing to fulfill it as it could be explained in + * the entity. + * + * @see HTTP RFC - + * 10.4.4 403 Forbidden + */ + public static final Status CLIENT_ERROR_FORBIDDEN = new Status(403); + + /** + * The requested resource is no longer available at the server and no forwarding address is + * known. + * + * @see HTTP RFC - + * 10.4.11 410 Gone + */ + public static final Status CLIENT_ERROR_GONE = new Status(410); + + /** + * The server refuses to accept the request without a defined Content-Length. + * + * @see HTTP RFC - + * 10.4.12 411 Length Required + */ + public static final Status CLIENT_ERROR_LENGTH_REQUIRED = new Status(411); + + /** + * The method specified in the Request-Line is not allowed for the resource identified by the + * Request-URI. + * + * @see HTTP RFC - + * 10.4.6 405 Method Not Allowed + */ + public static final Status CLIENT_ERROR_METHOD_NOT_ALLOWED = new Status(405); + + /** + * The resource identified by the request is only capable of generating response entities whose + * content characteristics do not match the user's requirements (in Accept* headers). + * + * @see HTTP RFC - + * 10.4.7 406 Not Acceptable + */ + public static final Status CLIENT_ERROR_NOT_ACCEPTABLE = new Status(406); + + /** + * The server has not found anything matching the Request-URI or the server does not wish to + * reveal exactly why the request has been refused, or no other response is applicable. + * + * @see HTTP RFC - + * 10.4.5 404 Not Found + */ + public static final Status CLIENT_ERROR_NOT_FOUND = new Status(404); + + /** + * This code is reserved for future use. + * + * @see HTTP RFC - + * 10.4.3 402 Payment Required + */ + public static final Status CLIENT_ERROR_PAYMENT_REQUIRED = new Status(402); + + /** + * Sent by the server when the user agent asks the server to carry out a request under certain + * conditions that are not met. + * + * @see HTTP RFC - + * 10.4.13 412 Precondition Failed + */ + public static final Status CLIENT_ERROR_PRECONDITION_FAILED = new Status(412); + + /** + * This code is similar to 401 (Unauthorized), but indicates that the client must first + * authenticate itself with the proxy. + * + * @see HTTP RFC - + * 10.4.8 407 Proxy Authentication Required + */ + public static final Status CLIENT_ERROR_PROXY_AUTHENTIFICATION_REQUIRED = new Status(407); + + /** + * The server is refusing to process a request because the request entity is larger than the + * server is willing or able to process. + * + * @see HTTP RFC - + * 10.4.14 413 Request Entity Too Large + */ + public static final Status CLIENT_ERROR_REQUEST_ENTITY_TOO_LARGE = new Status(413); + + /** + * Sent by the server when an HTTP client opens a connection, but has never sent a request (or + * never sent the blank line that signals the end of the request). + * + * @see HTTP RFC - + * 10.4.9 408 Request Timeout + */ + public static final Status CLIENT_ERROR_REQUEST_TIMEOUT = new Status(408); + + /** + * The server is refusing to service the request because the Request-URI is longer than the + * server is willing to interpret. + * + * @see HTTP RFC - + * 10.4.15 414 Request-URI Too Long + */ + public static final Status CLIENT_ERROR_REQUEST_URI_TOO_LONG = new Status(414); + + /** + * The request includes a Range request-header field and the selected resource is too small for + * any of the byte-ranges to apply. + * + * @see HTTP RFC - + * 10.4.17 416 Requested Range Not Satisfiable + */ + public static final Status CLIENT_ERROR_REQUESTED_RANGE_NOT_SATISFIABLE = new Status(416); + + /** + * The server refuses to accept the request because the user has sent too many requests in a + * given amount of time. + * + * @see HTTP RFC - 10.4.12 429 Too Many + * Requests + */ + public static final Status CLIENT_ERROR_TOO_MANY_REQUESTS = new Status(429); + + /** + * The request requires user authentication. + * + * @see HTTP RFC - + * 10.4.2 401 Unauthorized + */ + public static final Status CLIENT_ERROR_UNAUTHORIZED = new Status(401); + + /** + * The server is refusing to service the request because the entity of the request is in a + * format not supported by the requested resource for the requested method. + * + * @see HTTP RFC - + * 10.4.16 415 Unsupported Media Type + */ + public static final Status CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE = new Status(415); + + /** + * The client connector faced an error during the communication with the remote server + * (interruption, timeout, etc.). The status code is 1001. + */ + public static final Status CONNECTOR_ERROR_COMMUNICATION = new Status(1001); + + /** The client connector could not connect to the remote server. The status code is 1000. */ + public static final Status CONNECTOR_ERROR_CONNECTION = new Status(1000); + + /** + * The client connector faced an internal error during the process of a request to its server or + * the process of a response to its client. The status code is 1002. + */ + public static final Status CONNECTOR_ERROR_INTERNAL = new Status(1002); + + /** + * This interim response (the client has to wait for the final response) is used to inform the + * client that the initial part of the request has been received and has not yet been rejected + * or completed by the server. + * + * @see HTTP RFC - + * 10.1.1 100 Continue + */ + public static final Status INFO_CONTINUE = new Status(100); + + /** + * Warning status code, typically returned by a cache, indicating that it is intentionally + * disconnected from the rest of the network for a period of time. + * + * @see HTTP RFC - + * 14.46 Warning + */ + public static final Status INFO_DISCONNECTED_OPERATION = new Status(112); + + /** + * Warning status code, typically returned by a cache, indicating that it heuristically chose a + * freshness lifetime greater than 24 hours and the response's age is greater than 24 hours. + * + * @see HTTP RFC - + * 14.46 Warning + */ + public static final Status INFO_HEURISTIC_EXPIRATION = new Status(113); + + /** + * Warning status code, optionally including arbitrary information to be presented to a human + * user, typically returned by a cache. + * + * @see HTTP RFC - + * 14.46 Warning + */ + public static final Status INFO_MISC_WARNING = new Status(199); + + /** + * Warning status code, typically returned by a cache, indicating that the response is stale + * because an attempt to revalidate the response failed, due to an inability to reach the + * server. + * + * @see HTTP RFC - + * 14.46 Warning + */ + public static final Status INFO_REVALIDATION_FAILED = new Status(111); + + /** + * Warning status code, typically returned by a cache, indicating that the response is stale. + * + * @see HTTP RFC - + * 14.46 Warning + */ + public static final Status INFO_STALE_RESPONSE = new Status(110); + + /** + * The server understands and is willing to comply with the client's request, via the Upgrade + * message header field, for a change in the application protocol being used on this connection. + * + * @see HTTP RFC - + * 10.1.1 101 Switching Protocols + */ + public static final Status INFO_SWITCHING_PROTOCOL = new Status(101); + + /** + * The requested resource resides temporarily under a different URI which should not be used for + * future requests by the client (use status codes 303 or 307 instead since this status has been + * manifestly misused). + * + * @see HTTP RFC - + * 10.3.3 302 Found + */ + public static final Status REDIRECTION_FOUND = new Status(302); + + /** + * The server lets the user agent choosing one of the multiple representations of the requested + * resource, each representation having its own specific location provided in the response + * entity. + * + * @see HTTP RFC - + * 10.3.1 300 Multiple Choices + */ + public static final Status REDIRECTION_MULTIPLE_CHOICES = new Status(300); + + /** + * Status code sent by the server in response to a conditional GET request in case the document + * has not been modified. + * + * @see HTTP RFC - + * 10.3.5 304 Not Modified + */ + public static final Status REDIRECTION_NOT_MODIFIED = new Status(304); + + /** + * The requested resource has been assigned a new permanent URI and any future references to + * this resource SHOULD use one of the returned URIs. + * + * @see HTTP RFC - + * 10.3.2 301 Moved Permanently + */ + public static final Status REDIRECTION_PERMANENT = new Status(301); + + /** + * The response to the request can be found under a different URI and SHOULD be retrieved using + * a GET method on that resource. + * + * @see HTTP RFC - + * 10.3.4 303 See Other + */ + public static final Status REDIRECTION_SEE_OTHER = new Status(303); + + /** + * The requested resource resides temporarily under a different URI which should not be used for + * future requests by the client. + * + * @see HTTP RFC - + * 10.3.8 307 Temporary Redirect + */ + public static final Status REDIRECTION_TEMPORARY = new Status(307); + + /** + * The requested resource MUST be accessed through the proxy given by the Location field. + * + * @see HTTP RFC - + * 10.3.6 305 Use Proxy + */ + public static final Status REDIRECTION_USE_PROXY = new Status(305); + + /** + * The server, while acting as a gateway or proxy, received an invalid response from the + * upstream server it accessed in attempting to fulfill the request. + * + * @see HTTP RFC - + * 10.5.3 502 Bad Gateway + */ + public static final Status SERVER_ERROR_BAD_GATEWAY = new Status(502); + + /** + * The server, while acting as a gateway or proxy, could not connect to the upstream server. + * + * @see HTTP RFC - + * 10.5.5 504 Gateway Timeout + */ + public static final Status SERVER_ERROR_GATEWAY_TIMEOUT = new Status(504); + + /** + * The server encountered an unexpected condition which prevented it from fulfilling the + * request. + * + * @see HTTP RFC - + * 10.5.1 500 Internal Server Error + */ + public static final Status SERVER_ERROR_INTERNAL = new Status(500); + + /** + * The server does not support the functionality required to fulfill the request. + * + * @see HTTP RFC - + * 10.5.2 501 Not Implemented + */ + public static final Status SERVER_ERROR_NOT_IMPLEMENTED = new Status(501); + + /** + * The server is currently unable to handle the request due to a temporary overloading or + * maintenance of the server. + * + * @see HTTP RFC - + * 10.5.4 503 Service Unavailable + */ + public static final Status SERVER_ERROR_SERVICE_UNAVAILABLE = new Status(503); + + /** + * The server does not support, or refuses to support, the HTTP protocol version that was used + * in the request message. + * + * @see HTTP RFC - + * 10.5.6 505 HTTP Version Not Supported + */ + public static final Status SERVER_ERROR_VERSION_NOT_SUPPORTED = new Status(505); + + /** + * The request has been accepted for processing, but the processing has not been completed. + * + * @see HTTP RFC - + * 10.2.3 202 Accepted + */ + public static final Status SUCCESS_ACCEPTED = new Status(202); + + /** + * The request has been fulfilled and resulted in a new resource being created. + * + * @see HTTP RFC - + * 10.2.2 201 Created + */ + public static final Status SUCCESS_CREATED = new Status(201); + + /** + * Warning status code, optionally including arbitrary information to be presented to a human + * user, typically returned by a cache. + * + * @see HTTP RFC - + * 14.46 Warning + */ + public static final Status SUCCESS_MISC_PERSISTENT_WARNING = new Status(299); + + /** + * The server has fulfilled the request but does not need to return an entity-body (for example + * after a DELETE), and might want to return updated meta-information. + * + * @see HTTP RFC - + * 10.2.5 204 No Content + */ + public static final Status SUCCESS_NO_CONTENT = new Status(204); + + /** + * The request has succeeded but the returned meta-information in the entity-header does not + * come from the origin server, but is gathered from a local or a third-party copy. + * + * @see HTTP RFC - + * 10.2.4 203 Non-Authoritative Information + */ + public static final Status SUCCESS_NON_AUTHORITATIVE = new Status(203); + + /** + * The request has succeeded. + * + * @see HTTP RFC - + * 10.2.1 200 OK + */ + public static final Status SUCCESS_OK = new Status(200); + + /** + * The server has fulfilled the partial GET request for the resource assuming the request has + * included a Range header field indicating the desired range. + * + * @see HTTP RFC - + * 10.2.7 206 Partial Content + */ + public static final Status SUCCESS_PARTIAL_CONTENT = new Status(206); + + /** + * The server has fulfilled the request, and the user agent SHOULD reset the document view which + * caused the request to be sent. + * + * @see HTTP RFC - + * 10.2.6 205 Reset Content + */ + public static final Status SUCCESS_RESET_CONTENT = new Status(205); + + /** + * Warning status code, typically returned by a cache or a proxy, indicating that the response + * has been transformed. + * + * @see HTTP RFC - + * 14.46 Warning + */ + public static final Status SUCCESS_TRANSFORMATION_APPLIED = new Status(214); + + /** + * Check if the provided reason phrase of the status contains forbidden characters such as CR + * and LF. An IllegalArgumentException is thrown in this case. + * + * @see Status Code + * and Reason Phrase + * @param reasonPhrase The reason phrase to check. + * @return The name if it is correct. + */ + private static String checkReasonPhrase(String reasonPhrase) { + if (reasonPhrase != null) { + if (reasonPhrase.contains("\n") || reasonPhrase.contains("\r")) { + throw new IllegalArgumentException( + "Reason phrase of the status must not contain CR or LF characters."); + } + } + + return reasonPhrase; + } + + /** + * Indicates if the status is a client error status, meaning "The request contains bad syntax or + * cannot be fulfilled". + * + * @param code The code of the status. + * @return True if the status is a client error status. + */ + public static boolean isClientError(int code) { + return (code >= 400) && (code <= 499); + } + + /** + * Indicates if the status is a connector error status, meaning "The connector failed to send or + * receive an apparently valid message". + * + * @param code The code of the status. + * @return True if the status is a server error status. + */ + public static boolean isConnectorError(int code) { + return (code >= 1000) && (code <= 1099); + } + + /** + * Indicates if the status is an error (client or server) status. + * + * @param code The code of the status. + * @return True if the status is an error (client or server) status. + */ + public static boolean isError(int code) { + return isClientError(code) + || isServerError(code) + || isConnectorError(code) + || isGlobalError(code); + } + + /** + * Indicates if the status is a client error status, meaning "The request contains bad syntax or + * cannot be fulfilled". + * + * @param code The code of the status. + * @return True if the status is a client error status. + */ + public static boolean isGlobalError(int code) { + return (code >= 600) && (code <= 699); + } + + /** + * Indicates if the status is an information status, meaning "request received, continuing + * process". + * + * @param code The code of the status. + * @return True if the status is an information status. + */ + public static boolean isInformational(int code) { + return (code >= 100) && (code <= 199); + } + + /** + * Indicates if the status is a redirection status, meaning "Further action must be taken in + * order to complete the request". + * + * @param code The code of the status. + * @return True if the status is a redirection status. + */ + public static boolean isRedirection(int code) { + return (code >= 300) && (code <= 399); + } + + /** + * Indicates if the status is a server error status, meaning "The server failed to fulfill an + * apparently valid request". + * + * @param code The code of the status. + * @return True if the status is a server error status. + */ + public static boolean isServerError(int code) { + return (code >= 500) && (code <= 599); + } + + /** + * Indicates if the status is a success status, meaning "The action was successfully received, + * understood, and accepted". + * + * @param code The code of the status. + * @return True if the status is a success status. + */ + public static boolean isSuccess(int code) { + return (code >= 200) && (code <= 299); + } + + /** + * Returns the status associated with a code. If an existing constant exists, then it is + * returned, otherwise a new instance is created. + * + * @param code The code. + * @return The associated status. + */ + public static Status valueOf(int code) { return switch (code) { case 100 -> INFO_CONTINUE; case 101 -> INFO_SWITCHING_PROTOCOL; @@ -696,217 +636,220 @@ public static Status valueOf(int code) { case 1002 -> CONNECTOR_ERROR_INTERNAL; default -> new Status(code); }; - } - - /** The specification code. */ - private final int code; - - /** The longer description. */ - private final String description; - - /** - * The short reason phrase displayed next to the status code in a HTTP response. - */ - private volatile String reasonPhrase; - - /** The related error or exception. */ - private final Throwable throwable; - - /** The URI of the specification describing the method. */ - private final String uri; - - /** - * Constructor. - * - * @param code The specification code. - */ - public Status(int code) { - this(code, null, null, null, null); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - */ - public Status(int code, String reasonPhrase) { - this(code, null, reasonPhrase, null, null); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The longer description. - */ - public Status(int code, String reasonPhrase, String description) { - this(code, null, reasonPhrase, description, null); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The longer description. - * @param uri The URI of the specification describing the method. - */ - public Status(int code, String reasonPhrase, String description, String uri) { - this(code, null, reasonPhrase, description, uri); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param throwable The related error or exception. - */ - public Status(int code, Throwable throwable) { - this(code, throwable, null, null, null); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - */ - public Status(int code, Throwable throwable, String reasonPhrase) { - this(code, throwable, reasonPhrase, null, null); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The longer description. - */ - public Status(int code, Throwable throwable, String reasonPhrase, String description) { - this(code, throwable, reasonPhrase, description, null); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The longer description. - * @param uri The URI of the specification describing the method. - */ - public Status(int code, Throwable throwable, String reasonPhrase, String description, String uri) { - this.code = code; - this.throwable = throwable; - this.reasonPhrase = checkReasonPhrase(reasonPhrase); - this.description = description; - this.uri = uri; - } - - /** - * Constructor. - * - * @param status The status to copy. - * @param description The description to associate. - */ - public Status(Status status, String description) { - this(status, null, null, description); - } - - /** - * Constructor. - * - * @param status The status to copy. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The description to associate. - */ - public Status(Status status, String reasonPhrase, String description) { - this(status, null, reasonPhrase, description); - } - - /** - * Constructor. - * - * @param status The status to copy. - * @param throwable The related error or exception. - */ - public Status(Status status, Throwable throwable) { - this(status, throwable, null, null); - } - - /** - * Constructor. - * - * @param status The status to copy. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - */ - public Status(Status status, Throwable throwable, String reasonPhrase) { - this(status, throwable, reasonPhrase, null); - } - - /** - * Constructor. - * - * @param status The status to copy. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The description to associate. - */ - public Status(Status status, Throwable throwable, String reasonPhrase, String description) { - this(status.getCode(), (throwable == null) ? status.getThrowable() : throwable, - (reasonPhrase == null) ? status.getReasonPhrase() : reasonPhrase, - (description == null) ? status.getDescription() : description, status.getUri()); - } - - /** - * Indicates if the status is equal to a given one. - * - * @param object The object to compare to. - * @return True if the status is equal to a given one. - */ - @Override - public boolean equals(final Object object) { - return (object instanceof Status) && (this.code == ((Status) object).getCode()); - } - - /** - * Returns the corresponding code (HTTP or custom code). - * - * @return The corresponding code. - */ - public int getCode() { - return this.code; - } - - /** - * Returns the description. This value is typically used by the - * {@link org.restlet.service.StatusService} to build a meaningful description - * of an error via a response entity. - * - * @return The description. - */ - public String getDescription() { - if (this.description != null) { - return this.description; - } + } + + /** The specification code. */ + private final int code; + + /** The longer description. */ + private final String description; + + /** The short reason phrase displayed next to the status code in an HTTP response. */ + private volatile String reasonPhrase; + + /** The related error or exception. */ + private final Throwable throwable; + + /** The URI of the specification describing the method. */ + private final String uri; + + /** + * Constructor. + * + * @param code The specification code. + */ + public Status(int code) { + this(code, null, null, null, null); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + */ + public Status(int code, String reasonPhrase) { + this(code, null, reasonPhrase, null, null); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The longer description. + */ + public Status(int code, String reasonPhrase, String description) { + this(code, null, reasonPhrase, description, null); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The longer description. + * @param uri The URI of the specification describing the method. + */ + public Status(int code, String reasonPhrase, String description, String uri) { + this(code, null, reasonPhrase, description, uri); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param throwable The related error or exception. + */ + public Status(int code, Throwable throwable) { + this(code, throwable, null, null, null); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + */ + public Status(int code, Throwable throwable, String reasonPhrase) { + this(code, throwable, reasonPhrase, null, null); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The longer description. + */ + public Status(int code, Throwable throwable, String reasonPhrase, String description) { + this(code, throwable, reasonPhrase, description, null); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The longer description. + * @param uri The URI of the specification describing the method. + */ + public Status( + int code, Throwable throwable, String reasonPhrase, String description, String uri) { + this.code = code; + this.throwable = throwable; + this.reasonPhrase = checkReasonPhrase(reasonPhrase); + this.description = description; + this.uri = uri; + } + + /** + * Constructor. + * + * @param status The status to copy. + * @param description The description to associate. + */ + public Status(Status status, String description) { + this(status, null, null, description); + } + + /** + * Constructor. + * + * @param status The status to copy. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The description to associate. + */ + public Status(Status status, String reasonPhrase, String description) { + this(status, null, reasonPhrase, description); + } + + /** + * Constructor. + * + * @param status The status to copy. + * @param throwable The related error or exception. + */ + public Status(Status status, Throwable throwable) { + this(status, throwable, null, null); + } + + /** + * Constructor. + * + * @param status The status to copy. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + */ + public Status(Status status, Throwable throwable, String reasonPhrase) { + this(status, throwable, reasonPhrase, null); + } + + /** + * Constructor. + * + * @param status The status to copy. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The description to associate. + */ + public Status(Status status, Throwable throwable, String reasonPhrase, String description) { + this( + status.getCode(), + (throwable == null) ? status.getThrowable() : throwable, + (reasonPhrase == null) ? status.getReasonPhrase() : reasonPhrase, + (description == null) ? status.getDescription() : description, + status.getUri()); + } + + /** + * Indicates if the status is equal to a given one. + * + * @param object The object to compare to. + * @return True if the status is equal to a given one. + */ + @Override + public boolean equals(final Object object) { + return (object instanceof Status) && (this.code == ((Status) object).getCode()); + } + + /** + * Returns the corresponding code (HTTP or custom code). + * + * @return The corresponding code. + */ + public int getCode() { + return this.code; + } + + /** + * Returns the description. This value is typically used by the {@link + * org.restlet.service.StatusService} to build a meaningful description of an error via a + * response entity. + * + * @return The description. + */ + public String getDescription() { + if (this.description != null) { + return this.description; + } return switch (this.code) { case 100 -> "The client should continue with its request"; - case 101 -> "The server is willing to change the application protocol being used on this connection"; + case 101 -> + "The server is willing to change the application protocol being used on this connection"; case 110 -> "MUST be included whenever the returned response is stale"; case 111 -> "MUST be included if a cache returns a stale response because an attempt to revalidate the response failed, due to an inability to reach the server"; @@ -917,9 +860,12 @@ public String getDescription() { case 199 -> "The warning text MAY include arbitrary information to be presented to a human user, or logged. A system receiving this warning MUST NOT take any automated action, besides presenting the warning to the user"; case 200 -> "The request has succeeded"; - case 201 -> "The request has been fulfilled and resulted in a new resource being created"; - case 202 -> "The request has been accepted for processing, but the processing has not been completed"; - case 203 -> "The returned meta-information is not the definitive set as available from the origin server"; + case 201 -> + "The request has been fulfilled and resulted in a new resource being created"; + case 202 -> + "The request has been accepted for processing, but the processing has not been completed"; + case 203 -> + "The returned meta-information is not the definitive set as available from the origin server"; case 204 -> "The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta-information"; case 205 -> @@ -933,8 +879,10 @@ public String getDescription() { case 301 -> "The requested resource has been assigned a new permanent URI"; case 302 -> "The requested resource can be found under a different URI"; case 303 -> "The response to the request can be found under a different URI"; - case 304 -> "The client has performed a conditional GET request and the document has not been modified"; - case 305 -> "The requested resource must be accessed through the proxy given by the location field"; + case 304 -> + "The client has performed a conditional GET request and the document has not been modified"; + case 305 -> + "The requested resource must be accessed through the proxy given by the location field"; case 307 -> "The requested resource resides temporarily under a different URI"; case 400 -> "The request could not be understood by the server due to malformed syntax"; case 401 -> "The request requires user authentication"; @@ -947,8 +895,10 @@ public String getDescription() { "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request"; case 407 -> "This code is similar to Unauthorized, but indicates that the client must first authenticate itself with the proxy"; - case 408 -> "The client did not produce a request within the time that the server was prepared to wait"; - case 409 -> "The request could not be completed due to a conflict with the current state of the resource"; + case 408 -> + "The client did not produce a request within the time that the server was prepared to wait"; + case 409 -> + "The request could not be completed due to a conflict with the current state of the resource"; case 410 -> "The requested resource is no longer available at the server and no forwarding address is known"; case 411 -> "The server refuses to accept the request without a defined content length"; @@ -962,17 +912,20 @@ public String getDescription() { "The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method"; case 416 -> "For byte ranges, this means that the first byte position were greater than the current length of the selected resource"; - case 417 -> "The expectation given in the request header could not be met by this server"; + case 417 -> + "The expectation given in the request header could not be met by this server"; case 429 -> "The server is refusing to service the request because the user has sent too many requests in a given amount of time (\"rate limiting\")"; - case 500 -> "The server encountered an unexpected condition which prevented it from fulfilling the request"; - case 501 -> "The server does not support the functionality required to fulfill the request"; + case 500 -> + "The server encountered an unexpected condition which prevented it from fulfilling the request"; + case 501 -> + "The server does not support the functionality required to fulfill the request"; case 502 -> "The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request"; case 503 -> "The server is currently unable to handle the request due to a temporary overloading or maintenance of the server"; case 504 -> - "The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed to access in attempting to complete the request"; + "The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI (e.g., HTTP, FTP, LDAP) or some other auxiliary server (e.g., DNS) it needed to access in attempting to complete the request"; case 505 -> "The server does not support, or refuses to support, the protocol version that was used in the request message"; case 1000 -> "The connector failed to connect to the server"; @@ -981,20 +934,18 @@ public String getDescription() { "The connector encountered an unexpected condition which prevented it from fulfilling the request"; default -> null; }; - } - /** - * Returns the reason phrase of this status. When supported by the HTTP server - * connector, this is returned in the first line of the HTTP response, next to - * to the status code. - * - * @return The reason phrase of this status. - */ - public String getReasonPhrase() { - if (this.reasonPhrase != null) { - return this.reasonPhrase; - } + /** + * Returns the reason phrase of this status. When supported by the HTTP server connector, this + * is returned in the first line of the HTTP response, next to the status code. + * + * @return The reason phrase of this status. + */ + public String getReasonPhrase() { + if (this.reasonPhrase != null) { + return this.reasonPhrase; + } return switch (this.code) { case 100 -> "Continue"; @@ -1050,32 +1001,32 @@ public String getReasonPhrase() { case 1002 -> "Internal Connector Error"; default -> null; }; + } + /** + * Returns the related error or exception. + * + * @return The related error or exception. + */ + public Throwable getThrowable() { + return this.throwable; } - /** - * Returns the related error or exception. - * - * @return The related error or exception. - */ - public Throwable getThrowable() { - return this.throwable; - } - - /** - * Returns the URI of the specification describing the status. - * - * @return The URI of the specification describing the status. - */ - public String getUri() { - if (this.uri != null) { - return this.uri; - } + /** + * Returns the URI of the specification describing the status. + * + * @return The URI of the specification describing the status. + */ + public String getUri() { + if (this.uri != null) { + return this.uri; + } return switch (this.code) { case 100 -> BASE_HTTP + "#sec10.1.1"; case 101 -> BASE_HTTP + "#sec10.1.2"; - case 110, 111, 112, 113, 199, 214, 299 -> "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46"; + case 110, 111, 112, 113, 199, 214, 299 -> + "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46"; case 200 -> BASE_HTTP + "#sec10.2.1"; case 201 -> BASE_HTTP + "#sec10.2.2"; case 202 -> BASE_HTTP + "#sec10.2.3"; @@ -1116,121 +1067,124 @@ public String getUri() { case 504 -> BASE_HTTP + "#sec10.5.5"; case 505 -> BASE_HTTP + "#sec10.5.6"; case 1000 -> BASE_RESTLET + "org/restlet/data/Status.html#CONNECTOR_ERROR_CONNECTION"; - case 1001 -> BASE_RESTLET + "org/restlet/data/Status.html#CONNECTOR_ERROR_COMMUNICATION"; + case 1001 -> + BASE_RESTLET + "org/restlet/data/Status.html#CONNECTOR_ERROR_COMMUNICATION"; case 1002 -> BASE_RESTLET + "org/restlet/data/Status.html#CONNECTOR_ERROR_INTERNAL"; default -> null; }; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return getCode(); + } + + /** + * Indicates if the status is a client error status, meaning "The request contains bad syntax or + * cannot be fulfilled". + * + * @return True if the status is a client error status. + */ + public boolean isClientError() { + return isClientError(getCode()); + } + /** + * Indicates if the status is a connector error status, meaning "The connector failed to send or + * receive an apparently valid message". + * + * @return True if the status is a connector error status. + */ + public boolean isConnectorError() { + return isConnectorError(getCode()); } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return getCode(); - } - - /** - * Indicates if the status is a client error status, meaning "The request - * contains bad syntax or cannot be fulfilled". - * - * @return True if the status is a client error status. - */ - public boolean isClientError() { - return isClientError(getCode()); - } - - /** - * Indicates if the status is a connector error status, meaning "The connector - * failed to send or receive an apparently valid message". - * - * @return True if the status is a connector error status. - */ - public boolean isConnectorError() { - return isConnectorError(getCode()); - } - - /** - * Indicates if the status is an error (client or server) status. - * - * @return True if the status is an error (client or server) status. - */ - public boolean isError() { - return isError(getCode()); - } - - /** - * Indicates if the status is a global error status, meaning "The server has - * definitive information about a particular user". - * - * @return True if the status is a global error status. - */ - public boolean isGlobalError() { - return isGlobalError(getCode()); - } - - /** - * Indicates if the status is an information status, meaning "request received, - * continuing process". - * - * @return True if the status is an information status. - */ - public boolean isInformational() { - return isInformational(getCode()); - } - - /** - * Indicates if an error is recoverable, meaning that simply retrying after a - * delay could result in a success. Tests {@link #isConnectorError()} and if the - * status is {@link #CLIENT_ERROR_REQUEST_TIMEOUT} or - * {@link #SERVER_ERROR_GATEWAY_TIMEOUT} or - * {@link #SERVER_ERROR_SERVICE_UNAVAILABLE}. - * - * @return True if the error is recoverable. - */ - public boolean isRecoverableError() { - return isConnectorError() || equals(Status.CLIENT_ERROR_REQUEST_TIMEOUT) - || equals(Status.SERVER_ERROR_GATEWAY_TIMEOUT) || equals(Status.SERVER_ERROR_SERVICE_UNAVAILABLE); - } - - /** - * Indicates if the status is a redirection status, meaning "Further action must - * be taken in order to complete the request". - * - * @return True if the status is a redirection status. - */ - public boolean isRedirection() { - return isRedirection(getCode()); - } - - /** - * Indicates if the status is a server error status, meaning "The server failed - * to fulfill an apparently valid request". - * - * @return True if the status is a server error status. - */ - public boolean isServerError() { - return isServerError(getCode()); - } - - /** - * Indicates if the status is a success status, meaning "The action was - * successfully received, understood, and accepted". - * - * @return True if the status is a success status. - */ - public boolean isSuccess() { - return isSuccess(getCode()); - } - - /** - * Returns the reason phrase of the status followed by its HTTP code. - * - * @return The reason phrase of the status followed by its HTTP code. - */ - @Override - public String toString() { - return getReasonPhrase() + " (" + this.code + ")" - + ((getDescription() == null) ? "" : " - " + getDescription()); - } + /** + * Indicates if the status is an error (client or server) status. + * + * @return True if the status is an error (client or server) status. + */ + public boolean isError() { + return isError(getCode()); + } + + /** + * Indicates if the status is a global error status, meaning "The server has definitive + * information about a particular user". + * + * @return True if the status is a global error status. + */ + public boolean isGlobalError() { + return isGlobalError(getCode()); + } + + /** + * Indicates if the status is an information status, meaning "request received, continuing + * process". + * + * @return True if the status is an information status. + */ + public boolean isInformational() { + return isInformational(getCode()); + } + /** + * Indicates if an error is recoverable, meaning that simply retrying after a delay could result + * in a success. Tests {@link #isConnectorError()} and if the status is {@link + * #CLIENT_ERROR_REQUEST_TIMEOUT} or {@link #SERVER_ERROR_GATEWAY_TIMEOUT} or {@link + * #SERVER_ERROR_SERVICE_UNAVAILABLE}. + * + * @return True if the error is recoverable. + */ + public boolean isRecoverableError() { + return isConnectorError() + || equals(Status.CLIENT_ERROR_REQUEST_TIMEOUT) + || equals(Status.SERVER_ERROR_GATEWAY_TIMEOUT) + || equals(Status.SERVER_ERROR_SERVICE_UNAVAILABLE); + } + + /** + * Indicates if the status is a redirection status, meaning "Further action must be taken in + * order to complete the request". + * + * @return True if the status is a redirection status. + */ + public boolean isRedirection() { + return isRedirection(getCode()); + } + + /** + * Indicates if the status is a server error status, meaning "The server failed to fulfill an + * apparently valid request". + * + * @return True if the status is a server error status. + */ + public boolean isServerError() { + return isServerError(getCode()); + } + + /** + * Indicates if the status is a success status, meaning "The action was successfully received, + * understood, and accepted". + * + * @return True if the status is a success status. + */ + public boolean isSuccess() { + return isSuccess(getCode()); + } + + /** + * Returns the reason phrase of the status followed by its HTTP code. + * + * @return The reason phrase of the status followed by its HTTP code. + */ + @Override + public String toString() { + return getReasonPhrase() + + " (" + + this.code + + ")" + + ((getDescription() == null) ? "" : " - " + getDescription()); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Tag.java b/org.restlet/src/main/java/org/restlet/data/Tag.java index a008d74e44..4e98bef0bd 100644 --- a/org.restlet/src/main/java/org/restlet/data/Tag.java +++ b/org.restlet/src/main/java/org/restlet/data/Tag.java @@ -1,129 +1,121 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.restlet.Context; -import org.restlet.representation.RepresentationInfo; - import java.util.Objects; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.representation.RepresentationInfo; /** - * Validation tag equivalent to an HTTP entity tag (E-Tag). "A strong entity tag - * may be shared by two entities of a resource only if they are equivalent by - * octet equality.
+ * Validation tag equivalent to an HTTP entity tag (E-Tag). A strong entity tag may be shared by two + * entities of a resource only if they are equivalent by octet equality.
*
- * A weak entity tag may be shared by two entities of a resource only if the - * entities are equivalent and could be substituted for each other with no - * significant change in semantics." - * + * A weak entity tag may be shared by two entities of a resource only if the entities are equivalent + * and could be substituted for each other with no significant change in semantics." + * * @see RepresentationInfo#getTag() - * @see HTTP - * Entity Tags - * @see HTTP - * Entity Tag Cache Validators + * @see HTTP Entity + * Tags + * @see HTTP Entity Tag + * Cache Validators * @author Jerome Louvel */ public final class Tag { - /** Tag matching any other tag, used in call's condition data. */ - public static final Tag ALL = Tag.parse("*"); - - /** - * Parses a tag formatted as defined by the HTTP standard. - * - * @param httpTag The HTTP tag string; if it starts with 'W/' the tag will be - * marked as weak and the data following the 'W/' used as the - * tag; otherwise it should be surrounded with quotes (e.g., - * "sometag"). - * @return A new tag instance. - * @see HTTP - * Entity Tags - */ - public static Tag parse(String httpTag) { - Tag result = null; - boolean weak = false; - String httpTagCopy = httpTag; - - if (httpTagCopy.startsWith("W/")) { - weak = true; - httpTagCopy = httpTagCopy.substring(2); - } - - if (httpTagCopy.startsWith("\"") && httpTagCopy.endsWith("\"")) { - result = new Tag(httpTagCopy.substring(1, httpTagCopy.length() - 1), weak); - } else if (httpTagCopy.equals("*")) { - result = new Tag("*", weak); - } else { - Context.getCurrentLogger().log(Level.WARNING, "Invalid tag format detected: " + httpTagCopy); - } - - return result; - } - - /** The name. */ - private volatile String name; - - /** The tag weakness. */ - private final boolean weak; - - /** - * Default constructor. The opaque tag is set to null and the weakness indicator - * is set to true. - */ - public Tag() { - this(null, true); - } - - /** - * Constructor of weak tags. - * - * @param opaqueTag The tag value. - */ - public Tag(String opaqueTag) { - this(opaqueTag, true); - } - - /** - * Constructor. - * - * @param opaqueTag The tag value. - * @param weak The weakness indicator. - */ - public Tag(final String opaqueTag, boolean weak) { - this.name = opaqueTag; - this.weak = weak; - } - - /** - * Indicates if both tags are equal. - * - * @param object The object to compare to. - * @return True if both tags are equal. - */ - @Override - public boolean equals(final Object object) { - return equals(object, true); - } - - /** - * Indicates if both tags are equal. - * - * @param object The object to compare to. - * @param checkWeakness The equality test takes care or not of the weakness. - * - * @return True if both tags are equal. - */ - public boolean equals(final Object object, boolean checkWeakness) { + /** Tag matching any other tag used in call's condition data. */ + public static final Tag ALL = Tag.parse("*"); + + /** + * Parses a tag formatted as defined by the HTTP standard. + * + * @param httpTag The HTTP tag string; if it starts with 'W/' the tag will be marked as weak and + * the data following the 'W/' used as the tag; otherwise it should be surrounded with + * quotes (e.g., "sometag"). + * @return A new tag instance. + * @see HTTP Entity + * Tags + */ + public static Tag parse(String httpTag) { + Tag result = null; + boolean weak = false; + String httpTagCopy = httpTag; + + if (httpTagCopy.startsWith("W/")) { + weak = true; + httpTagCopy = httpTagCopy.substring(2); + } + + if (httpTagCopy.startsWith("\"") && httpTagCopy.endsWith("\"")) { + result = new Tag(httpTagCopy.substring(1, httpTagCopy.length() - 1), weak); + } else if (httpTagCopy.equals("*")) { + result = new Tag("*", weak); + } else { + Context.getCurrentLogger() + .log(Level.WARNING, "Invalid tag format detected: " + httpTagCopy); + } + + return result; + } + + /** The name. */ + private volatile String name; + + /** The tag weakness. */ + private final boolean weak; + + /** + * Default constructor. The opaque tag is set to null, and the weakness indicator is set to + * true. + */ + public Tag() { + this(null, true); + } + + /** + * Constructor of weak tags. + * + * @param opaqueTag The tag value. + */ + public Tag(String opaqueTag) { + this(opaqueTag, true); + } + + /** + * Constructor. + * + * @param opaqueTag The tag value. + * @param weak The weakness indicator. + */ + public Tag(final String opaqueTag, boolean weak) { + this.name = opaqueTag; + this.weak = weak; + } + + /** + * Indicates if both tags are equal. + * + * @param object The object to compare to. + * @return True if both tags are equal. + */ + @Override + public boolean equals(final Object object) { + return equals(object, true); + } + + /** + * Indicates if both tags are equal. + * + * @param object The object to compare to. + * @param checkWeakness The equality test takes care or not of the weakness. + * @return True if both tags are equal. + */ + public boolean equals(final Object object, boolean checkWeakness) { if (object instanceof Tag that) { if (checkWeakness && that.isWeak() != isWeak()) { return false; @@ -135,57 +127,56 @@ public boolean equals(final Object object, boolean checkWeakness) { } } - /** - * Returns tag formatted as an HTTP tag string. - * - * @return The formatted HTTP tag string. - * @see HTTP - * Entity Tags - */ - public String format() { - if ("*".equals(getName())) { - return "*"; - } - - final StringBuilder sb = new StringBuilder(); - if (isWeak()) { - sb.append("W/"); - } - return sb.append('"').append(getName()).append('"').toString(); - } - - /** - * Returns the name, corresponding to an HTTP opaque tag value. - * - * @return The name, corresponding to an HTTP opaque tag value. - */ - public String getName() { - return this.name; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return format().hashCode(); - } - - /** - * Indicates if the tag is weak. - * - * @return True if the tag is weak, false if the tag is strong. - */ - public boolean isWeak() { - return this.weak; - } - - /** - * Returns the name. - * - * @return The name. - */ - @Override - public String toString() { - return getName(); - } + /** + * Returns tag formatted as an HTTP tag string. + * + * @return The formatted HTTP tag string. + * @see HTTP Entity + * Tags + */ + public String format() { + if ("*".equals(getName())) { + return "*"; + } + + final StringBuilder sb = new StringBuilder(); + if (isWeak()) { + sb.append("W/"); + } + return sb.append('"').append(getName()).append('"').toString(); + } + + /** + * Returns the name, corresponding to an HTTP opaque tag value. + * + * @return The name, corresponding to an HTTP opaque tag value. + */ + public String getName() { + return this.name; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return format().hashCode(); + } + + /** + * Indicates if the tag is weak. + * + * @return True if the tag is weak, false if the tag is strong. + */ + public boolean isWeak() { + return this.weak; + } + + /** + * Returns the name. + * + * @return The name. + */ + @Override + public String toString() { + return getName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/data/Warning.java b/org.restlet/src/main/java/org/restlet/data/Warning.java index ef337faee7..3b9599edda 100644 --- a/org.restlet/src/main/java/org/restlet/data/Warning.java +++ b/org.restlet/src/main/java/org/restlet/data/Warning.java @@ -1,120 +1,115 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import java.util.Date; /** - * Additional information about the status or transformation of a request or - * response. This is typically used to warn about a possible issues with caching - * operations or transformations applied to the entity body.
+ * Additional information about the status or transformation of a request or response. This is + * typically used to warn about a possible issue with caching operations or transformations applied + * to the entity body.
*
- * Note that when used with HTTP connectors, this class maps to the "Warning" - * header. - * + * Note that when used with HTTP connectors, this class maps to the "Warning" header. + * * @author Jerome Louvel */ public class Warning { - /** The agent. Typically, a caching agent. */ - private volatile String agent; - - /** The warning date. */ - private volatile Date date; - - /** The special status. */ - private volatile Status status; - - /** The warning text. */ - private volatile String text; - - /** - * Constructor. - */ - public Warning() { - this.agent = null; - this.date = null; - this.status = null; - this.text = null; - } - - /** - * Returns the agent. Typically a caching agent. - * - * @return The agent. Typically a caching agent. - */ - public String getAgent() { - return agent; - } - - /** - * Returns the warning date. - * - * @return The warning date. - */ - public Date getDate() { - return date; - } - - /** - * Returns the special status. - * - * @return The special status. - */ - public Status getStatus() { - return status; - } - - /** - * Returns the warning text. - * - * @return The warning text. - */ - public String getText() { - return text; - } - - /** - * Sets the agent. Typically a caching agent. - * - * @param agent The agent. Typically a caching agent. - */ - public void setAgent(String agent) { - this.agent = agent; - } - - /** - * Sets the warning date. - * - * @param date The warning date. - */ - public void setDate(Date date) { - this.date = date; - } - - /** - * Sets the special status. - * - * @param status The special status. - */ - public void setStatus(Status status) { - this.status = status; - } - - /** - * Sets the warning text. - * - * @param text The warning text. - */ - public void setText(String text) { - this.text = text; - } - + /** The agent. Typically, a caching agent. */ + private volatile String agent; + + /** The warning date. */ + private volatile Date date; + + /** The special status. */ + private volatile Status status; + + /** The warning text. */ + private volatile String text; + + /** Constructor. */ + public Warning() { + this.agent = null; + this.date = null; + this.status = null; + this.text = null; + } + + /** + * Returns the agent. Typically, a caching agent. + * + * @return The agent. Typically, a caching agent. + */ + public String getAgent() { + return agent; + } + + /** + * Returns the warning date. + * + * @return The warning date. + */ + public Date getDate() { + return date; + } + + /** + * Returns the special status. + * + * @return The special status. + */ + public Status getStatus() { + return status; + } + + /** + * Returns the warning text. + * + * @return The warning text. + */ + public String getText() { + return text; + } + + /** + * Sets the agent. Typically, a caching agent. + * + * @param agent The agent. Typically, a caching agent. + */ + public void setAgent(String agent) { + this.agent = agent; + } + + /** + * Sets the warning date. + * + * @param date The warning date. + */ + public void setDate(Date date) { + this.date = date; + } + + /** + * Sets the special status. + * + * @param status The special status. + */ + public void setStatus(Status status) { + this.status = status; + } + + /** + * Sets the warning text. + * + * @param text The warning text. + */ + public void setText(String text) { + this.text = text; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java index 3642e818e7..14a60d53a4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java @@ -1,258 +1,258 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine; +import java.util.logging.Level; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; import org.restlet.data.Status; import org.restlet.routing.Filter; -import java.util.logging.Level; - /** - * Chain helper serving as base class for Application and Component helpers. - * + * Chain helper serving as a base class for Application and Component helpers. + * * @author Jerome Louvel */ public abstract class CompositeHelper extends RestletHelper { - /** The first inbound filter. */ - private volatile Filter firstInboundFilter; - - /** The first outbound Filter. */ - private volatile Filter firstOutboundFilter; - - /** The next Restlet after the inbound chain. */ - private volatile Restlet inboundNext; - - /** The last inbound filter. */ - private volatile Filter lastInboundFilter; - - /** The last outbound filter. */ - private volatile Filter lastOutboundFilter; - - /** The next Restlet after the outbound chain. */ - private volatile Restlet outboundNext; - - /** - * Constructor. - * - * @param helped The helped Restlet. - */ - public CompositeHelper(T helped) { - super(helped); - this.inboundNext = null; - this.firstInboundFilter = null; - this.firstOutboundFilter = null; - this.lastInboundFilter = null; - this.lastOutboundFilter = null; - this.outboundNext = null; - } - - /** - * Adds a new inbound filter to the chain. - * - * @param filter The inbound filter to add. - */ - protected synchronized void addInboundFilter(Filter filter) { - Restlet next = getInboundNext(); - - if (getFirstInboundFilter() == null) { - setFirstInboundFilter(filter); - } else if (getLastInboundFilter() != null) { - getLastInboundFilter().setNext(filter); - } - - setLastInboundFilter(filter); - setInboundNext(next); - } - - /** - * Adds a new outbound filter to the chain. - * - * @param filter The outbound filter to add. - */ - protected synchronized void addOutboundFilter(Filter filter) { - Restlet next = getOutboundNext(); - - if (getFirstOutboundFilter() == null) { - setFirstOutboundFilter(filter); - } else if (getLastOutboundFilter() != null) { - getLastOutboundFilter().setNext(filter); - } - - setLastOutboundFilter(filter); - setOutboundNext(next); - } - - /** - * Clears the chain. Sets the first and last filters to null. - */ - public void clear() { - setFirstInboundFilter(null); - setFirstOutboundFilter(null); - setInboundNext(null); - setLastInboundFilter(null); - setLastOutboundFilter(null); - setOutboundNext(null); - } - - /** - * Returns the first inbound filter. - * - * @return The first inbound filter. - */ - public Filter getFirstInboundFilter() { - return firstInboundFilter; - } - - /** - * Returns the first outbound filter. - * - * @return The first outbound filter. - */ - public Filter getFirstOutboundFilter() { - return firstOutboundFilter; - } - - /** - * Returns the next Restlet in the inbound chain. - * - * @return The next Restlet in the inbound chain. - */ - protected synchronized Restlet getInboundNext() { - Restlet result = null; - - if (getLastInboundFilter() != null) { - result = getLastInboundFilter().getNext(); - } else { - result = this.inboundNext; - } - - return result; - } - - /** - * Returns the last inbound filter. - * - * @return the last inbound filter. - */ - protected Filter getLastInboundFilter() { - return this.lastInboundFilter; - } - - /** - * Returns the last outbound filter. - * - * @return the last outbound filter. - */ - protected Filter getLastOutboundFilter() { - return this.lastOutboundFilter; - } - - /** - * Returns the next Restlet in the outbound chain. - * - * @return The next Restlet in the outbound chain. - */ - public synchronized Restlet getOutboundNext() { - Restlet result = null; - - if (getLastOutboundFilter() != null) { - result = getLastOutboundFilter().getNext(); - } else { - result = this.outboundNext; - } - - return result; - } - - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - - if (getFirstInboundFilter() != null) { - getFirstInboundFilter().handle(request, response); - } else { - final Restlet next = this.inboundNext; - if (next != null) { - next.handle(request, response); - } else { - response.setStatus(Status.SERVER_ERROR_INTERNAL); - getHelped().getLogger().log(Level.SEVERE, "The " + getHelped().getClass().getName() - + " class has no Restlet defined to process calls. Maybe it wasn't properly started."); - } - } - } - - /** - * Sets the first inbound filter. - * - * @param firstInboundFilter The first inbound filter. - */ - protected void setFirstInboundFilter(Filter firstInboundFilter) { - this.firstInboundFilter = firstInboundFilter; - } - - /** - * Sets the first outbound filter. - * - * @param firstOutboundFilter The first outbound filter. - */ - protected void setFirstOutboundFilter(Filter firstOutboundFilter) { - this.firstOutboundFilter = firstOutboundFilter; - } - - /** - * Sets the next Restlet after the inbound chain. - * - * @param next The Restlet to process after the inbound chain. - */ - protected synchronized void setInboundNext(Restlet next) { - if (getLastInboundFilter() != null) { - getLastInboundFilter().setNext(next); - } - - this.inboundNext = next; - } - - /** - * Sets the last inbound filter. - * - * @param last The last inbound filter. - */ - protected void setLastInboundFilter(Filter last) { - this.lastInboundFilter = last; - } - - /** - * Sets the last outbound filter. - * - * @param last The last outbound filter. - */ - protected void setLastOutboundFilter(Filter last) { - this.lastOutboundFilter = last; - } - - /** - * Sets the next Restlet after the outbound chain. - * - * @param next The Restlet to process after the outbound chain. - */ - protected synchronized void setOutboundNext(Restlet next) { - if (getLastOutboundFilter() != null) { - getLastOutboundFilter().setNext(next); - } - - this.outboundNext = next; - } - + /** The first inbound filter. */ + private volatile Filter firstInboundFilter; + + /** The first outbound Filter. */ + private volatile Filter firstOutboundFilter; + + /** The next Restlet after the inbound chain. */ + private volatile Restlet inboundNext; + + /** The last inbound filter. */ + private volatile Filter lastInboundFilter; + + /** The last outbound filter. */ + private volatile Filter lastOutboundFilter; + + /** The next Restlet after the outbound chain. */ + private volatile Restlet outboundNext; + + /** + * Constructor. + * + * @param helped The helped Restlet. + */ + public CompositeHelper(T helped) { + super(helped); + this.inboundNext = null; + this.firstInboundFilter = null; + this.firstOutboundFilter = null; + this.lastInboundFilter = null; + this.lastOutboundFilter = null; + this.outboundNext = null; + } + + /** + * Adds a new inbound filter to the chain. + * + * @param filter The inbound filter to add. + */ + protected synchronized void addInboundFilter(Filter filter) { + Restlet next = getInboundNext(); + + if (getFirstInboundFilter() == null) { + setFirstInboundFilter(filter); + } else if (getLastInboundFilter() != null) { + getLastInboundFilter().setNext(filter); + } + + setLastInboundFilter(filter); + setInboundNext(next); + } + + /** + * Adds a new outbound filter to the chain. + * + * @param filter The outbound filter to add. + */ + protected synchronized void addOutboundFilter(Filter filter) { + Restlet next = getOutboundNext(); + + if (getFirstOutboundFilter() == null) { + setFirstOutboundFilter(filter); + } else if (getLastOutboundFilter() != null) { + getLastOutboundFilter().setNext(filter); + } + + setLastOutboundFilter(filter); + setOutboundNext(next); + } + + /** Clears the chain. Sets the first and last filters to null. */ + public void clear() { + setFirstInboundFilter(null); + setFirstOutboundFilter(null); + setInboundNext(null); + setLastInboundFilter(null); + setLastOutboundFilter(null); + setOutboundNext(null); + } + + /** + * Returns the first inbound filter. + * + * @return The first inbound filter. + */ + public Filter getFirstInboundFilter() { + return firstInboundFilter; + } + + /** + * Returns the first outbound filter. + * + * @return The first outbound filter. + */ + public Filter getFirstOutboundFilter() { + return firstOutboundFilter; + } + + /** + * Returns the next Restlet in the inbound chain. + * + * @return The next Restlet in the inbound chain. + */ + protected synchronized Restlet getInboundNext() { + final Restlet result; + + if (getLastInboundFilter() != null) { + result = getLastInboundFilter().getNext(); + } else { + result = this.inboundNext; + } + + return result; + } + + /** + * Returns the last inbound filter. + * + * @return the last inbound filter. + */ + protected Filter getLastInboundFilter() { + return this.lastInboundFilter; + } + + /** + * Returns the last outbound filter. + * + * @return the last outbound filter. + */ + protected Filter getLastOutboundFilter() { + return this.lastOutboundFilter; + } + + /** + * Returns the next Restlet in the outbound chain. + * + * @return The next Restlet in the outbound chain. + */ + public synchronized Restlet getOutboundNext() { + final Restlet result; + + if (getLastOutboundFilter() != null) { + result = getLastOutboundFilter().getNext(); + } else { + result = this.outboundNext; + } + + return result; + } + + @Override + public void handle(Request request, Response response) { + super.handle(request, response); + + if (getFirstInboundFilter() != null) { + getFirstInboundFilter().handle(request, response); + } else { + final Restlet next = this.inboundNext; + if (next != null) { + next.handle(request, response); + } else { + response.setStatus(Status.SERVER_ERROR_INTERNAL); + getHelped() + .getLogger() + .log( + Level.SEVERE, + "The " + + getHelped().getClass().getName() + + " class has no Restlet defined to process calls. Maybe it wasn't properly started."); + } + } + } + + /** + * Sets the first inbound filter. + * + * @param firstInboundFilter The first inbound filter. + */ + protected void setFirstInboundFilter(Filter firstInboundFilter) { + this.firstInboundFilter = firstInboundFilter; + } + + /** + * Sets the first outbound filter. + * + * @param firstOutboundFilter The first outbound filter. + */ + protected void setFirstOutboundFilter(Filter firstOutboundFilter) { + this.firstOutboundFilter = firstOutboundFilter; + } + + /** + * Sets the next Restlet after the inbound chain. + * + * @param next The Restlet to process after the inbound chain. + */ + protected synchronized void setInboundNext(Restlet next) { + if (getLastInboundFilter() != null) { + getLastInboundFilter().setNext(next); + } + + this.inboundNext = next; + } + + /** + * Sets the last inbound filter. + * + * @param last The last inbound filter. + */ + protected void setLastInboundFilter(Filter last) { + this.lastInboundFilter = last; + } + + /** + * Sets the last outbound filter. + * + * @param last The last outbound filter. + */ + protected void setLastOutboundFilter(Filter last) { + this.lastOutboundFilter = last; + } + + /** + * Sets the next Restlet after the outbound chain. + * + * @param next The Restlet to process after the outbound chain. + */ + protected synchronized void setOutboundNext(Restlet next) { + if (getLastOutboundFilter() != null) { + getLastOutboundFilter().setNext(next); + } + + this.outboundNext = next; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/Edition.java b/org.restlet/src/main/java/org/restlet/engine/Edition.java index d265706824..5efd1df29a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Edition.java +++ b/org.restlet/src/main/java/org/restlet/engine/Edition.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine; /** @@ -16,88 +15,82 @@ */ public enum Edition { - /** - * Android mobile OS, JSE. - */ - ANDROID("Android", "Android", "Android"), - JSE("Java Standard Edition", "Java SE", "JSE"); + /** Android mobile OS, JSE. */ + ANDROID("Android", "Android", "Android"), + JSE("Java Standard Edition", "Java SE", "JSE"); - /** The current engine edition. */ - public static Edition CURRENT = Edition.JSE; + /** The current engine edition. */ + public static Edition CURRENT = Edition.JSE; - private final String fullName; - private final String mediumName; - private final String shortName; + private final String fullName; + private final String mediumName; + private final String shortName; - Edition(final String fullName, final String mediumName, final String shortName) { - this.fullName = fullName; - this.mediumName = mediumName; - this.shortName = shortName; - } + Edition(final String fullName, final String mediumName, final String shortName) { + this.fullName = fullName; + this.mediumName = mediumName; + this.shortName = shortName; + } - /** - * Returns the full size name of the edition. - * - * @return The full size of the edition. - */ - public String getFullName() { - return fullName; - } + /** + * Returns the full size name of the edition. + * + * @return The full size of the edition. + */ + public String getFullName() { + return fullName; + } - /** - * Returns the medium size name of the edition. - * - * @return The medium size name of the edition. - */ - public String getMediumName() { - return mediumName; - } + /** + * Returns the medium size name of the edition. + * + * @return The medium size name of the edition. + */ + public String getMediumName() { + return mediumName; + } - /** - * Returns the short size name of the edition. - * - * @return The short size name of the edition. - */ - public String getShortName() { - return shortName; - } - - /** - * Returns true if this edition is the current one. - * - * @return True if this edition is the current one. - */ - public boolean isCurrentEdition() { - return this == CURRENT; - } - - /** - * Returns true if this edition is not the current one. - * - * @return True if this edition is not the current one. - */ - public boolean isNotCurrentEdition() { - return this != CURRENT; - } + /** + * Returns the short size name of the edition. + * + * @return The short size name of the edition. + */ + public String getShortName() { + return shortName; + } - public static boolean isCurrentEditionOneOf(Edition... editions) { - boolean result = false; + /** + * Returns true if this edition is the current one. + * + * @return True if this edition is the current one. + */ + public boolean isCurrentEdition() { + return this == CURRENT; + } - if (editions != null) { - for (int i = 0; i < editions.length && !result; i++) { - result = editions[i].isCurrentEdition(); - } - } + /** + * Returns true if this edition is not the current one. + * + * @return True if this edition is not the current one. + */ + public boolean isNotCurrentEdition() { + return this != CURRENT; + } - return result; - } + public static boolean isCurrentEditionOneOf(Edition... editions) { + boolean result = false; - /** - * Set this edition as the current one. - */ - public void setCurrentEdition() { - CURRENT = this; - } + if (editions != null) { + for (int i = 0; i < editions.length && !result; i++) { + result = editions[i].isCurrentEdition(); + } + } + return result; + } + /** Set this edition as the current one. */ + public void setCurrentEdition() { + CURRENT = this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/Engine.java b/org.restlet/src/main/java/org/restlet/engine/Engine.java index e915ca0344..3f6d77d497 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Engine.java +++ b/org.restlet/src/main/java/org/restlet/engine/Engine.java @@ -1,18 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; @@ -22,7 +22,6 @@ import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; - import org.restlet.Client; import org.restlet.Context; import org.restlet.Request; @@ -34,48 +33,43 @@ import org.restlet.engine.log.LoggerFacade; /** - * Engine supporting the Restlet API. The engine acts as a registry of various - * {@link Helper} types: {@link org.restlet.engine.security.AuthenticatorHelper} - * , {@link org.restlet.engine.connector.ClientHelper}, - * {@link org.restlet.engine.converter.ConverterHelper} and - * {@link org.restlet.engine.connector.ServerHelper} classes.
+ * Engine supporting the Restlet API. The engine acts as a registry of various {@link Helper} types: + * {@link org.restlet.engine.security.AuthenticatorHelper} , {@link + * org.restlet.engine.connector.ClientHelper}, {@link org.restlet.engine.converter.ConverterHelper} + * and {@link org.restlet.engine.connector.ServerHelper} classes.
*
- * Note that by default the JULI logging mechanism is used but it is possible to - * replace it by providing an alternate {@link LoggerFacade} implementation. For - * this, just pass a system property named - * "org.restlet.engine.loggerFacadeClass" with the qualified class name as a - * value. - * + * Note that by default the JULI logging mechanism is used but it is possible to replace it by + * providing an alternate {@link LoggerFacade} implementation. For this, just pass a system property + * named "org.restlet.engine.loggerFacadeClass" with the qualified class name as a value. + * * @author Jerome Louvel */ public class Engine { public static final String DESCRIPTOR = "META-INF/services"; - public static final String DESCRIPTOR_AUTHENTICATOR = "org.restlet.engine.security.AuthenticatorHelper"; + public static final String DESCRIPTOR_AUTHENTICATOR = + "org.restlet.engine.security.AuthenticatorHelper"; - public static final String DESCRIPTOR_AUTHENTICATOR_PATH = DESCRIPTOR + "/" - + DESCRIPTOR_AUTHENTICATOR; + public static final String DESCRIPTOR_AUTHENTICATOR_PATH = + DESCRIPTOR + "/" + DESCRIPTOR_AUTHENTICATOR; public static final String DESCRIPTOR_CLIENT = "org.restlet.engine.ClientHelper"; - public static final String DESCRIPTOR_CLIENT_PATH = DESCRIPTOR + "/" - + DESCRIPTOR_CLIENT; + public static final String DESCRIPTOR_CLIENT_PATH = DESCRIPTOR + "/" + DESCRIPTOR_CLIENT; - public static final String DESCRIPTOR_CONVERTER = "org.restlet.engine.converter.ConverterHelper"; + public static final String DESCRIPTOR_CONVERTER = + "org.restlet.engine.converter.ConverterHelper"; - public static final String DESCRIPTOR_CONVERTER_PATH = DESCRIPTOR + "/" - + DESCRIPTOR_CONVERTER; + public static final String DESCRIPTOR_CONVERTER_PATH = DESCRIPTOR + "/" + DESCRIPTOR_CONVERTER; public static final String DESCRIPTOR_PROTOCOL = "org.restlet.engine.ProtocolHelper"; - public static final String DESCRIPTOR_PROTOCOL_PATH = DESCRIPTOR + "/" - + DESCRIPTOR_PROTOCOL; + public static final String DESCRIPTOR_PROTOCOL_PATH = DESCRIPTOR + "/" + DESCRIPTOR_PROTOCOL; public static final String DESCRIPTOR_SERVER = "org.restlet.engine.ServerHelper"; - public static final String DESCRIPTOR_SERVER_PATH = DESCRIPTOR + "/" - + DESCRIPTOR_SERVER; + public static final String DESCRIPTOR_SERVER_PATH = DESCRIPTOR + "/" + DESCRIPTOR_SERVER; /** The registered engine. */ private static volatile Engine instance = null; @@ -84,7 +78,8 @@ public class Engine { private static volatile boolean logConfigured = false; /** The general log formatter. */ - private static volatile Class logFormatter = org.restlet.engine.log.SimplestFormatter.class; + private static volatile Class logFormatter = + org.restlet.engine.log.SimplestFormatter.class; /** The general log level. */ private static volatile Level logLevel = Level.INFO; @@ -102,64 +97,54 @@ public class Engine { private static volatile Level restletLogLevel; /** Complete version. */ - public static final String VERSION = MAJOR_NUMBER + '.' + MINOR_NUMBER - + RELEASE_NUMBER; + public static final String VERSION = MAJOR_NUMBER + '.' + MINOR_NUMBER + RELEASE_NUMBER; /** Complete version header. */ public static final String VERSION_HEADER = "Restlet-Framework/" + VERSION; - /** - * Clears the current Restlet Engine altogether. - */ + /** Clears the current Restlet Engine altogether. */ public static synchronized void clear() { instance = null; } /** - * Creates a new standalone thread with local Restlet thread variable - * properly set. - * + * Creates a new standalone thread with the local Restlet thread variable properly set. + * * @param runnable The runnable task to execute. - * @param name The thread name. - * @return The thread with proper variables ready to run the given runnable - * task. + * @param name The thread name. + * @return The thread with proper variables ready to run the given runnable task. */ - public static Thread createThreadWithLocalVariables(final Runnable runnable, - String name) { + public static Thread createThreadWithLocalVariables(final Runnable runnable, String name) { // Save the thread local variables - final org.restlet.Application currentApplication = org.restlet.Application - .getCurrent(); + final org.restlet.Application currentApplication = org.restlet.Application.getCurrent(); final Context currentContext = Context.getCurrent(); - final Integer currentVirtualHost = org.restlet.routing.VirtualHost - .getCurrent(); + final Integer currentVirtualHost = org.restlet.routing.VirtualHost.getCurrent(); final Response currentResponse = Response.getCurrent(); - Runnable r = new Runnable() { - - @Override - public void run() { - // Copy the thread local variables - Response.setCurrent(currentResponse); - Context.setCurrent(currentContext); - org.restlet.routing.VirtualHost.setCurrent(currentVirtualHost); - org.restlet.Application.setCurrent(currentApplication); - - try { - // Run the user task - runnable.run(); - } finally { - Engine.clearThreadLocalVariables(); - } - } + Runnable r = + new Runnable() { - }; + @Override + public void run() { + // Copy the thread local variables + Response.setCurrent(currentResponse); + Context.setCurrent(currentContext); + org.restlet.routing.VirtualHost.setCurrent(currentVirtualHost); + org.restlet.Application.setCurrent(currentApplication); + + try { + // Run the user task + runnable.run(); + } finally { + Engine.clearThreadLocalVariables(); + } + } + }; return new Thread(r, name); } - /** - * Clears the thread local variables set by the Restlet API and engine. - */ + /** Clears the thread local variables set by the Restlet API and engine. */ public static void clearThreadLocalVariables() { Response.setCurrent(null); Context.setCurrent(null); @@ -167,44 +152,41 @@ public static void clearThreadLocalVariables() { org.restlet.Application.setCurrent(null); } - /** - * Updates the global log configuration of the JVM programmatically. - */ + /** Updates the global log configuration of the JVM programmatically. */ public static void configureLog() { if ((System.getProperty("java.util.logging.config.file") == null) - && (System.getProperty( - "java.util.logging.config.class") == null)) { + && (System.getProperty("java.util.logging.config.class") == null)) { StringBuilder sb = new StringBuilder(); - sb.append("handlers=").append( - java.util.logging.ConsoleHandler.class.getCanonicalName()) + sb.append("handlers=") + .append(java.util.logging.ConsoleHandler.class.getCanonicalName()) .append('\n'); if (getLogLevel() != null) { - sb.append(".level=").append(getLogLevel().getName()) - .append('\n'); + sb.append(".level=").append(getLogLevel().getName()).append('\n'); } if (getRestletLogLevel() != null) { - sb.append("org.restlet.level=") - .append(getRestletLogLevel().getName()).append('\n'); + sb.append("org.restlet.level=").append(getRestletLogLevel().getName()).append('\n'); } if (getLogFormatter() != null) { - String handler = java.util.logging.ConsoleHandler.class - .getCanonicalName(); - sb.append(handler).append(".formatter=") + String handler = java.util.logging.ConsoleHandler.class.getCanonicalName(); + sb.append(handler) + .append(".formatter=") .append(getLogFormatter().getCanonicalName()) .append("\n"); if (getLogLevel() != null) { - sb.append(handler).append(".level=") - .append(getLogLevel().getName()).append("\n"); + sb.append(handler) + .append(".level=") + .append(getLogLevel().getName()) + .append("\n"); } } try { - LogManager.getLogManager().readConfiguration( - new ByteArrayInputStream(sb.toString().getBytes())); + LogManager.getLogManager() + .readConfiguration(new ByteArrayInputStream(sb.toString().getBytes())); } catch (Throwable t) { t.printStackTrace(); } @@ -214,9 +196,8 @@ public static void configureLog() { } /** - * Returns an anonymous logger. By default, it calls - * {@link #getLogger(String)} with a "" name. - * + * Returns an anonymous logger. By default, it calls {@link #getLogger(String)} with a "" name. + * * @return The logger. */ public static Logger getAnonymousLogger() { @@ -225,7 +206,7 @@ public static Logger getAnonymousLogger() { /** * Returns the registered Restlet engine. - * + * * @return The registered Restlet engine. */ public static synchronized Engine getInstance() { @@ -240,7 +221,7 @@ public static synchronized Engine getInstance() { /** * Returns the general log formatter. - * + * * @return The general log formatter. */ public static Class getLogFormatter() { @@ -249,7 +230,7 @@ public static Class getLogFormatter() { /** * Returns a logger based on the class name of the given object. - * + * * @param clazz The parent class. * @return The logger. */ @@ -259,33 +240,31 @@ public static Logger getLogger(Class clazz) { /** * Returns a logger based on the class name of the given object. - * - * @param clazz The parent class. - * @param defaultLoggerName The default logger name to use if no one can be - * inferred from the class. + * + * @param clazz The parent class. + * @param defaultLoggerName The default logger name to use if no one can be inferred from the + * class. * @return The logger. */ public static Logger getLogger(Class clazz, String defaultLoggerName) { - return getInstance().getLoggerFacade().getLogger(clazz, - defaultLoggerName); + return getInstance().getLoggerFacade().getLogger(clazz, defaultLoggerName); } /** * Returns a logger based on the class name of the given object. - * - * @param object The parent object. - * @param defaultLoggerName The default logger name to use if no one can be - * inferred from the object class. + * + * @param object The parent object. + * @param defaultLoggerName The default logger name to use if no one can be inferred from the + * object class. * @return The logger. */ public static Logger getLogger(Object object, String defaultLoggerName) { - return getInstance().getLoggerFacade().getLogger(object, - defaultLoggerName); + return getInstance().getLoggerFacade().getLogger(object, defaultLoggerName); } /** * Returns a logger based on the given logger name. - * + * * @param loggerName The logger name. * @return The logger. */ @@ -295,7 +274,7 @@ public static Logger getLogger(String loggerName) { /** * Returns the general log level. - * + * * @return The general log level. */ public static Level getLogLevel() { @@ -304,7 +283,7 @@ public static Level getLogLevel() { /** * Returns the classloader resource for a given name/path. - * + * * @param name The name/path to lookup. * @return The resource URL. */ @@ -313,9 +292,8 @@ public static java.net.URL getResource(String name) { } /** - * Returns the Restlet log level. For loggers with a name starting with - * "org.restlet". - * + * Returns the Restlet log level. For loggers with a name starting with "org.restlet". + * * @return The Restlet log level. */ public static Level getRestletLogLevel() { @@ -324,19 +302,18 @@ public static Level getRestletLogLevel() { /** * Returns the class object for the given name using the engine classloader. - * + * * @param className The class name to lookup. * @return The class object or null if the class was not found. * @see #getClassLoader() */ - public static Class loadClass(String className) - throws ClassNotFoundException { + public static Class loadClass(String className) throws ClassNotFoundException { return getInstance().getClassLoader().loadClass(className); } /** * Registers a new Restlet Engine. - * + * * @return The registered engine. */ public static synchronized Engine register() { @@ -345,9 +322,8 @@ public static synchronized Engine register() { /** * Registers a new Restlet Engine. - * - * @param discoverPlugins True if plug-ins should be automatically - * discovered. + * + * @param discoverPlugins True if plug-ins should be automatically discovered. * @return The registered engine. */ public static synchronized Engine register(boolean discoverPlugins) { @@ -362,18 +338,17 @@ public static synchronized Engine register(boolean discoverPlugins) { /** * Sets the general log formatter. - * + * * @param logFormatter The general log formatter. */ - public static void setLogFormatter( - Class logFormatter) { + public static void setLogFormatter(Class logFormatter) { Engine.logFormatter = logFormatter; configureLog(); } /** * Sets the general log level. Modifies the global JVM's {@link LogManager}. - * + * * @param logLevel The general log level. */ public static void setLogLevel(Level logLevel) { @@ -382,9 +357,8 @@ public static void setLogLevel(Level logLevel) { } /** - * Sets the Restlet log level. For loggers with a name starting with - * "org.restlet". - * + * Sets the Restlet log level. For loggers with a name starting with "org.restlet". + * * @param restletLogLevel The Restlet log level. */ public static void setRestletLogLevel(Level restletLogLevel) { @@ -411,23 +385,21 @@ public static void setRestletLogLevel(Level restletLogLevel) { private final List registeredProtocols; /** List of available server connectors. */ - private final List> registeredServers; + private final List> + registeredServers; /** User class loader to use for dynamic class loading. */ private volatile ClassLoader userClassLoader; - /** - * Constructor that will automatically attempt to discover connectors. - */ + /** Constructor that will automatically attempt to discover connectors. */ public Engine() { this(true); } /** * Constructor. - * - * @param discoverHelpers True if helpers should be automatically - * discovered. + * + * @param discoverHelpers True if helpers should be automatically discovered. */ public Engine(boolean discoverHelpers) { // Prevent engine initialization code from recreating other engines @@ -437,17 +409,22 @@ public Engine(boolean discoverHelpers) { this.classLoader = createClassLoader(); this.userClassLoader = null; - String loggerFacadeClass = System.getProperty( - "org.restlet.engine.loggerFacadeClass", - "org.restlet.engine.log.LoggerFacade"); + String loggerFacadeClass = + System.getProperty( + "org.restlet.engine.loggerFacadeClass", + "org.restlet.engine.log.LoggerFacade"); try { - this.loggerFacade = (LoggerFacade) getClassLoader() - .loadClass(loggerFacadeClass).getDeclaredConstructor() - .newInstance(); + this.loggerFacade = + (LoggerFacade) + getClassLoader() + .loadClass(loggerFacadeClass) + .getDeclaredConstructor() + .newInstance(); } catch (Exception e) { this.loggerFacade = new LoggerFacade(); - this.loggerFacade.getLogger("org.restlet").log(Level.WARNING, - "Unable to register the logger facade", e); + this.loggerFacade + .getLogger("org.restlet") + .log(Level.WARNING, "Unable to register the logger facade", e); } this.registeredClients = new CopyOnWriteArrayList<>(); @@ -465,17 +442,19 @@ public Engine(boolean discoverHelpers) { discoverAuthenticators(); discoverConverters(); } catch (IOException e) { - Context.getCurrentLogger().log(Level.WARNING, - "An error occurred while discovering the engine helpers.", - e); + Context.getCurrentLogger() + .log( + Level.WARNING, + "An error occurred while discovering the engine helpers.", + e); } } } /** - * Creates a new class loader. By default, it returns an instance of - * {@link org.restlet.engine.util.EngineClassLoader}. - * + * Creates a new class loader. By default, it returns an instance of {@link + * org.restlet.engine.util.EngineClassLoader}. + * * @return A new class loader. */ protected ClassLoader createClassLoader() { @@ -484,8 +463,8 @@ protected ClassLoader createClassLoader() { /** * Creates a new helper for a given client connector. - * - * @param client The client to help. + * + * @param client The client to help. * @param helperClass Optional helper class name. * @return The new helper. */ @@ -496,22 +475,26 @@ public org.restlet.engine.connector.ConnectorHelper createHelper( if (!client.getProtocols().isEmpty()) { org.restlet.engine.connector.ConnectorHelper connector = null; - for (final Iterator> iter = getRegisteredClients() - .iterator(); (result == null) && iter.hasNext();) { + for (final Iterator> iter = + getRegisteredClients().iterator(); + (result == null) && iter.hasNext(); ) { connector = iter.next(); - if (new HashSet<>(connector.getProtocols()) - .containsAll(client.getProtocols())) { - if ((helperClass == null) || connector.getClass() - .getCanonicalName().equals(helperClass)) { + if (new HashSet<>(connector.getProtocols()).containsAll(client.getProtocols())) { + if ((helperClass == null) + || connector.getClass().getCanonicalName().equals(helperClass)) { try { - result = connector.getClass() - .getConstructor(Client.class) - .newInstance(client); + result = + connector + .getClass() + .getConstructor(Client.class) + .newInstance(client); } catch (Exception e) { - Context.getCurrentLogger().log(Level.SEVERE, - "Exception during the instantiation of the client connector.", - e); + Context.getCurrentLogger() + .log( + Level.SEVERE, + "Exception during the instantiation of the client connector.", + e); } } } @@ -520,19 +503,16 @@ public org.restlet.engine.connector.ConnectorHelper createHelper( if (result == null) { // Couldn't find a matching connector StringBuilder sb = new StringBuilder(); - sb.append( - "No available client connector supports the required protocols: "); + sb.append("No available client connector supports the required protocols: "); for (Protocol p : client.getProtocols()) { sb.append("'").append(p.getName()).append("' "); } - sb.append( - ". Please add the JAR of a matching connector to your classpath."); + sb.append(". Please add the JAR of a matching connector to your classpath."); if (Edition.ANDROID.isCurrentEdition()) { - sb.append( - " Then, register this connector helper manually."); + sb.append(" Then, register this connector helper manually."); } Context.getCurrentLogger().log(Level.WARNING, sb.toString()); @@ -544,8 +524,8 @@ public org.restlet.engine.connector.ConnectorHelper createHelper( /** * Creates a new helper for a given server connector. - * - * @param server The server to help. + * + * @param server The server to help. * @param helperClass Optional helper class name. * @return The new helper. */ @@ -556,22 +536,27 @@ public org.restlet.engine.connector.ConnectorHelper createHe if (!server.getProtocols().isEmpty()) { org.restlet.engine.connector.ConnectorHelper connector = null; - for (final Iterator> iter = getRegisteredServers() - .iterator(); (result == null) && iter.hasNext();) { + for (final Iterator> + iter = getRegisteredServers().iterator(); + (result == null) && iter.hasNext(); ) { connector = iter.next(); - if ((helperClass == null) || connector.getClass() - .getCanonicalName().equals(helperClass)) { + if ((helperClass == null) + || connector.getClass().getCanonicalName().equals(helperClass)) { if (new HashSet<>(connector.getProtocols()) .containsAll(server.getProtocols())) { try { - result = connector.getClass() - .getConstructor(org.restlet.Server.class) - .newInstance(server); + result = + connector + .getClass() + .getConstructor(org.restlet.Server.class) + .newInstance(server); } catch (Exception e) { - Context.getCurrentLogger().log(Level.SEVERE, - "Exception while instantiation the server connector.", - e); + Context.getCurrentLogger() + .log( + Level.SEVERE, + "Exception while instantiation the server connector.", + e); } } } @@ -580,19 +565,16 @@ public org.restlet.engine.connector.ConnectorHelper createHe if (result == null) { // Couldn't find a matching connector final StringBuilder sb = new StringBuilder(); - sb.append( - "No available server connector supports the required protocols: "); + sb.append("No available server connector supports the required protocols: "); for (final Protocol p : server.getProtocols()) { sb.append("'").append(p.getName()).append("' "); } - sb.append( - ". Please add the JAR of a matching connector to your classpath."); + sb.append(". Please add the JAR of a matching connector to your classpath."); if (Edition.ANDROID.isCurrentEdition()) { - sb.append( - " Then, register this connector helper manually."); + sb.append(" Then, register this connector helper manually."); } Context.getCurrentLogger().log(Level.WARNING, sb.toString()); @@ -604,54 +586,48 @@ public org.restlet.engine.connector.ConnectorHelper createHe /** * Discovers the authenticator helpers and register the default helpers. - * + * * @throws IOException */ private void discoverAuthenticators() throws IOException { - registerHelpers(DESCRIPTOR_AUTHENTICATOR_PATH, - getRegisteredAuthenticators(), null); + registerHelpers(DESCRIPTOR_AUTHENTICATOR_PATH, getRegisteredAuthenticators(), null); registerDefaultAuthentications(); } /** - * Discovers the server and client connectors and register the default - * connectors. - * + * Discovers the server and client connectors and register the default connectors. + * * @throws IOException */ private void discoverConnectors() throws IOException { - registerHelpers(DESCRIPTOR_CLIENT_PATH, getRegisteredClients(), - Client.class); - registerHelpers(DESCRIPTOR_SERVER_PATH, getRegisteredServers(), - org.restlet.Server.class); + registerHelpers(DESCRIPTOR_CLIENT_PATH, getRegisteredClients(), Client.class); + registerHelpers(DESCRIPTOR_SERVER_PATH, getRegisteredServers(), org.restlet.Server.class); registerDefaultConnectors(); } /** * Discovers the converter helpers and register the default helpers. - * + * * @throws IOException */ private void discoverConverters() throws IOException { - registerHelpers(DESCRIPTOR_CONVERTER_PATH, getRegisteredConverters(), - null); + registerHelpers(DESCRIPTOR_CONVERTER_PATH, getRegisteredConverters(), null); registerDefaultConverters(); } /** * Discovers the protocol helpers and register the default helpers. - * + * * @throws IOException */ private void discoverProtocols() throws IOException { - registerHelpers(DESCRIPTOR_PROTOCOL_PATH, getRegisteredProtocols(), - null); + registerHelpers(DESCRIPTOR_PROTOCOL_PATH, getRegisteredProtocols(), null); registerDefaultProtocols(); } /** * Finds the converter helper supporting the given conversion. - * + * * @return The converter helper or null. */ public org.restlet.engine.converter.ConverterHelper findHelper() { @@ -661,17 +637,17 @@ public org.restlet.engine.converter.ConverterHelper findHelper() { /** * Finds the authenticator helper supporting the given scheme. - * + * * @param challengeScheme The challenge scheme to match. - * @param clientSide Indicates if client side support is required. - * @param serverSide Indicates if server side support is required. + * @param clientSide Indicates if client side support is required. + * @param serverSide Indicates if server side support is required. * @return The authenticator helper or null. */ public org.restlet.engine.security.AuthenticatorHelper findHelper( - ChallengeScheme challengeScheme, boolean clientSide, - boolean serverSide) { + ChallengeScheme challengeScheme, boolean clientSide, boolean serverSide) { org.restlet.engine.security.AuthenticatorHelper result = null; - List helpers = getRegisteredAuthenticators(); + List helpers = + getRegisteredAuthenticators(); org.restlet.engine.security.AuthenticatorHelper current; for (int i = 0; (result == null) && (i < helpers.size()); i++) { @@ -688,12 +664,11 @@ public org.restlet.engine.security.AuthenticatorHelper findHelper( } /** - * Returns the class loader. It uses the delegation model with the Engine - * class's class loader as a parent. If this parent doesn't find a class or - * resource, it then tries the user class loader (via - * {@link #getUserClassLoader()} and finally the - * {@link Thread#getContextClassLoader()}. - * + * Returns the class loader. It uses the delegation model with the Engine class's class loader + * as a parent. If this parent doesn't find a class or resource, it then tries the user class + * loader (via {@link #getUserClassLoader()} and finally the {@link + * Thread#getContextClassLoader()}. + * * @return The engine class loader. * @see org.restlet.engine.util.EngineClassLoader */ @@ -703,7 +678,7 @@ public ClassLoader getClassLoader() { /** * Returns the logger facade to use. - * + * * @return The logger facade to use. */ public LoggerFacade getLoggerFacade() { @@ -712,7 +687,7 @@ public LoggerFacade getLoggerFacade() { /** * Parses a line to extract the provider class name. - * + * * @param line The line to parse. * @return The provider's class name or an empty string. */ @@ -726,7 +701,7 @@ private String getProviderClassName(String line) { /** * Returns the list of available authentication helpers. - * + * * @return The list of available authentication helpers. */ public List getRegisteredAuthenticators() { @@ -735,7 +710,7 @@ public List getRegisteredAuthen /** * Returns the list of available client connectors. - * + * * @return The list of available client connectors. */ public List> getRegisteredClients() { @@ -744,7 +719,7 @@ public List> getRegisteredC /** * Returns the list of available converters. - * + * * @return The list of available converters. */ public List getRegisteredConverters() { @@ -753,7 +728,7 @@ public List getRegisteredConverter /** * Returns the list of available protocol connectors. - * + * * @return The list of available protocol connectors. */ public List getRegisteredProtocols() { @@ -762,208 +737,172 @@ public List getRegisteredProtocols( /** * Returns the list of available server connectors. - * + * * @return The list of available server connectors. */ - public List> getRegisteredServers() { + public List> + getRegisteredServers() { return this.registeredServers; } /** - * Returns the class loader specified by the user and that should be used in - * priority. - * + * Returns the class loader specified by the user and that should be used in priority. + * * @return The user class loader */ public ClassLoader getUserClassLoader() { return userClassLoader; } - /** - * Registers the default authentication helpers. - */ + /** Registers the default authentication helpers. */ public void registerDefaultAuthentications() { - getRegisteredAuthenticators() - .add(new org.restlet.engine.security.HttpBasicHelper()); + getRegisteredAuthenticators().add(new org.restlet.engine.security.HttpBasicHelper()); } - /** - * Registers the default client and server connectors. - */ + /** Registers the default client and server connectors. */ public void registerDefaultConnectors() { - getRegisteredClients() - .add(new org.restlet.engine.local.ClapClientHelper(null)); - getRegisteredClients() - .add(new org.restlet.engine.local.RiapClientHelper(null)); - getRegisteredServers() - .add(new org.restlet.engine.local.RiapServerHelper(null)); - getRegisteredServers() - .add(new org.restlet.engine.connector.HttpServerHelper(null)); - getRegisteredServers() - .add(new org.restlet.engine.connector.HttpsServerHelper(null)); - getRegisteredClients() - .add(new org.restlet.engine.local.FileClientHelper(null)); - getRegisteredClients() - .add(new org.restlet.engine.local.ZipClientHelper(null)); - getRegisteredClients() - .add(new org.restlet.engine.connector.HttpClientHelper(null)); + getRegisteredClients().add(new org.restlet.engine.local.ClapClientHelper(null)); + getRegisteredClients().add(new org.restlet.engine.local.RiapClientHelper(null)); + getRegisteredServers().add(new org.restlet.engine.local.RiapServerHelper(null)); + getRegisteredServers().add(new org.restlet.engine.connector.HttpServerHelper(null)); + getRegisteredServers().add(new org.restlet.engine.connector.HttpsServerHelper(null)); + getRegisteredClients().add(new org.restlet.engine.local.FileClientHelper(null)); + getRegisteredClients().add(new org.restlet.engine.local.ZipClientHelper(null)); + getRegisteredClients().add(new org.restlet.engine.connector.HttpClientHelper(null)); } - /** - * Registers the default converters. - */ + /** Registers the default converters. */ public void registerDefaultConverters() { - getRegisteredConverters() - .add(new org.restlet.engine.converter.DefaultConverter()); - getRegisteredConverters().add( - new org.restlet.engine.converter.StatusInfoHtmlConverter()); + getRegisteredConverters().add(new org.restlet.engine.converter.DefaultConverter()); + getRegisteredConverters().add(new org.restlet.engine.converter.StatusInfoHtmlConverter()); } - /** - * Registers the default protocols. - */ + /** Registers the default protocols. */ public void registerDefaultProtocols() { - getRegisteredProtocols() - .add(new org.restlet.engine.connector.HttpProtocolHelper()); + getRegisteredProtocols().add(new org.restlet.engine.connector.HttpProtocolHelper()); } /** * Registers a helper. - * - * @param classLoader The classloader to use. - * @param provider Bynary name of the helper's class. - * @param helpers The list of helpers to update. + * + * @param classLoader The classloader to use. + * @param provider Bynary name of the helper's class. + * @param helpers The list of helpers to update. * @param constructorClass The constructor parameter class to look for. */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void registerHelper(ClassLoader classLoader, String provider, - List helpers, Class constructorClass) { + @SuppressWarnings({"unchecked", "rawtypes"}) + public void registerHelper( + ClassLoader classLoader, String provider, List helpers, Class constructorClass) { if ((provider != null) && (!provider.isEmpty())) { // Instantiate the factory try { Class providerClass = classLoader.loadClass(provider); if (constructorClass == null) { - helpers.add(providerClass.getDeclaredConstructor() - .newInstance()); + helpers.add(providerClass.getDeclaredConstructor().newInstance()); } else { - helpers.add(providerClass.getConstructor(constructorClass) - .newInstance(constructorClass.cast(null))); + helpers.add( + providerClass + .getConstructor(constructorClass) + .newInstance(constructorClass.cast(null))); } } catch (Throwable t) { - Context.getCurrentLogger().log(Level.INFO, - "Unable to register the helper " + provider, t); + Context.getCurrentLogger() + .log(Level.INFO, "Unable to register the helper " + provider, t); } } } /** * Registers a helper. - * - * @param classLoader The classloader to use. - * @param configUrl Configuration URL to parse - * @param helpers The list of helpers to update. + * + * @param classLoader The classloader to use. + * @param configUrl Configuration URL to parse + * @param helpers The list of helpers to update. * @param constructorClass The constructor parameter class to look for. */ - public void registerHelpers(ClassLoader classLoader, java.net.URL configUrl, - List helpers, Class constructorClass) { - try { - java.io.BufferedReader reader = null; - try { - reader = new java.io.BufferedReader( - new InputStreamReader(configUrl.openStream(), "utf-8"), - IoUtils.BUFFER_SIZE); - String line = reader.readLine(); - - while (line != null) { - registerHelper(classLoader, getProviderClassName(line), - helpers, constructorClass); - line = reader.readLine(); - } - } catch (IOException e) { - Context.getCurrentLogger().log(Level.SEVERE, - "Unable to read the provider descriptor: " - + configUrl.toString()); - } finally { - if (reader != null) { - reader.close(); - } + public void registerHelpers( + ClassLoader classLoader, + java.net.URL configUrl, + List helpers, + Class constructorClass) { + + try (java.io.BufferedReader reader = + new java.io.BufferedReader( + new InputStreamReader(configUrl.openStream(), StandardCharsets.UTF_8), + IoUtils.BUFFER_SIZE)) { + String line = reader.readLine(); + + while (line != null) { + registerHelper(classLoader, getProviderClassName(line), helpers, constructorClass); + line = reader.readLine(); } - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.SEVERE, - "Exception while detecting the helpers.", ioe); + } catch (IOException e) { + Context.getCurrentLogger() + .log(Level.SEVERE, "Unable to read the provider descriptor: " + configUrl); } } /** * Registers a list of helpers. - * - * @param descriptorPath Classpath to the descriptor file. - * @param helpers The list of helpers to update. + * + * @param descriptorPath Classpath to the descriptor file. + * @param helpers The list of helpers to update. * @param constructorClass The constructor parameter class to look for. * @throws IOException */ - public void registerHelpers(String descriptorPath, List helpers, - Class constructorClass) throws IOException { + public void registerHelpers(String descriptorPath, List helpers, Class constructorClass) + throws IOException { ClassLoader classLoader = getClassLoader(); - Enumeration configUrls = classLoader - .getResources(descriptorPath); + Enumeration configUrls = classLoader.getResources(descriptorPath); if (configUrls != null) { while (configUrls.hasMoreElements()) { - registerHelpers(classLoader, configUrls.nextElement(), helpers, - constructorClass); + registerHelpers(classLoader, configUrls.nextElement(), helpers, constructorClass); } } } /** - * Registers a factory used by the URL class to create the - * {@link java.net.URLConnection} instances when the - * {@link java.net.URL#openConnection()} or - * {@link java.net.URL#openStream()} methods are invoked. - *

- * The implementation is based on the client dispatcher of the current - * context, as provided by {@link Context#getCurrent()} method. + * Registers a factory used by the URL class to create the {@link java.net.URLConnection} + * instances when the {@link java.net.URL#openConnection()} or {@link java.net.URL#openStream()} + * methods are invoked. + * + *

The implementation is based on the client dispatcher of the current context, as provided + * by {@link Context#getCurrent()} method. */ public void registerUrlFactory() { - // Set up an java.net.URLStreamHandlerFactory for - // proper creation of java.net.URL instances + // Set up an java.net.URLStreamHandlerFactory for proper creation of java.net.URL instances java.net.URL.setURLStreamHandlerFactory( new java.net.URLStreamHandlerFactory() { - public java.net.URLStreamHandler createURLStreamHandler( - String protocol) { + public java.net.URLStreamHandler createURLStreamHandler(String protocol) { return new java.net.URLStreamHandler() { @Override - protected java.net.URLConnection openConnection( - java.net.URL url) throws IOException { + protected java.net.URLConnection openConnection(java.net.URL url) + throws IOException { return new java.net.URLConnection(url) { @Override - public void connect() throws IOException { - } + public void connect() throws IOException {} @Override - public InputStream getInputStream() - throws IOException { + public InputStream getInputStream() throws IOException { InputStream result1 = null; // Retrieve the current context - final Context context = Context - .getCurrent(); + final Context context = Context.getCurrent(); if (context != null) { - Response response = context - .getClientDispatcher() - .handle(new Request( - Method.GET, - this.url.toString())); - - if (response.getStatus() - .isSuccess()) { - result1 = response.getEntity() - .getStream(); + Response response = + context.getClientDispatcher() + .handle( + new Request( + Method.GET, + this.url.toString())); + + if (response.getStatus().isSuccess()) { + result1 = response.getEntity().getStream(); } } @@ -971,16 +910,14 @@ public InputStream getInputStream() } }; } - }; } - }); } /** * Sets the engine class loader. - * + * * @param newClassLoader The new user class loader to use. */ public void setClassLoader(ClassLoader newClassLoader) { @@ -989,7 +926,7 @@ public void setClassLoader(ClassLoader newClassLoader) { /** * Sets the logger facade to use. - * + * * @param loggerFacade The logger facade to use. */ public void setLoggerFacade(LoggerFacade loggerFacade) { @@ -998,9 +935,8 @@ public void setLoggerFacade(LoggerFacade loggerFacade) { /** * Sets the list of available authentication helpers. - * - * @param registeredAuthenticators The list of available authentication - * helpers. + * + * @param registeredAuthenticators The list of available authentication helpers. */ public void setRegisteredAuthenticators( List registeredAuthenticators) { @@ -1009,8 +945,7 @@ public void setRegisteredAuthenticators( this.registeredAuthenticators.clear(); if (registeredAuthenticators != null) { - this.registeredAuthenticators - .addAll(registeredAuthenticators); + this.registeredAuthenticators.addAll(registeredAuthenticators); } } } @@ -1018,7 +953,7 @@ public void setRegisteredAuthenticators( /** * Sets the list of available client helpers. - * + * * @param registeredClients The list of available client helpers. */ public void setRegisteredClients( @@ -1036,7 +971,7 @@ public void setRegisteredClients( /** * Sets the list of available converter helpers. - * + * * @param registeredConverters The list of available converter helpers. */ public void setRegisteredConverters( @@ -1054,7 +989,7 @@ public void setRegisteredConverters( /** * Sets the list of available protocol helpers. - * + * * @param registeredProtocols The list of available protocol helpers. */ public void setRegisteredProtocols( @@ -1072,11 +1007,12 @@ public void setRegisteredProtocols( /** * Sets the list of available server helpers. - * + * * @param registeredServers The list of available server helpers. */ public void setRegisteredServers( - List> registeredServers) { + List> + registeredServers) { synchronized (this.registeredServers) { if (registeredServers != this.registeredServers) { this.registeredServers.clear(); @@ -1090,11 +1026,10 @@ public void setRegisteredServers( /** * Sets the user class loader that should be used in priority. - * + * * @param newClassLoader The new user class loader to use. */ public void setUserClassLoader(ClassLoader newClassLoader) { this.userClassLoader = newClassLoader; } - } diff --git a/org.restlet/src/main/java/org/restlet/engine/Helper.java b/org.restlet/src/main/java/org/restlet/engine/Helper.java index 8432814460..801c615bbf 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Helper.java +++ b/org.restlet/src/main/java/org/restlet/engine/Helper.java @@ -1,19 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine; /** * Abstract marker class parent of all engine helpers. - * + * * @author Jerome Louvel */ -public abstract class Helper { - -} +public abstract class Helper {} diff --git a/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java b/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java index 33b6a162c4..3024dd52b0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -17,156 +19,145 @@ import org.restlet.service.MetadataService; import org.restlet.util.Series; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - /** - * Delegate used by API classes to get support from the implementation classes. - * Note that this is an SPI class that is not intended for public usage. - * + * Delegate used by API classes to get support from the implementation classes. Note that this is an + * SPI class that is not intended for public usage. + * * @author Jerome Louvel */ public abstract class RestletHelper extends Helper { - /** - * The map of attributes exchanged between the API and the Engine via this - * helper. - */ - private final Map attributes; - - /** - * The helped Restlet. - */ - private volatile T helped; - - /** - * Constructor. - * - * @param helped The helped Restlet. - */ - public RestletHelper(T helped) { - this.attributes = new ConcurrentHashMap(); - this.helped = helped; - } - - /** - * Returns the map of attributes exchanged between the API and the Engine via - * this helper. - * - * @return The map of attributes. - */ - public Map getAttributes() { - return this.attributes; - } - - /** - * Returns the helped Restlet context. - * - * @return The helped Restlet context. - */ - public Context getContext() { - return getHelped().getContext(); - } - - /** - * Returns the helped Restlet. - * - * @return The helped Restlet. - */ - public T getHelped() { - return this.helped; - } - - /** - * Returns the helped Restlet parameters. - * - * @return The helped Restlet parameters. - */ - public Series getHelpedParameters() { - final Series result; - - if ((getHelped() != null) && (getHelped().getContext() != null)) { - result = getHelped().getContext().getParameters(); - } else { - result = new Series<>(Parameter.class); - } - - return result; - } - - /** - * Returns the helped Restlet logger. - * - * @return The helped Restlet logger. - */ - public Logger getLogger() { - if (getHelped() != null && getHelped().getContext() != null) { - return getHelped().getContext().getLogger(); - } - return Context.getCurrentLogger(); - } - - /** - * Returns the metadata service. If the parent application doesn't exist, a new - * instance is created. - * - * @return The metadata service. - */ - public MetadataService getMetadataService() { - MetadataService result = null; - - if (getHelped() != null) { - org.restlet.Application application = getHelped().getApplication(); - - if (application != null) { - result = application.getMetadataService(); - } - } - - if (result == null) { - result = new MetadataService(); - } - - return result; - } - - /** - * Handles a call. - * - * @param request The request to handle. - * @param response The response to update. - */ - public void handle(Request request, Response response) { - // Associate the response to the current thread - Response.setCurrent(response); - - // Associate the context to the current thread - if (getContext() != null) { - Context.setCurrent(getContext()); - } - } - - /** - * Sets the helped Restlet. - * - * @param helpedRestlet The helped Restlet. - */ - public void setHelped(T helpedRestlet) { - this.helped = helpedRestlet; - } - - /** Start callback. */ - public abstract void start() throws Exception; - - /** Stop callback. */ - public abstract void stop() throws Exception; - - /** - * Update callback with less impact than a {@link #stop()} followed by a - * {@link #start()}. - * - * @throws Exception - */ - public abstract void update() throws Exception; + /** The map of attributes exchanged between the API and the Engine via this helper. */ + private final Map attributes; + + /** The helped Restlet. */ + private volatile T helped; + + /** + * Constructor. + * + * @param helped The helped Restlet. + */ + public RestletHelper(T helped) { + this.attributes = new ConcurrentHashMap<>(); + this.helped = helped; + } + + /** + * Returns the map of attributes exchanged between the API and the Engine via this helper. + * + * @return The map of attributes. + */ + public Map getAttributes() { + return this.attributes; + } + + /** + * Returns the helped Restlet context. + * + * @return The helped Restlet context. + */ + public Context getContext() { + return getHelped().getContext(); + } + + /** + * Returns the helped Restlet. + * + * @return The helped Restlet. + */ + public T getHelped() { + return this.helped; + } + + /** + * Returns the helped Restlet parameters. + * + * @return The helped Restlet parameters. + */ + public Series getHelpedParameters() { + final Series result; + + if ((getHelped() != null) && (getHelped().getContext() != null)) { + result = getHelped().getContext().getParameters(); + } else { + result = new Series<>(Parameter.class); + } + + return result; + } + + /** + * Returns the helped Restlet logger. + * + * @return The helped Restlet logger. + */ + public Logger getLogger() { + if (getHelped() != null && getHelped().getContext() != null) { + return getHelped().getContext().getLogger(); + } + return Context.getCurrentLogger(); + } + + /** + * Returns the metadata service. If the parent application doesn't exist, a new instance is + * created. + * + * @return The metadata service. + */ + public MetadataService getMetadataService() { + MetadataService result = null; + + if (getHelped() != null) { + org.restlet.Application application = getHelped().getApplication(); + + if (application != null) { + result = application.getMetadataService(); + } + } + + if (result == null) { + result = new MetadataService(); + } + + return result; + } + + /** + * Handles a call. + * + * @param request The request to handle. + * @param response The response to update. + */ + public void handle(Request request, Response response) { + // Associate the response to the current thread + Response.setCurrent(response); + + // Associate the context to the current thread + if (getContext() != null) { + Context.setCurrent(getContext()); + } + } + + /** + * Sets the helped Restlet. + * + * @param helpedRestlet The helped Restlet. + */ + public void setHelped(T helpedRestlet) { + this.helped = helpedRestlet; + } + + /** Start callback. */ + public abstract void start() throws Exception; + + /** Stop callback. */ + public abstract void stop() throws Exception; + + /** + * Update callback with less impact than a {@link #stop()} followed by a {@link #start()}. + * + * @throws Exception + */ + public abstract void update() throws Exception; } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java index dbe50bdb02..4d9cc47e9c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java @@ -1,54 +1,51 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; -import org.restlet.Context; - import java.util.logging.Logger; +import org.restlet.Context; /** * Converter between high-level and low-level HTTP calls. - * + * * @author Jerome Louvel */ public class Adapter { - /** The context. */ - private volatile Context context; - - /** - * Constructor. - * - * @param context The context to use. - */ - public Adapter(Context context) { - this.context = context; - } - - /** - * Returns the context. - * - * @return The context. - */ - public Context getContext() { - return this.context; - } - - /** - * Returns the logger. - * - * @return The logger. - */ - public Logger getLogger() { - Logger result = (getContext() != null) ? getContext().getLogger() : null; - return (result != null) ? result : Context.getCurrentLogger(); - } - + /** The context. */ + private volatile Context context; + + /** + * Constructor. + * + * @param context The context to use. + */ + public Adapter(Context context) { + this.context = context; + } + + /** + * Returns the context. + * + * @return The context. + */ + public Context getContext() { + return this.context; + } + + /** + * Returns the logger. + * + * @return The logger. + */ + public Logger getLogger() { + Logger result = (getContext() != null) ? getContext().getLogger() : null; + return (result != null) ? result : Context.getCurrentLogger(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java index 26f6eeaa21..6c6ac8de33 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.data.Header; import org.restlet.data.Protocol; @@ -17,19 +19,15 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Logger; - /** * Low-level call for the HTTP connectors. - * + * * @author Jerome Louvel */ public abstract class Call { /** * Returns true if the given exception is caused by a broken connection. - * + * * @param exception The exception to inspect. * @return True if the given exception is caused by a broken connection. */ @@ -39,19 +37,21 @@ public static boolean isBroken(Throwable exception) { // detect Tomcat and Jetty exceptions if (exception instanceof IOException) { String exceptionName = exception.getClass().getName(); - result = (exceptionName.endsWith("ClientAbortException") - || exceptionName.endsWith("jetty.io.EofException")); + result = + (exceptionName.endsWith("ClientAbortException") + || exceptionName.endsWith("jetty.io.EofException")); } // check for known exception messages if (!result) { String exceptionMessage = exception.getMessage(); if (exceptionMessage != null) { - result = (exceptionMessage.indexOf("Broken pipe") != -1) - || (exceptionMessage.equals( - "An existing connection must have been closed by the remote party.") + result = + (exceptionMessage.contains("Broken pipe")) || (exceptionMessage.equals( - "An open connection has been abandonned by your network stack."))); + "An existing connection must have been closed by the remote party.") + || (exceptionMessage.equals( + "An open connection has been abandonned by your network stack."))); } } @@ -110,9 +110,7 @@ public static boolean isBroken(Throwable exception) { /** The protocol version. */ private volatile String version; - /** - * Constructor. - */ + /** Constructor. */ public Call() { this.hostDomain = null; this.hostPort = -1; @@ -135,7 +133,7 @@ public Call() { /** * Returns the client address.
* Corresponds to the IP address of the requesting client. - * + * * @return The client address. */ public String getClientAddress() { @@ -145,7 +143,7 @@ public String getClientAddress() { /** * Returns the client port.
* Corresponds to the TCP/IP port of the requesting client. - * + * * @return The client port. */ public int getClientPort() { @@ -154,7 +152,7 @@ public int getClientPort() { /** * Returns the host domain. - * + * * @return The host domain. */ public String getHostDomain() { @@ -163,7 +161,7 @@ public String getHostDomain() { /** * Returns the host port. - * + * * @return The host port. */ public int getHostPort() { @@ -172,7 +170,7 @@ public int getHostPort() { /** * Returns the logger. - * + * * @return The logger. */ public Logger getLogger() { @@ -181,7 +179,7 @@ public Logger getLogger() { /** * Returns the request method. - * + * * @return The request method. */ public String getMethod() { @@ -190,7 +188,7 @@ public String getMethod() { /** * Returns the exact protocol (HTTP or HTTPS). - * + * * @return The exact protocol (HTTP or HTTPS). */ public Protocol getProtocol() { @@ -202,7 +200,7 @@ public Protocol getProtocol() { /** * Returns the reason phrase. - * + * * @return The reason phrase. */ public String getReasonPhrase() { @@ -211,7 +209,7 @@ public String getReasonPhrase() { /** * Returns the representation wrapping the given stream. - * + * * @param stream The response input stream. * @return The wrapping representation. */ @@ -221,7 +219,7 @@ protected Representation getRepresentation(InputStream stream) { /** * Returns the modifiable list of request headers. - * + * * @return The modifiable list of request headers. */ public Series

getRequestHeaders() { @@ -229,9 +227,8 @@ public Series
getRequestHeaders() { } /** - * Returns the URI on the request line (most like a relative reference, but - * not necessarily). - * + * Returns the URI on the request line (most like a relative reference, but not necessarily). + * * @return The URI on the request line. */ public String getRequestUri() { @@ -240,7 +237,7 @@ public String getRequestUri() { /** * Returns the modifiable list of server headers. - * + * * @return The modifiable list of server headers. */ public Series
getResponseHeaders() { @@ -250,7 +247,7 @@ public Series
getResponseHeaders() { /** * Returns the response address.
* Corresponds to the IP address of the responding server. - * + * * @return The response address. */ public String getServerAddress() { @@ -259,7 +256,7 @@ public String getServerAddress() { /** * Returns the server port. - * + * * @return The server port. */ public int getServerPort() { @@ -268,7 +265,7 @@ public int getServerPort() { /** * Returns the status code. - * + * * @return The status code. * @throws IOException */ @@ -278,7 +275,7 @@ public int getStatusCode() throws IOException { /** * Returns the user principal. - * + * * @return The user principal. */ public java.security.Principal getUserPrincipal() { @@ -287,7 +284,7 @@ public java.security.Principal getUserPrincipal() { /** * Returns the protocol version used. - * + * * @return The protocol version used. */ public String getVersion() { @@ -296,14 +293,14 @@ public String getVersion() { /** * Indicates if the client wants a persistent connection. - * + * * @return True if the client wants a persistent connection. */ protected abstract boolean isClientKeepAlive(); /** * Indicates if the confidentiality of the call is ensured (ex: via SSL). - * + * * @return True if the confidentiality of the call is ensured (ex: via SSL). */ public boolean isConfidential() { @@ -312,7 +309,7 @@ public boolean isConfidential() { /** * Returns true if the given exception is caused by a broken connection. - * + * * @param exception The exception to inspect. * @return True if the given exception is caused by a broken connection. */ @@ -322,9 +319,8 @@ public boolean isConnectionBroken(Throwable exception) { /** * Indicates if both the client and the server want a persistent connection. - * - * @return True if the connection should be kept alive after the call - * processing. + * + * @return True if the connection should be kept alive after the call processing. */ protected boolean isKeepAlive() { return isClientKeepAlive() && isServerKeepAlive(); @@ -332,7 +328,7 @@ protected boolean isKeepAlive() { /** * Indicates if the request entity is chunked. - * + * * @return True if the request entity is chunked. */ protected boolean isRequestChunked() { @@ -341,7 +337,7 @@ protected boolean isRequestChunked() { /** * Indicates if the response entity is chunked. - * + * * @return True if the response entity is chunked. */ protected boolean isResponseChunked() { @@ -350,14 +346,14 @@ protected boolean isResponseChunked() { /** * Indicates if the server wants a persistent connection. - * + * * @return True if the server wants a persistent connection. */ protected abstract boolean isServerKeepAlive(); /** * Sets the client address. - * + * * @param clientAddress The client address. */ protected void setClientAddress(String clientAddress) { @@ -366,7 +362,7 @@ protected void setClientAddress(String clientAddress) { /** * Sets the client port. - * + * * @param clientPort The client port. */ protected void setClientPort(int clientPort) { @@ -375,9 +371,8 @@ protected void setClientPort(int clientPort) { /** * Indicates if the confidentiality of the call is ensured (ex: via SSL). - * - * @param confidential True if the confidentiality of the call is ensured - * (ex: via SSL). + * + * @param confidential True if the confidentiality of the call is ensured (ex: via SSL). */ protected void setConfidential(boolean confidential) { this.confidential = confidential; @@ -385,7 +380,7 @@ protected void setConfidential(boolean confidential) { /** * Sets the host domain name. - * + * * @param hostDomain The baseRef domain name. */ public void setHostDomain(String hostDomain) { @@ -394,7 +389,7 @@ public void setHostDomain(String hostDomain) { /** * Sets the host port. - * + * * @param hostPort The host port. */ public void setHostPort(int hostPort) { @@ -403,7 +398,7 @@ public void setHostPort(int hostPort) { /** * Sets the request method. - * + * * @param method The request method. */ protected void setMethod(String method) { @@ -412,7 +407,7 @@ protected void setMethod(String method) { /** * Sets the exact protocol used (HTTP or HTTPS). - * + * * @param protocol The protocol. */ public void setProtocol(Protocol protocol) { @@ -421,7 +416,7 @@ public void setProtocol(Protocol protocol) { /** * Sets the reason phrase. - * + * * @param reasonPhrase The reason phrase. */ public void setReasonPhrase(String reasonPhrase) { @@ -430,7 +425,7 @@ public void setReasonPhrase(String reasonPhrase) { /** * Sets the full request URI. - * + * * @param requestUri The full request URI. */ protected void setRequestUri(String requestUri) { @@ -444,7 +439,7 @@ protected void setRequestUri(String requestUri) { /** * Sets the response address.
* Corresponds to the IP address of the responding server. - * + * * @param responseAddress The response address. */ public void setServerAddress(String responseAddress) { @@ -453,7 +448,7 @@ public void setServerAddress(String responseAddress) { /** * Sets the server port. - * + * * @param serverPort The server port. */ public void setServerPort(int serverPort) { @@ -462,7 +457,7 @@ public void setServerPort(int serverPort) { /** * Sets the status code. - * + * * @param code The status code. */ public void setStatusCode(int code) { @@ -471,7 +466,7 @@ public void setStatusCode(int code) { /** * Sets the user principal. - * + * * @param principal The user principal. */ public void setUserPrincipal(java.security.Principal principal) { @@ -480,11 +475,10 @@ public void setUserPrincipal(java.security.Principal principal) { /** * Sets the protocol version used. - * + * * @param version The protocol version used. */ public void setVersion(String version) { this.version = version; } - } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java index bd7fa75e11..8ccd8a3a29 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import java.io.IOException; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -20,9 +21,6 @@ import org.restlet.engine.header.HeaderUtils; import org.restlet.util.Series; -import java.io.IOException; -import java.util.logging.Level; - /** * Converter of high-level uniform calls into low-level HTTP client calls. * @@ -40,70 +38,74 @@ public ClientAdapter(Context context) { } /** - * Commits the changes to a handled HTTP client call back into the original - * uniform call. The default implementation first invokes the - * "addResponseHeaders" then asks the "htppCall" to send the response back - * to the client. + * Commits the changes to a handled HTTP client call back into the original uniform call. The + * default implementation first invokes the "addResponseHeaders" then asks the "htppCall" to + * send the response back to the client. * * @param httpCall The original HTTP call. - * @param request The high-level request. + * @param request The high-level request. * @param response The high-level response. * @throws Exception */ - public void commit(final ClientCall httpCall, Request request, - Response response) throws Exception { + public void commit(final ClientCall httpCall, Request request, Response response) + throws Exception { if (httpCall != null) { // Check if the call is asynchronous if (request.isAsynchronous()) { final Uniform userCallback = request.getOnResponse(); // Send the request to the client - httpCall.sendRequest(request, response, new Uniform() { - public void handle(Request request, Response response) { - try { - updateResponse(response, - new Status(httpCall.getStatusCode(), - httpCall.getReasonPhrase()), - httpCall); - - if (userCallback != null) { - userCallback.handle(request, response); + httpCall.sendRequest( + request, + response, + new Uniform() { + public void handle(Request request, Response response) { + try { + updateResponse( + response, + new Status( + httpCall.getStatusCode(), + httpCall.getReasonPhrase()), + httpCall); + + if (userCallback != null) { + userCallback.handle(request, response); + } + } catch (Throwable t) { + getLogger() + .log( + Level.WARNING, + "Unexpected error or exception inside the user call back", + t); + } } - } catch (Throwable t) { - getLogger().log(Level.WARNING, - "Unexpected error or exception inside the user call back", - t); - } - } - }); + }); } else { - updateResponse(response, httpCall.sendRequest(request), - httpCall); + updateResponse(response, httpCall.sendRequest(request), httpCall); } } } /** - * Reads the response headers of a handled HTTP client call to update the - * original uniform call. + * Reads the response headers of a handled HTTP client call to update the original uniform call. * * @param httpCall The handled HTTP client call. * @param response The high-level response to update. */ protected void readResponseHeaders(ClientCall httpCall, Response response) { try { - Series

responseHeaders = httpCall - .getResponseHeaders(); + Series
responseHeaders = httpCall.getResponseHeaders(); // Put the response headers in the call's attributes map - response.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, - responseHeaders); + response.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, responseHeaders); HeaderUtils.copyResponseTransportHeaders(responseHeaders, response); } catch (Exception e) { - getLogger().log(Level.FINE, - "An error occurred during the processing of the HTTP response.", - e); + getLogger() + .log( + Level.FINE, + "An error occurred during the processing of the HTTP response.", + e); response.setStatus(Status.CONNECTOR_ERROR_INTERNAL, e); } } @@ -111,7 +113,7 @@ protected void readResponseHeaders(ClientCall httpCall, Response response) { /** * Converts a low-level HTTP call into a high-level uniform call. * - * @param client The HTTP client that will handle the call. + * @param client The HTTP client that will handle the call. * @param request The high-level request. * @return A new high-level uniform call. */ @@ -124,8 +126,7 @@ public ClientCall toSpecific(HttpClientHelper client, Request request) { HeaderUtils.addGeneralHeaders(request, result.getRequestHeaders()); if (request.getEntity() != null) { - HeaderUtils.addEntityHeaders(request.getEntity(), - result.getRequestHeaders()); + HeaderUtils.addEntityHeaders(request.getEntity(), result.getRequestHeaders()); } // NOTE: This must stay at the end because the AWS challenge @@ -137,16 +138,14 @@ public ClientCall toSpecific(HttpClientHelper client, Request request) { } /** - * Updates the response with information from the lower-level HTTP client - * call. + * Updates the response with information from the lower-level HTTP client call. * * @param response The response to update. - * @param status The response status to apply. + * @param status The response status to apply. * @param httpCall The source HTTP client call. * @throws IOException */ - public void updateResponse(Response response, Status status, - ClientCall httpCall) { + public void updateResponse(Response response, Status status, ClientCall httpCall) { // Send the request to the client response.setStatus(status); @@ -168,12 +167,10 @@ public void updateResponse(Response response, Status status, response.getEntity().release(); } else if (response.getStatus().equals(Status.SUCCESS_NO_CONTENT)) { response.getEntity().release(); - } else if (response.getStatus() - .equals(Status.SUCCESS_RESET_CONTENT)) { + } else if (response.getStatus().equals(Status.SUCCESS_RESET_CONTENT)) { response.getEntity().release(); response.setEntity(null); - } else if (response.getStatus() - .equals(Status.REDIRECTION_NOT_MODIFIED)) { + } else if (response.getStatus().equals(Status.REDIRECTION_NOT_MODIFIED)) { response.getEntity().release(); } else if (response.getStatus().isInformational()) { response.getEntity().release(); diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java index f75e608983..0bb02e5fa0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java @@ -1,14 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import static org.restlet.data.Encoding.IDENTITY; +import static org.restlet.data.Status.CONNECTOR_ERROR_COMMUNICATION; +import static org.restlet.data.Status.REDIRECTION_NOT_MODIFIED; +import static org.restlet.data.Status.SUCCESS_NO_CONTENT; +import static org.restlet.data.Status.SUCCESS_RESET_CONTENT; +import static org.restlet.representation.Representation.UNKNOWN_SIZE; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -21,25 +31,16 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.logging.Level; - -import static org.restlet.data.Encoding.IDENTITY; -import static org.restlet.data.Status.*; -import static org.restlet.representation.Representation.UNKNOWN_SIZE; - /** * Low-level HTTP client call. - * + * * @author Jerome Louvel */ public abstract class ClientCall extends Call { /** * Returns the local IP address or 127.0.0.1 if the resolution fails. - * + * * @return The local IP address or 127.0.0.1 if the resolution fails. */ public static String getLocalAddress() { @@ -55,13 +56,12 @@ public static String getLocalAddress() { /** * Constructor setting the request address to the local host. - * - * @param helper The parent HTTP client helper. - * @param method The method name. + * + * @param helper The parent HTTP client helper. + * @param method The method name. * @param requestUri The request URI. */ - public ClientCall(HttpClientHelper helper, String method, - String requestUri) { + public ClientCall(HttpClientHelper helper, String method, String requestUri) { this.helper = helper; setMethod(method); setRequestUri(requestUri); @@ -69,9 +69,9 @@ public ClientCall(HttpClientHelper helper, String method, } /** - * Returns the content length of the request entity if know, - * {@link Representation#UNKNOWN_SIZE} otherwise. - * + * Returns the content length of the request entity if know, {@link Representation#UNKNOWN_SIZE} + * otherwise. + * * @return The request content length. */ protected long getContentLength() { @@ -80,7 +80,7 @@ protected long getContentLength() { /** * Returns the HTTP client helper. - * + * * @return The HTTP client helper. */ public HttpClientHelper getHelper() { @@ -89,35 +89,34 @@ public HttpClientHelper getHelper() { /** * Returns the request entity stream if it exists. - * + * * @return The request entity stream if it exists. */ public abstract OutputStream getRequestEntityStream(); /** * Returns the request head stream if it exists. - * + * * @return The request head stream if it exists. */ public abstract OutputStream getRequestHeadStream(); /** - * Returns the response entity if available. Note that no metadata is - * associated by default, you have to manually set them from your headers. - * + * Returns the response entity if available. Note that no metadata is associated by default, you + * have to manually set them from your headers. + * * @param response the Response to get the entity from * @return The response entity if available. */ public Representation getResponseEntity(Response response) { Representation result = null; - long size = UNKNOWN_SIZE; + final long size; // Compute the content length Series

responseHeaders = getResponseHeaders(); - String transferEncoding = responseHeaders - .getFirstValue(HeaderConstants.HEADER_TRANSFER_ENCODING, true); - if ((transferEncoding != null) - && !IDENTITY.getName().equalsIgnoreCase(transferEncoding)) { + String transferEncoding = + responseHeaders.getFirstValue(HeaderConstants.HEADER_TRANSFER_ENCODING, true); + if ((transferEncoding != null) && !IDENTITY.getName().equalsIgnoreCase(transferEncoding)) { size = UNKNOWN_SIZE; } else { size = getContentLength(); @@ -130,8 +129,7 @@ public Representation getResponseEntity(Response response) { && !response.getStatus().equals(SUCCESS_RESET_CONTENT)) { // Make sure that an InputRepresentation will not be instantiated // while the stream is closed. - InputStream stream = getUnClosedResponseEntityStream( - getResponseEntityStream(size)); + InputStream stream = getUnClosedResponseEntityStream(getResponseEntityStream(size)); if (stream != null) { result = getRepresentation(stream); @@ -143,8 +141,9 @@ public Representation getResponseEntity(Response response) { // Informs that the size has not been specified in the header. if (size == UNKNOWN_SIZE) { - getLogger().fine( - "The length of the message body is unknown. The entity must be handled carefully and consumed entirely in order to surely release the connection."); + getLogger() + .fine( + "The length of the message body is unknown. The entity must be handled carefully and consumed entirely to surely release the connection."); } } result = HeaderUtils.extractEntityHeaders(responseHeaders, result); @@ -154,22 +153,20 @@ public Representation getResponseEntity(Response response) { /** * Returns the response entity stream if it exists. - * + * * @param size The expected entity size or -1 if unknown. * @return The response entity stream if it exists. */ public abstract InputStream getResponseEntityStream(long size); /** - * Checks if the given input stream really contains bytes to be read. If so, - * returns the inputStream otherwise returns null. - * + * Checks if the given input stream really contains bytes to be read. If so, returns the + * inputStream otherwise returns null. + * * @param inputStream the inputStream to check. - * @return null if the given inputStream does not contain any byte, an - * inputStream otherwise. + * @return null if the given inputStream does not contain any byte, an inputStream otherwise. */ - private InputStream getUnClosedResponseEntityStream( - InputStream inputStream) { + private InputStream getUnClosedResponseEntityStream(InputStream inputStream) { InputStream result = null; if (inputStream != null) { @@ -177,8 +174,7 @@ private InputStream getUnClosedResponseEntityStream( if (inputStream.available() > 0) { result = inputStream; } else { - java.io.PushbackInputStream is = new java.io.PushbackInputStream( - inputStream); + java.io.PushbackInputStream is = new java.io.PushbackInputStream(inputStream); int i = is.read(); if (i >= 0) { @@ -187,10 +183,8 @@ private InputStream getUnClosedResponseEntityStream( } } } catch (IOException ioe) { - getLogger().log(Level.FINER, "End of response entity stream.", - ioe); + getLogger().log(Level.FINER, "End of response entity stream.", ioe); } - } return result; @@ -207,21 +201,19 @@ protected boolean isServerKeepAlive() { } /** - * Sends the request to the client. Commits the request line, headers and - * optional entity and send them over the network. - * + * Sends the request to the client. Commits the request line, headers and optional entity and + * send them over the network. + * * @param request The high-level request. * @return the status of the communication */ public Status sendRequest(Request request) { Status result = null; - Representation entity = request.isEntityAvailable() - ? request.getEntity() - : null; + Representation entity = request.isEntityAvailable() ? request.getEntity() : null; // Get the connector service to callback - org.restlet.service.ConnectorService connectorService = ConnectorHelper - .getConnectorService(); + org.restlet.service.ConnectorService connectorService = + ConnectorHelper.getConnectorService(); if (connectorService != null) { connectorService.beforeSend(entity); } @@ -229,7 +221,7 @@ public Status sendRequest(Request request) { try { if (entity != null) { - // In order to workaround bug #6472250 + // To work around bug #6472250 // (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6472250), // it is very important to reuse that exact same "requestStream" // reference when manipulating the request stream, otherwise @@ -248,9 +240,12 @@ public Status sendRequest(Request request) { // any open request stream. result = new Status(getStatusCode(), getReasonPhrase()); } catch (IOException ioe) { - getHelper().getLogger().log(Level.FINE, - "An error occurred during the communication with the remote HTTP server.", - ioe); + getHelper() + .getLogger() + .log( + Level.FINE, + "An error occurred during the communication with the remote HTTP server.", + ioe); result = new Status(CONNECTOR_ERROR_COMMUNICATION, ioe); } finally { if (entity != null) { @@ -267,26 +262,27 @@ public Status sendRequest(Request request) { } /** - * Sends the request to the client. Commits the request line, headers and - * optional entity and send them over the network. - * - * @param request The high-level request. + * Sends the request to the client. Commits the request line, headers, and optional entity and + * send them over the network. + * + * @param request The high-level request. * @param response The high-level response. * @param callback The callback invoked upon request completion. */ - public void sendRequest(Request request, Response response, - org.restlet.Uniform callback) throws Exception { - Context.getCurrentLogger().warning( - "Currently callbacks are not available for this connector."); + public void sendRequest(Request request, Response response, org.restlet.Uniform callback) + throws Exception { + Context.getCurrentLogger() + .warning("Currently callbacks are not available for this connector."); } /** * Indicates if the request entity should be chunked. - * + * * @return True if the request should be chunked */ protected boolean shouldRequestBeChunked(Request request) { - return request.isEntityAvailable() && (request.getEntity() != null) + return request.isEntityAvailable() + && (request.getEntity() != null) && !request.getEntity().hasKnownSize(); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java index 2e0c78e18f..bd51e99ea0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import java.util.logging.Level; import org.restlet.Client; import org.restlet.Context; import org.restlet.Request; @@ -16,11 +16,10 @@ import org.restlet.data.Status; import org.restlet.engine.connector.ClientHelper; -import java.util.logging.Level; - /** - * Base HTTP client connector. Here is the list of parameters that are - * supported. They should be set in the Client's context before it is started: + * Base HTTP client connector. Here is the list of parameters that are supported. They should be set + * in the Client's context before it is started: + * * * * @@ -37,74 +36,79 @@ * requests and responses. * *
list of supported parameters
- * + * * @author Jerome Louvel */ public abstract class HttpClientHelper extends ClientHelper { - /** The adapter from uniform calls to HTTP calls. */ - private volatile ClientAdapter adapter; + /** The adapter from uniform calls to HTTP calls. */ + private volatile ClientAdapter adapter; - /** - * Constructor. - * - * @param client The client to help. - */ - public HttpClientHelper(Client client) { - super(client); - this.adapter = null; - } + /** + * Constructor. + * + * @param client The client to help. + */ + public HttpClientHelper(Client client) { + super(client); + this.adapter = null; + } - /** - * Creates a low-level HTTP client call from a high-level request. - * - * @param request The high-level request. - * @return A low-level HTTP client call. - */ - public abstract ClientCall create(Request request); + /** + * Creates a low-level HTTP client call from a high-level request. + * + * @param request The high-level request. + * @return A low-level HTTP client call. + */ + public abstract ClientCall create(Request request); - /** - * Returns the adapter from uniform calls to HTTP calls. - * - * @return the adapter from uniform calls to HTTP calls. - */ - public ClientAdapter getAdapter() throws Exception { - if (this.adapter == null) { - String adapterClass = getHelpedParameters().getFirstValue("adapter", - "org.restlet.engine.adapter.ClientAdapter"); - this.adapter = (ClientAdapter) Class.forName(adapterClass).getConstructor(Context.class) - .newInstance(getContext()); - } + /** + * Returns the adapter from uniform calls to HTTP calls. + * + * @return the adapter from uniform calls to HTTP calls. + */ + public ClientAdapter getAdapter() throws Exception { + if (this.adapter == null) { + String adapterClass = + getHelpedParameters() + .getFirstValue("adapter", "org.restlet.engine.adapter.ClientAdapter"); + this.adapter = + (ClientAdapter) + Class.forName(adapterClass) + .getConstructor(Context.class) + .newInstance(getContext()); + } - return this.adapter; - } + return this.adapter; + } - /** - * Returns the connection timeout. Defaults to 15000. - * - * @return The connection timeout. - */ - public int getSocketConnectTimeoutMs() { - return Integer.parseInt(getHelpedParameters().getFirstValue("socketConnectTimeoutMs", "15000")); - } + /** + * Returns the connection timeout. Defaults to 15000. + * + * @return The connection timeout. + */ + public int getSocketConnectTimeoutMs() { + return Integer.parseInt( + getHelpedParameters().getFirstValue("socketConnectTimeoutMs", "15000")); + } - @Override - public void handle(Request request, Response response) { - try { - ClientCall clientCall = getAdapter().toSpecific(this, request); - getAdapter().commit(clientCall, request, response); - } catch (Exception e) { - getLogger().log(Level.INFO, "Error while handling an HTTP client call", e); - response.setStatus(Status.CONNECTOR_ERROR_INTERNAL, e); - } - } + @Override + public void handle(Request request, Response response) { + try { + ClientCall clientCall = getAdapter().toSpecific(this, request); + getAdapter().commit(clientCall, request, response); + } catch (Exception e) { + getLogger().log(Level.INFO, "Error while handling an HTTP client call", e); + response.setStatus(Status.CONNECTOR_ERROR_INTERNAL, e); + } + } - /** - * Sets the adapter from uniform calls to HTTP calls. - * - * @param adapter The adapter to set. - */ - public void setAdapter(ClientAdapter adapter) { - this.adapter = adapter; - } + /** + * Sets the adapter from uniform calls to HTTP calls. + * + * @param adapter The adapter to set. + */ + public void setAdapter(ClientAdapter adapter) { + this.adapter = adapter; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java index 699c747d2f..e2031eb342 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java @@ -1,18 +1,49 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_MATCH; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_MODIFIED_SINCE; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_NONE_MATCH; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_RANGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_UNMODIFIED_SINCE; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; -import org.restlet.data.*; -import org.restlet.engine.header.*; +import org.restlet.data.CacheDirective; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ClientInfo; +import org.restlet.data.Conditions; +import org.restlet.data.Cookie; +import org.restlet.data.Header; +import org.restlet.data.Method; +import org.restlet.data.Range; +import org.restlet.data.RecipientInfo; +import org.restlet.data.Reference; +import org.restlet.data.Tag; +import org.restlet.data.Warning; +import org.restlet.engine.header.CacheDirectiveReader; +import org.restlet.engine.header.CookieReader; +import org.restlet.engine.header.ExpectationReader; +import org.restlet.engine.header.HeaderConstants; +import org.restlet.engine.header.HeaderReader; +import org.restlet.engine.header.PreferenceReader; +import org.restlet.engine.header.RangeReader; +import org.restlet.engine.header.RecipientInfoReader; +import org.restlet.engine.header.StringReader; +import org.restlet.engine.header.TagReader; +import org.restlet.engine.header.WarningReader; import org.restlet.engine.security.AuthenticatorUtils; import org.restlet.engine.util.DateUtils; import org.restlet.engine.util.ReferenceUtils; @@ -20,529 +51,581 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -import java.io.IOException; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; - -import static org.restlet.engine.header.HeaderConstants.*; - /** * Request wrapper for server HTTP calls. - * + * * @author Jerome Louvel */ public class HttpRequest extends Request { - /** - * Indicates if the access control data for request headers was parsed and added - */ - private volatile boolean accessControlRequestHeadersAdded; - - /** - * Indicates if the access control data for request methods was parsed and added - */ - private volatile boolean accessControlRequestMethodAdded; - - /** Indicates if the cache control data was parsed and added. */ - private volatile boolean cacheDirectivesAdded; - - /** Indicates if the client data was parsed and added. */ - private volatile boolean clientAdded; - - /** Indicates if the conditions were parsed and added. */ - private volatile boolean conditionAdded; - - /** The context of the HTTP server connector that issued the call. */ - private volatile Context context; - - /** Indicates if the cookies were parsed and added. */ - private volatile boolean cookiesAdded; - - /** Indicates if the request entity was added. */ - private volatile boolean entityAdded; - - /** The low-level HTTP call. */ - private volatile ServerCall httpCall; - - /** Indicates if the proxy security data was parsed and added. */ - private volatile boolean proxySecurityAdded; - - /** Indicates if the ranges data was parsed and added. */ - private volatile boolean rangesAdded; - - /** Indicates if the recipients info was parsed and added. */ - private volatile boolean recipientsInfoAdded; - - /** Indicates if the referrer was parsed and added. */ - private volatile boolean referrerAdded; - - /** Indicates if the security data was parsed and added. */ - private volatile boolean securityAdded; - - /** Indicates if the warning data was parsed and added. */ - private volatile boolean warningsAdded; - - /** - * Constructor. - * - * @param context The context of the HTTP server connector that issued the - * call. - * @param httpCall The low-level HTTP server call. - */ - public HttpRequest(Context context, ServerCall httpCall) { - this.context = context; - this.clientAdded = false; - this.conditionAdded = false; - this.cookiesAdded = false; - this.entityAdded = false; - this.referrerAdded = false; - this.securityAdded = false; - this.proxySecurityAdded = false; - this.recipientsInfoAdded = false; - this.warningsAdded = false; - this.httpCall = httpCall; - - // Set the properties - setMethod(Method.valueOf(httpCall.getMethod())); - - // Set the host reference - StringBuilder sb = new StringBuilder(); - sb.append(httpCall.getProtocol().getSchemeName()).append("://"); - sb.append(httpCall.getHostDomain()); - if ((httpCall.getHostPort() != -1) && (httpCall.getHostPort() != httpCall.getProtocol().getDefaultPort())) { - sb.append(':').append(httpCall.getHostPort()); - } - setHostRef(sb.toString()); - - // Set the resource reference - if (httpCall.getRequestUri() != null) { - setResourceRef(new Reference(getHostRef(), httpCall.getRequestUri())); - - if (getResourceRef().isRelative()) { - // Take care of the "/" between the host part and the segments. - if (!httpCall.getRequestUri().startsWith("/")) { - setResourceRef(new Reference(getHostRef().toString() + "/" + httpCall.getRequestUri())); - } else { - setResourceRef(new Reference(getHostRef().toString() + httpCall.getRequestUri())); - } - } - - setOriginalRef(ReferenceUtils.getOriginalRef(getResourceRef(), httpCall.getRequestHeaders())); - } - - // Set the request date - String dateHeader = httpCall.getRequestHeaders().getFirstValue(HeaderConstants.HEADER_DATE, true); - Date date = null; - if (dateHeader != null) { - date = DateUtils.parse(dateHeader); - } - - if (date == null) { - date = new Date(); - } - - setDate(date); - } - - @Override - public boolean abort() { - return getHttpCall().abort(); - } - - @Override - public void flushBuffers() throws IOException { - getHttpCall().flushBuffers(); - } - - @Override - public Set getAccessControlRequestHeaders() { - Set result = super.getAccessControlRequestHeaders(); - if (!accessControlRequestHeadersAdded) { - for (String header : getHttpCall().getRequestHeaders() - .getValuesArray(HeaderConstants.HEADER_ACCESS_CONTROL_REQUEST_HEADERS, true)) { - new StringReader(header).addValues(result); - } - accessControlRequestHeadersAdded = true; - } - return result; - } - - @Override - public Method getAccessControlRequestMethod() { - Method result = super.getAccessControlRequestMethod(); - if (!accessControlRequestMethodAdded) { - String header = getHttpCall().getRequestHeaders() - .getFirstValue(HeaderConstants.HEADER_ACCESS_CONTROL_REQUEST_METHOD, true); - if (header != null) { - result = Method.valueOf(header); - super.setAccessControlRequestMethod(result); - } - accessControlRequestMethodAdded = true; - } - return result; - } - - @Override - public List getCacheDirectives() { - List result = super.getCacheDirectives(); - - if (!cacheDirectivesAdded) { - for (Header header : getHttpCall().getRequestHeaders().subList(HeaderConstants.HEADER_CACHE_CONTROL)) { - CacheDirectiveReader.addValues(header, result); - } - - cacheDirectivesAdded = true; - } - - return result; - } - - @Override - public ChallengeResponse getChallengeResponse() { - ChallengeResponse result = super.getChallengeResponse(); - - if (!this.securityAdded) { - // Extract the header value - String authorization = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_AUTHORIZATION); - - // Set the challenge response - result = AuthenticatorUtils.parseResponse(this, authorization, getHttpCall().getRequestHeaders()); - setChallengeResponse(result); - this.securityAdded = true; - } - - return result; - } - - /** - * Returns the client-specific information. - * - * @return The client-specific information. - */ - @Override - public ClientInfo getClientInfo() { - final ClientInfo result = super.getClientInfo(); - - if (!this.clientAdded) { - // Extract the header values - String acceptMediaType = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_ACCEPT); - String acceptCharset = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_ACCEPT_CHARSET); - String acceptEncoding = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_ACCEPT_ENCODING); - String acceptLanguage = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_ACCEPT_LANGUAGE); - String acceptPatch = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_ACCEPT_PATCH); - String expect = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_EXPECT); - - // Parse the headers and update the call preferences - - // Parse the Accept* headers. If an error occurs during the parsing - // of each header, the error is traced and we keep on with the other - // headers. - try { - PreferenceReader.addCharacterSets(acceptCharset, result); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, e.getMessage()); - } - - try { - PreferenceReader.addEncodings(acceptEncoding, result); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, e.getMessage()); - } - - try { - PreferenceReader.addLanguages(acceptLanguage, result); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, e.getMessage()); - } - - try { - PreferenceReader.addMediaTypes(acceptMediaType, result); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, e.getMessage()); - } - - try { - PreferenceReader.addPatches(acceptPatch, result); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, e.getMessage()); - } - - try { - ExpectationReader.addValues(expect, result); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, e.getMessage()); - } - - // Set other properties - result.setAgent(getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_USER_AGENT)); - result.setFrom(getHttpCall().getRequestHeaders().getFirstValue(HeaderConstants.HEADER_FROM, true)); - result.setAddress(getHttpCall().getClientAddress()); - result.setPort(getHttpCall().getClientPort()); - - if (getHttpCall().getUserPrincipal() != null) { - result.getPrincipals().add(getHttpCall().getUserPrincipal()); - } - - if (this.context != null) { - // Special handling for the non standard but common - // "X-Forwarded-For" header. - final boolean useForwardedForHeader = Boolean - .parseBoolean(this.context.getParameters().getFirstValue("useForwardedForHeader", false)); - if (useForwardedForHeader) { - // Lookup the "X-Forwarded-For" header supported by popular - // proxies and caches. - final String header = getHttpCall().getRequestHeaders() - .getValues(HeaderConstants.HEADER_X_FORWARDED_FOR); - if (header != null) { - final String[] addresses = header.split(","); + /** Indicates if the access control data for request headers was parsed and added */ + private volatile boolean accessControlRequestHeadersAdded; + + /** Indicates if the access control data for request methods was parsed and added */ + private volatile boolean accessControlRequestMethodAdded; + + /** Indicates if the cache control data was parsed and added. */ + private volatile boolean cacheDirectivesAdded; + + /** Indicates if the client data was parsed and added. */ + private volatile boolean clientAdded; + + /** Indicates if the conditions were parsed and added. */ + private volatile boolean conditionAdded; + + /** The context of the HTTP server connector that issued the call. */ + private volatile Context context; + + /** Indicates if the cookies were parsed and added. */ + private volatile boolean cookiesAdded; + + /** Indicates if the request entity was added. */ + private volatile boolean entityAdded; + + /** The low-level HTTP call. */ + private volatile ServerCall httpCall; + + /** Indicates if the proxy security data was parsed and added. */ + private volatile boolean proxySecurityAdded; + + /** Indicates if the ranges data was parsed and added. */ + private volatile boolean rangesAdded; + + /** Indicates if the recipients info was parsed and added. */ + private volatile boolean recipientsInfoAdded; + + /** Indicates if the referrer was parsed and added. */ + private volatile boolean referrerAdded; + + /** Indicates if the security data was parsed and added. */ + private volatile boolean securityAdded; + + /** Indicates if the warning data was parsed and added. */ + private volatile boolean warningsAdded; + + /** + * Constructor. + * + * @param context The context of the HTTP server connector that issued the call. + * @param httpCall The low-level HTTP server call. + */ + public HttpRequest(Context context, ServerCall httpCall) { + this.context = context; + this.clientAdded = false; + this.conditionAdded = false; + this.cookiesAdded = false; + this.entityAdded = false; + this.referrerAdded = false; + this.securityAdded = false; + this.proxySecurityAdded = false; + this.recipientsInfoAdded = false; + this.warningsAdded = false; + this.httpCall = httpCall; + + // Set the properties + setMethod(Method.valueOf(httpCall.getMethod())); + + // Set the host reference + StringBuilder sb = new StringBuilder(); + sb.append(httpCall.getProtocol().getSchemeName()).append("://"); + sb.append(httpCall.getHostDomain()); + if ((httpCall.getHostPort() != -1) + && (httpCall.getHostPort() != httpCall.getProtocol().getDefaultPort())) { + sb.append(':').append(httpCall.getHostPort()); + } + setHostRef(sb.toString()); + + // Set the resource reference + if (httpCall.getRequestUri() != null) { + setResourceRef(new Reference(getHostRef(), httpCall.getRequestUri())); + + if (getResourceRef().isRelative()) { + // Take care of the "/" between the host part and the segments. + if (!httpCall.getRequestUri().startsWith("/")) { + setResourceRef( + new Reference( + getHostRef().toString() + "/" + httpCall.getRequestUri())); + } else { + setResourceRef( + new Reference(getHostRef().toString() + httpCall.getRequestUri())); + } + } + + setOriginalRef( + ReferenceUtils.getOriginalRef(getResourceRef(), httpCall.getRequestHeaders())); + } + + // Set the request date + String dateHeader = + httpCall.getRequestHeaders().getFirstValue(HeaderConstants.HEADER_DATE, true); + Date date = null; + if (dateHeader != null) { + date = DateUtils.parse(dateHeader); + } + + if (date == null) { + date = new Date(); + } + + setDate(date); + } + + @Override + public boolean abort() { + return getHttpCall().abort(); + } + + @Override + public void flushBuffers() throws IOException { + getHttpCall().flushBuffers(); + } + + @Override + public Set getAccessControlRequestHeaders() { + Set result = super.getAccessControlRequestHeaders(); + if (!accessControlRequestHeadersAdded) { + for (String header : + getHttpCall() + .getRequestHeaders() + .getValuesArray( + HeaderConstants.HEADER_ACCESS_CONTROL_REQUEST_HEADERS, true)) { + new StringReader(header).addValues(result); + } + accessControlRequestHeadersAdded = true; + } + return result; + } + + @Override + public Method getAccessControlRequestMethod() { + Method result = super.getAccessControlRequestMethod(); + if (!accessControlRequestMethodAdded) { + String header = + getHttpCall() + .getRequestHeaders() + .getFirstValue( + HeaderConstants.HEADER_ACCESS_CONTROL_REQUEST_METHOD, true); + if (header != null) { + result = Method.valueOf(header); + super.setAccessControlRequestMethod(result); + } + accessControlRequestMethodAdded = true; + } + return result; + } + + @Override + public List getCacheDirectives() { + List result = super.getCacheDirectives(); + + if (!cacheDirectivesAdded) { + for (Header header : + getHttpCall() + .getRequestHeaders() + .subList(HeaderConstants.HEADER_CACHE_CONTROL)) { + CacheDirectiveReader.addValues(header, result); + } + + cacheDirectivesAdded = true; + } + + return result; + } + + @Override + public ChallengeResponse getChallengeResponse() { + ChallengeResponse result = super.getChallengeResponse(); + + if (!this.securityAdded) { + // Extract the header value + String authorization = + getHttpCall() + .getRequestHeaders() + .getValues(HeaderConstants.HEADER_AUTHORIZATION); + + // Set the challenge response + result = + AuthenticatorUtils.parseResponse( + this, authorization, getHttpCall().getRequestHeaders()); + setChallengeResponse(result); + this.securityAdded = true; + } + + return result; + } + + /** + * Returns the client-specific information. + * + * @return The client-specific information. + */ + @Override + public ClientInfo getClientInfo() { + final ClientInfo result = super.getClientInfo(); + + if (!this.clientAdded) { + // Extract the header values + String acceptMediaType = + getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_ACCEPT); + String acceptCharset = + getHttpCall() + .getRequestHeaders() + .getValues(HeaderConstants.HEADER_ACCEPT_CHARSET); + String acceptEncoding = + getHttpCall() + .getRequestHeaders() + .getValues(HeaderConstants.HEADER_ACCEPT_ENCODING); + String acceptLanguage = + getHttpCall() + .getRequestHeaders() + .getValues(HeaderConstants.HEADER_ACCEPT_LANGUAGE); + String acceptPatch = + getHttpCall() + .getRequestHeaders() + .getValues(HeaderConstants.HEADER_ACCEPT_PATCH); + String expect = + getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_EXPECT); + + // Parse the headers and update the call preferences + + // Parse the Accept* headers. If an error occurs during the parsing + // of each header, the error is traced and we keep on with the other + // headers. + try { + PreferenceReader.addCharacterSets(acceptCharset, result); + } catch (Exception e) { + this.context.getLogger().log(Level.INFO, e.getMessage()); + } + + try { + PreferenceReader.addEncodings(acceptEncoding, result); + } catch (Exception e) { + this.context.getLogger().log(Level.INFO, e.getMessage()); + } + + try { + PreferenceReader.addLanguages(acceptLanguage, result); + } catch (Exception e) { + this.context.getLogger().log(Level.INFO, e.getMessage()); + } + + try { + PreferenceReader.addMediaTypes(acceptMediaType, result); + } catch (Exception e) { + this.context.getLogger().log(Level.INFO, e.getMessage()); + } + + try { + PreferenceReader.addPatches(acceptPatch, result); + } catch (Exception e) { + this.context.getLogger().log(Level.INFO, e.getMessage()); + } + + try { + ExpectationReader.addValues(expect, result); + } catch (Exception e) { + this.context.getLogger().log(Level.INFO, e.getMessage()); + } + + // Set other properties + result.setAgent( + getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_USER_AGENT)); + result.setFrom( + getHttpCall() + .getRequestHeaders() + .getFirstValue(HeaderConstants.HEADER_FROM, true)); + result.setAddress(getHttpCall().getClientAddress()); + result.setPort(getHttpCall().getClientPort()); + + if (getHttpCall().getUserPrincipal() != null) { + result.getPrincipals().add(getHttpCall().getUserPrincipal()); + } + + if (this.context != null) { + // Special handling for the non-standard but common + // "X-Forwarded-For" header. + final boolean useForwardedForHeader = + Boolean.parseBoolean( + this.context + .getParameters() + .getFirstValue("useForwardedForHeader", false)); + if (useForwardedForHeader) { + // Lookup the "X-Forwarded-For" header supported by popular + // proxies and caches. + final String header = + getHttpCall() + .getRequestHeaders() + .getValues(HeaderConstants.HEADER_X_FORWARDED_FOR); + if (header != null) { + final String[] addresses = header.split(","); for (String s : addresses) { String address = s.trim(); result.getForwardedAddresses().add(address); } - } - } - } - - this.clientAdded = true; - } - - return result; - } - - /** - * Returns the condition data applying to this call. - * - * @return The condition data applying to this call. - */ - @Override - public Conditions getConditions() { - final Conditions result = super.getConditions(); - - if (!this.conditionAdded) { - // Extract the header values - String ifMatchHeader = getHttpCall().getRequestHeaders().getValues(HEADER_IF_MATCH); - String ifNoneMatchHeader = getHttpCall().getRequestHeaders().getValues(HEADER_IF_NONE_MATCH); - Date ifModifiedSince = null; - Date ifUnmodifiedSince = null; - String ifRangeHeader = getHttpCall().getRequestHeaders().getFirstValue(HEADER_IF_RANGE, true); - - for (Header header : getHttpCall().getRequestHeaders()) { - if (header.getName().equalsIgnoreCase(HEADER_IF_MODIFIED_SINCE)) { - ifModifiedSince = HeaderReader.readDate(header.getValue(), false); - } else if (header.getName().equalsIgnoreCase(HEADER_IF_UNMODIFIED_SINCE)) { - ifUnmodifiedSince = HeaderReader.readDate(header.getValue(), false); - } - } - - // Set the If-Modified-Since date - if ((ifModifiedSince != null) && (ifModifiedSince.getTime() != -1)) { - result.setModifiedSince(ifModifiedSince); - } - - // Set the If-Unmodified-Since date - if ((ifUnmodifiedSince != null) && (ifUnmodifiedSince.getTime() != -1)) { - result.setUnmodifiedSince(ifUnmodifiedSince); - } - - // Set the If-Match tags - if (ifMatchHeader != null) { - try { - new TagReader(ifMatchHeader).addValues(result.getMatch()); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, "Unable to process the if-match header: " + ifMatchHeader); - } - } - - // Set the If-None-Match tags - if (ifNoneMatchHeader != null) { - try { - new TagReader(ifNoneMatchHeader).addValues(result.getNoneMatch()); - } catch (Exception e) { - this.context.getLogger().log(Level.INFO, - "Unable to process the if-none-match header: " + ifNoneMatchHeader); - } - } - - if (!StringUtils.isNullOrEmpty(ifRangeHeader)) { - Tag tag = Tag.parse(ifRangeHeader); - - if (tag != null) { - result.setRangeTag(tag); - } else { - Date date = HeaderReader.readDate(ifRangeHeader, false); - result.setRangeDate(date); - } - } - - this.conditionAdded = true; - } - - return result; - } - - /** - * Returns the cookies provided by the client. - * - * @return The cookies provided by the client. - */ - @Override - public Series getCookies() { - Series result = super.getCookies(); - - if (!this.cookiesAdded) { - String cookieValues = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_COOKIE); - - if (cookieValues != null) { - new CookieReader(cookieValues).addValues(result); - } - - this.cookiesAdded = true; - } - - return result; - } - - /** - * Returns the representation provided by the client. - * - * @return The representation provided by the client. - */ - @Override - public Representation getEntity() { - if (!this.entityAdded) { - setEntity(getHttpCall().getRequestEntity()); - this.entityAdded = true; - } - - return super.getEntity(); - } - - /** - * Returns the low-level HTTP call. - * - * @return The low-level HTTP call. - */ - public ServerCall getHttpCall() { - return this.httpCall; - } - - @Override - public ChallengeResponse getProxyChallengeResponse() { - ChallengeResponse result = super.getProxyChallengeResponse(); - - if (!this.proxySecurityAdded) { - // Extract the header value - final String authorization = getHttpCall().getRequestHeaders() - .getValues(HeaderConstants.HEADER_PROXY_AUTHORIZATION); - - // Set the challenge response - result = AuthenticatorUtils.parseResponse(this, authorization, getHttpCall().getRequestHeaders()); - setProxyChallengeResponse(result); - this.proxySecurityAdded = true; - } - - return result; - } - - @Override - public List getRanges() { - final List result = super.getRanges(); - - if (!this.rangesAdded) { - // Extract the header value - final String ranges = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_RANGE); - result.addAll(RangeReader.read(ranges)); - - this.rangesAdded = true; - } - - return result; - } - - @Override - public List getRecipientsInfo() { - List result = super.getRecipientsInfo(); - if (!recipientsInfoAdded) { - for (String header : getHttpCall().getRequestHeaders().getValuesArray(HeaderConstants.HEADER_VIA, true)) { - new RecipientInfoReader(header).addValues(result); - } - recipientsInfoAdded = true; - } - return result; - } - - /** - * Returns the referrer reference if available. - * - * @return The referrer reference. - */ - @Override - public Reference getReferrerRef() { - if (!this.referrerAdded) { - final String referrerValue = getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_REFERRER); - if (referrerValue != null) { - setReferrerRef(new Reference(referrerValue)); - } - - this.referrerAdded = true; - } - - return super.getReferrerRef(); - } - - @Override - public List getWarnings() { - List result = super.getWarnings(); - if (!warningsAdded) { - for (String header : getHttpCall().getRequestHeaders().getValuesArray(HeaderConstants.HEADER_WARNING, - true)) { - new WarningReader(header).addValues(result); - } - warningsAdded = true; - } - return result; - } - - @Override - public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { - super.setAccessControlRequestHeaders(accessControlRequestHeaders); - this.accessControlRequestHeadersAdded = true; - } - - @Override - public void setAccessControlRequestMethod(Method accessControlRequestMethod) { - super.setAccessControlRequestMethod(accessControlRequestMethod); - this.accessControlRequestMethodAdded = true; - } - - @Override - public void setChallengeResponse(ChallengeResponse response) { - super.setChallengeResponse(response); - this.securityAdded = true; - } - - @Override - public void setEntity(Representation entity) { - super.setEntity(entity); - this.entityAdded = true; - } - - @Override - public void setProxyChallengeResponse(ChallengeResponse response) { - super.setProxyChallengeResponse(response); - this.proxySecurityAdded = true; - } - - @Override - public void setRecipientsInfo(List recipientsInfo) { - super.setRecipientsInfo(recipientsInfo); - this.recipientsInfoAdded = true; - } - - @Override - public void setWarnings(List warnings) { - super.setWarnings(warnings); - this.warningsAdded = true; - } + } + } + } + + this.clientAdded = true; + } + + return result; + } + + /** + * Returns the condition data applying to this call. + * + * @return The condition data applying to this call. + */ + @Override + public Conditions getConditions() { + final Conditions result = super.getConditions(); + + if (!this.conditionAdded) { + // Extract the header values + String ifMatchHeader = getHttpCall().getRequestHeaders().getValues(HEADER_IF_MATCH); + String ifNoneMatchHeader = + getHttpCall().getRequestHeaders().getValues(HEADER_IF_NONE_MATCH); + Date ifModifiedSince = null; + Date ifUnmodifiedSince = null; + String ifRangeHeader = + getHttpCall().getRequestHeaders().getFirstValue(HEADER_IF_RANGE, true); + + for (Header header : getHttpCall().getRequestHeaders()) { + if (header.getName().equalsIgnoreCase(HEADER_IF_MODIFIED_SINCE)) { + ifModifiedSince = HeaderReader.readDate(header.getValue(), false); + } else if (header.getName().equalsIgnoreCase(HEADER_IF_UNMODIFIED_SINCE)) { + ifUnmodifiedSince = HeaderReader.readDate(header.getValue(), false); + } + } + + // Set the If-Modified-Since date + if ((ifModifiedSince != null) && (ifModifiedSince.getTime() != -1)) { + result.setModifiedSince(ifModifiedSince); + } + + // Set the If-Unmodified-Since date + if ((ifUnmodifiedSince != null) && (ifUnmodifiedSince.getTime() != -1)) { + result.setUnmodifiedSince(ifUnmodifiedSince); + } + + // Set the If-Match tags + if (ifMatchHeader != null) { + try { + new TagReader(ifMatchHeader).addValues(result.getMatch()); + } catch (Exception e) { + this.context + .getLogger() + .log( + Level.INFO, + "Unable to process the if-match header: " + ifMatchHeader); + } + } + + // Set the If-None-Match tags + if (ifNoneMatchHeader != null) { + try { + new TagReader(ifNoneMatchHeader).addValues(result.getNoneMatch()); + } catch (Exception e) { + this.context + .getLogger() + .log( + Level.INFO, + "Unable to process the if-none-match header: " + + ifNoneMatchHeader); + } + } + + if (!StringUtils.isNullOrEmpty(ifRangeHeader)) { + Tag tag = Tag.parse(ifRangeHeader); + + if (tag != null) { + result.setRangeTag(tag); + } else { + Date date = HeaderReader.readDate(ifRangeHeader, false); + result.setRangeDate(date); + } + } + + this.conditionAdded = true; + } + + return result; + } + + /** + * Returns the cookies provided by the client. + * + * @return The cookies provided by the client. + */ + @Override + public Series getCookies() { + Series result = super.getCookies(); + + if (!this.cookiesAdded) { + String cookieValues = + getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_COOKIE); + + if (cookieValues != null) { + new CookieReader(cookieValues).addValues(result); + } + + this.cookiesAdded = true; + } + + return result; + } + + /** + * Returns the representation provided by the client. + * + * @return The representation provided by the client. + */ + @Override + public Representation getEntity() { + if (!this.entityAdded) { + setEntity(getHttpCall().getRequestEntity()); + this.entityAdded = true; + } + + return super.getEntity(); + } + + /** + * Returns the low-level HTTP call. + * + * @return The low-level HTTP call. + */ + public ServerCall getHttpCall() { + return this.httpCall; + } + + @Override + public ChallengeResponse getProxyChallengeResponse() { + ChallengeResponse result = super.getProxyChallengeResponse(); + + if (!this.proxySecurityAdded) { + // Extract the header value + final String authorization = + getHttpCall() + .getRequestHeaders() + .getValues(HeaderConstants.HEADER_PROXY_AUTHORIZATION); + + // Set the challenge response + result = + AuthenticatorUtils.parseResponse( + this, authorization, getHttpCall().getRequestHeaders()); + setProxyChallengeResponse(result); + this.proxySecurityAdded = true; + } + + return result; + } + + @Override + public List getRanges() { + final List result = super.getRanges(); + + if (!this.rangesAdded) { + // Extract the header value + final String ranges = + getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_RANGE); + result.addAll(RangeReader.read(ranges)); + + this.rangesAdded = true; + } + + return result; + } + + @Override + public List getRecipientsInfo() { + List result = super.getRecipientsInfo(); + if (!recipientsInfoAdded) { + for (String header : + getHttpCall() + .getRequestHeaders() + .getValuesArray(HeaderConstants.HEADER_VIA, true)) { + new RecipientInfoReader(header).addValues(result); + } + recipientsInfoAdded = true; + } + return result; + } + + /** + * Returns the referrer reference if available. + * + * @return The referrer reference. + */ + @Override + public Reference getReferrerRef() { + if (!this.referrerAdded) { + final String referrerValue = + getHttpCall().getRequestHeaders().getValues(HeaderConstants.HEADER_REFERRER); + if (referrerValue != null) { + setReferrerRef(new Reference(referrerValue)); + } + + this.referrerAdded = true; + } + + return super.getReferrerRef(); + } + + @Override + public List getWarnings() { + List result = super.getWarnings(); + if (!warningsAdded) { + for (String header : + getHttpCall() + .getRequestHeaders() + .getValuesArray(HeaderConstants.HEADER_WARNING, true)) { + new WarningReader(header).addValues(result); + } + warningsAdded = true; + } + return result; + } + + @Override + public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { + super.setAccessControlRequestHeaders(accessControlRequestHeaders); + this.accessControlRequestHeadersAdded = true; + } + + @Override + public void setAccessControlRequestMethod(Method accessControlRequestMethod) { + super.setAccessControlRequestMethod(accessControlRequestMethod); + this.accessControlRequestMethodAdded = true; + } + + @Override + public void setChallengeResponse(ChallengeResponse response) { + super.setChallengeResponse(response); + this.securityAdded = true; + } + + @Override + public void setEntity(Representation entity) { + super.setEntity(entity); + this.entityAdded = true; + } + + @Override + public void setProxyChallengeResponse(ChallengeResponse response) { + super.setProxyChallengeResponse(response); + this.proxySecurityAdded = true; + } + + @Override + public void setRecipientsInfo(List recipientsInfo) { + super.setRecipientsInfo(recipientsInfo); + this.recipientsInfoAdded = true; + } + + @Override + public void setWarnings(List warnings) { + super.setWarnings(warnings); + this.warningsAdded = true; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java index 6f933eae40..5fb3cef68c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; import org.restlet.Request; @@ -17,70 +16,69 @@ /** * Response wrapper for server HTTP calls. - * + * * @author Jerome Louvel */ public class HttpResponse extends Response { - /** - * Adds a new header to the given request. - * - * @param response The response to update. - * @param headerName The header name to add. - * @param headerValue The header value to add. - */ - public static void addHeader(Response response, String headerName, String headerValue) { - if (response instanceof HttpResponse) { - response.getHeaders().add(headerName, headerValue); - } - } - - /** The low-level HTTP call. */ - private volatile ServerCall httpCall; + /** + * Adds a new header to the given request. + * + * @param response The response to update. + * @param headerName The header name to add. + * @param headerValue The header value to add. + */ + public static void addHeader(Response response, String headerName, String headerValue) { + if (response instanceof HttpResponse) { + response.getHeaders().add(headerName, headerValue); + } + } - /** Indicates if the server data was parsed and added. */ - private volatile boolean serverAdded; + /** The low-level HTTP call. */ + private volatile ServerCall httpCall; - /** - * Constructor. - * - * @param httpCall The low-level HTTP server call. - * @param request The associated high-level request. - */ - public HttpResponse(ServerCall httpCall, Request request) { - super(request); - this.serverAdded = false; - this.httpCall = httpCall; + /** Indicates if the server data was parsed and added. */ + private volatile boolean serverAdded; - // Set the properties - setStatus(Status.SUCCESS_OK); - } + /** + * Constructor. + * + * @param httpCall The low-level HTTP server call. + * @param request The associated high-level request. + */ + public HttpResponse(ServerCall httpCall, Request request) { + super(request); + this.serverAdded = false; + this.httpCall = httpCall; - /** - * Returns the low-level HTTP call. - * - * @return The low-level HTTP call. - */ - public ServerCall getHttpCall() { - return this.httpCall; - } + // Set the properties + setStatus(Status.SUCCESS_OK); + } - /** - * Returns the server-specific information. - * - * @return The server-specific information. - */ - @Override - public ServerInfo getServerInfo() { - final ServerInfo result = super.getServerInfo(); + /** + * Returns the low-level HTTP call. + * + * @return The low-level HTTP call. + */ + public ServerCall getHttpCall() { + return this.httpCall; + } - if (!this.serverAdded) { - result.setAddress(this.httpCall.getServerAddress()); - result.setAgent(Engine.VERSION_HEADER); - result.setPort(this.httpCall.getServerPort()); - this.serverAdded = true; - } + /** + * Returns the server-specific information. + * + * @return The server-specific information. + */ + @Override + public ServerInfo getServerInfo() { + final ServerInfo result = super.getServerInfo(); - return result; - } + if (!this.serverAdded) { + result.setAddress(this.httpCall.getServerAddress()); + result.setAgent(Engine.VERSION_HEADER); + result.setPort(this.httpCall.getServerPort()); + this.serverAdded = true; + } + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java index 70f94db627..72a509355b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java @@ -1,25 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import java.lang.reflect.InvocationTargetException; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Server; import org.restlet.engine.Engine; import org.restlet.engine.connector.ServerHelper; -import java.lang.reflect.InvocationTargetException; -import java.util.logging.Level; - /** - * Base HTTP server connector. Here is the list of parameters that are - * supported. They should be set in the Server's context before it is started: + * Base HTTP server connector. Here is the list of parameters that are supported. They should be set + * in the Server's context before it is started: + * * * * @@ -46,84 +45,89 @@ * requests and responses. * *
list of supported parameters
- * + * * @author Jerome Louvel */ public class HttpServerHelper extends ServerHelper { - /** The adapter from HTTP calls to uniform calls. */ - private volatile ServerAdapter adapter; + /** The adapter from HTTP calls to uniform calls. */ + private volatile ServerAdapter adapter; - /** - * Default constructor. Note that many methods assume that a non-null server is - * set to work properly. You can use the setHelped(Server) method for this - * purpose or better rely on the other constructor. - */ - public HttpServerHelper() { - this(null); - } + /** + * Default constructor. Note that many methods assume that a non-null server is set to work + * properly. You can use the setHelped(Server) method for this purpose or better rely on the + * other constructor. + */ + public HttpServerHelper() { + this(null); + } - /** - * Constructor. - * - * @param server The server to help. - */ - public HttpServerHelper(Server server) { - super(server); - this.adapter = null; - } + /** + * Constructor. + * + * @param server The server to help. + */ + public HttpServerHelper(Server server) { + super(server); + this.adapter = null; + } - /** - * Returns the adapter from HTTP calls to uniform calls. - * - * @return the adapter from HTTP calls to uniform calls. - */ - public ServerAdapter getAdapter() { - if (this.adapter == null) { - try { - final String adapterClass = getHelpedParameters().getFirstValue("adapter", - "org.restlet.engine.adapter.ServerAdapter"); - this.adapter = (ServerAdapter) Engine.loadClass(adapterClass).getConstructor(Context.class) - .newInstance(getContext()); - } catch (IllegalArgumentException - | SecurityException - | InstantiationException - | IllegalAccessException - | InvocationTargetException - | NoSuchMethodException - | ClassNotFoundException e) { - getLogger().log(Level.SEVERE, "Unable to create the HTTP server adapter", e); - } + /** + * Returns the adapter from HTTP calls to uniform calls. + * + * @return the adapter from HTTP calls to uniform calls. + */ + public ServerAdapter getAdapter() { + if (this.adapter == null) { + try { + final String adapterClass = + getHelpedParameters() + .getFirstValue( + "adapter", "org.restlet.engine.adapter.ServerAdapter"); + this.adapter = + (ServerAdapter) + Engine.loadClass(adapterClass) + .getConstructor(Context.class) + .newInstance(getContext()); + } catch (IllegalArgumentException + | SecurityException + | InstantiationException + | IllegalAccessException + | InvocationTargetException + | NoSuchMethodException + | ClassNotFoundException e) { + getLogger().log(Level.SEVERE, "Unable to create the HTTP server adapter", e); + } } - return this.adapter; - } + return this.adapter; + } - /** - * Handles the connector call. The default behavior is to create an REST call - * and delegate it to the attached Restlet. - * - * @param httpCall The HTTP server call. - */ - public void handle(ServerCall httpCall) { - try { - HttpRequest request = getAdapter().toRequest(httpCall); - HttpResponse response = new HttpResponse(httpCall, request); - handle(request, response); - getAdapter().commit(response); - } catch (Exception e) { - getLogger().log(Level.WARNING, "Error while handling an HTTP server call", e); - } finally { - Engine.clearThreadLocalVariables(); - } - } + /** + * Handles the connector call. The default behavior is to create an REST call and delegate it to + * the attached Restlet. + * + * @param httpCall The HTTP server call. + */ + public void handle(ServerCall httpCall) { + try { + HttpRequest request = getAdapter().toRequest(httpCall); + HttpResponse response = new HttpResponse(httpCall, request); + handle(request, response); + getAdapter().commit(response); + } catch (Exception e) { + getLogger().log(Level.WARNING, "Error while handling an HTTP server call", e); + } finally { + Engine.clearThreadLocalVariables(); + } + } - /** - * Sets the adapter from HTTP calls to uniform calls. - * - * @param adapter The converter to set. - */ - public void setAdapter(ServerAdapter adapter) { - this.adapter = adapter; - } + /** + * Sets the adapter from HTTP calls to uniform calls. + * + * @param adapter The converter to set. + */ + public void setAdapter(ServerAdapter adapter) { + this.adapter = adapter; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java index 0d563ff75c..4b5c4883e3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; import java.io.IOException; @@ -16,8 +15,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; - -import org.eclipse.jetty.client.*; +import org.eclipse.jetty.client.InputStreamRequestContent; +import org.eclipse.jetty.client.InputStreamResponseListener; +import org.eclipse.jetty.client.Request; +import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields.Mutable; @@ -32,47 +33,37 @@ /** * HTTP client connector call based on Jetty's HttpRequest class. - * + * * @author Jerome Louvel * @author Tal Liron */ public class JettyClientCall extends ClientCall { - /** - * The associated HTTP client. - */ + /** The associated HTTP client. */ private final HttpClientHelper clientHelper; - /** - * The wrapped HTTP request. - */ + /** The wrapped HTTP request. */ private final Request request; - /** - * The wrapped HTTP response. - */ + /** The wrapped HTTP response. */ private volatile Response response; - /** - * The wrapped input stream response listener. - */ + /** The wrapped input stream response listener. */ private volatile InputStreamResponseListener inputStreamResponseListener; - /** - * Indicates if the response headers were added. - */ + /** Indicates if the response headers were added. */ private volatile boolean responseHeadersAdded; /** * Constructor. - * - * @param helper The parent HTTP client helper. - * @param method The method name. + * + * @param helper The parent HTTP client helper. + * @param method The method name. * @param requestUri The request URI. * @throws IOException */ - public JettyClientCall(HttpClientHelper helper, final String method, - final String requestUri) throws IOException { + public JettyClientCall(HttpClientHelper helper, final String method, final String requestUri) + throws IOException { super(helper, method, requestUri); this.clientHelper = helper; @@ -80,17 +71,19 @@ public JettyClientCall(HttpClientHelper helper, final String method, this.request = helper.getHttpClient().newRequest(requestUri); this.request.method(method); - setConfidential(this.request.getURI().getScheme() - .equalsIgnoreCase(Protocol.HTTPS.getSchemeName())); + setConfidential( + this.request + .getURI() + .getScheme() + .equalsIgnoreCase(Protocol.HTTPS.getSchemeName())); } else { - throw new IllegalArgumentException( - "Only HTTP or HTTPS resource URIs are allowed here"); + throw new IllegalArgumentException("Only HTTP or HTTPS resource URIs are allowed here"); } } /** * Returns the HTTP request. - * + * * @return The HTTP request. */ public Request getRequest() { @@ -99,7 +92,7 @@ public Request getRequest() { /** * Returns the HTTP response. - * + * * @return The HTTP response. */ public Response getResponse() { @@ -108,7 +101,7 @@ public Response getResponse() { /** * Returns the input stream response listener. - * + * * @return The input stream response listener. */ public InputStreamResponseListener getInputStreamResponseListener() { @@ -133,27 +126,28 @@ public OutputStream getRequestHeadStream() { @Override public InputStream getResponseEntityStream(long size) { - final InputStreamResponseListener inputStreamResponseListener = getInputStreamResponseListener(); - return inputStreamResponseListener == null ? null + final InputStreamResponseListener inputStreamResponseListener = + getInputStreamResponseListener(); + return inputStreamResponseListener == null + ? null : inputStreamResponseListener.getInputStream(); } /** - * Returns the response entity if available. Note that no metadata is - * associated by default, you have to manually set them from your headers. - * - * As a jetty client decodes the input stream on the fly, we have to clear the - * {@link org.restlet.representation.Representation#getEncodings()} to avoid - * decoding the input stream another time. - * + * Returns the response entity if available. Note that no metadata is associated by default, you + * have to manually set them from your headers. + * + *

As a jetty client decodes the input stream on the fly, we have to clear the {@link + * org.restlet.representation.Representation#getEncodings()} to avoid decoding the input stream + * another time. + * * @param response the Response to get the entity from * @return The response entity if available. */ @Override public Representation getResponseEntity(org.restlet.Response response) { Representation responseEntity = super.getResponseEntity(response); - if (responseEntity != null - && !responseEntity.getEncodings().isEmpty()) { + if (responseEntity != null && !responseEntity.getEncodings().isEmpty()) { responseEntity.getEncodings().clear(); // Entity size is reset accordingly. responseEntity.setSize(Representation.UNKNOWN_SIZE); @@ -163,7 +157,7 @@ public Representation getResponseEntity(org.restlet.Response response) { /** * Returns the modifiable list of response headers. - * + * * @return The modifiable list of response headers. */ @Override @@ -189,7 +183,7 @@ public Series

getResponseHeaders() { /** * Returns the response address.
* Corresponds to the IP address of the responding server. - * + * * @return The response address. */ @Override @@ -199,7 +193,7 @@ public String getServerAddress() { /** * Returns the response status code. - * + * * @return The response status code. */ @Override @@ -208,9 +202,9 @@ public int getStatusCode() { } /** - * Sends the request to the client. Commits the request line, headers, and - * optional entity and send them over the network. - * + * Sends the request to the client. Commits the request line, headers, and optional entity and + * send them over the network. + * * @param request The high-level request. * @return The result status. */ @@ -229,49 +223,52 @@ public Status sendRequest(org.restlet.Request request) { for (Header header : getRequestHeaders()) { final String name = header.getName(); switch (name) { - case HeaderConstants.HEADER_CONTENT_LENGTH: - // skip this header - break; - case HeaderConstants.HEADER_USER_AGENT: - this.request.agent(header.getValue()); - break; - default: - ((Mutable)this.request.getHeaders()).add(name, header.getValue()); - break; + case HeaderConstants.HEADER_CONTENT_LENGTH: + // skip this header + break; + case HeaderConstants.HEADER_USER_AGENT: + this.request.agent(header.getValue()); + break; + default: + ((Mutable) this.request.getHeaders()).add(name, header.getValue()); + break; } } // Ensure that the connection is active this.inputStreamResponseListener = new InputStreamResponseListener(); this.request.send(this.inputStreamResponseListener); - this.response = this.inputStreamResponseListener - .get(clientHelper.getIdleTimeout(), TimeUnit.MILLISECONDS); + this.response = + this.inputStreamResponseListener.get( + clientHelper.getIdleTimeout(), TimeUnit.MILLISECONDS); result = new Status(getStatusCode(), getReasonPhrase()); } catch (IOException e) { - this.clientHelper.getLogger().log(Level.WARNING, - "An error occurred while reading the request entity.", e); + this.clientHelper + .getLogger() + .log(Level.WARNING, "An error occurred while reading the request entity.", e); result = new Status(Status.CONNECTOR_ERROR_INTERNAL, e); // Release the connection getRequest().abort(e); } catch (TimeoutException e) { - this.clientHelper.getLogger().log(Level.WARNING, - "The HTTP request timed out.", e); + this.clientHelper.getLogger().log(Level.WARNING, "The HTTP request timed out.", e); result = new Status(Status.CONNECTOR_ERROR_COMMUNICATION, e); // Release the connection getRequest().abort(e); } catch (InterruptedException e) { - this.clientHelper.getLogger().log(Level.WARNING, - "The HTTP request thread was interrupted.", e); + this.clientHelper + .getLogger() + .log(Level.WARNING, "The HTTP request thread was interrupted.", e); result = new Status(Status.CONNECTOR_ERROR_COMMUNICATION, e); // Release the connection getRequest().abort(e); } catch (ExecutionException e) { - this.clientHelper.getLogger().log(Level.WARNING, - "An error occurred while processing the HTTP request.", e); + this.clientHelper + .getLogger() + .log(Level.WARNING, "An error occurred while processing the HTTP request.", e); result = new Status(Status.CONNECTOR_ERROR_COMMUNICATION, e); // Release the connection @@ -282,13 +279,13 @@ public Status sendRequest(org.restlet.Request request) { } @Override - public void sendRequest(org.restlet.Request request, - org.restlet.Response response, Uniform callback) throws Exception { + public void sendRequest( + org.restlet.Request request, org.restlet.Response response, Uniform callback) + throws Exception { sendRequest(request); final Uniform getOnSent = request.getOnSent(); - if (getOnSent != null) - getOnSent.handle(request, response); + if (getOnSent != null) getOnSent.handle(request, response); if (callback != null) // Transmit to the callback, if any diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java index 7b46250f03..cd07ca4706 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; import org.eclipse.jetty.server.Handler; @@ -19,10 +18,10 @@ import org.restlet.engine.connector.JettyServerHelper; /** - * Jetty handler that knows how to convert Jetty calls into Restlet calls. This - * handler isn't a full server, if you use it, you need to manually set up the - * Jetty server connector and add this handler to a Jetty server. - * + * Jetty handler that knows how to convert Jetty calls into Restlet calls. This handler isn't a full + * server, if you use it, you need to manually set up the Jetty server connector and add this + * handler to a Jetty server. + * * @author Valdis Rigdon * @author Jerome Louvel * @author Tal Liron @@ -34,7 +33,7 @@ public class JettyHandler extends Handler.Abstract { /** * Constructor for HTTP server connectors. - * + * * @param server Restlet HTTP server connector. */ public JettyHandler(Server server) { @@ -43,7 +42,7 @@ public JettyHandler(Server server) { /** * Constructor for HTTP server connectors. - * + * * @param server Restlet server connector. * @param secure Indicates if the server supports HTTP or HTTPS. */ @@ -68,20 +67,18 @@ protected void doStop() throws Exception { } /** - * Handles a Jetty call by converting it to a Restlet call and giving it for - * processing to the Restlet server. - * - * @param request The Jetty request. + * Handles a Jetty call by converting it to a Restlet call and giving it for processing to the + * Restlet server. + * + * @param request The Jetty request. * @param response The Jetty response. * @param callback The Jetty callback. */ @Override - public boolean handle(Request request, Response response, Callback callback) - throws Exception { - JettyServerCall httpCall = new JettyServerCall(this.helper.getHelped(), - request, response, callback); + public boolean handle(Request request, Response response, Callback callback) throws Exception { + JettyServerCall httpCall = + new JettyServerCall(this.helper.getHelped(), request, response, callback); this.helper.handle(httpCall); return true; } - } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java index 851256ab1d..600389d2d6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java @@ -1,14 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.Certificate; +import java.util.Arrays; +import java.util.List; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -19,19 +24,11 @@ import org.eclipse.jetty.util.Callback; import org.restlet.Server; import org.restlet.data.Header; -import org.restlet.engine.header.HeaderConstants; import org.restlet.util.Series; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.cert.Certificate; -import java.util.Arrays; -import java.util.List; - /** * Call that is used by the Jetty HTTP server connectors. - * + * * @author Jerome Louvel * @author Tal Liron */ @@ -51,14 +48,13 @@ public class JettyServerCall extends ServerCall { /** * Constructor. - * - * @param server The parent server. + * + * @param server The parent server. * @param request The wrapped Jetty HTTP request. * @param response The wrapped Jetty HTTP response. * @param callback The wrapped Jetty HTTP callback. */ - public JettyServerCall(Server server, Request request, Response response, - Callback callback) { + public JettyServerCall(Server server, Request request, Response response, Callback callback) { super(server); this.request = request; this.response = response; @@ -84,7 +80,7 @@ public void flushBuffers() throws IOException { /** * Returns the wrapped Jetty HTTP callback. - * + * * @return The wrapped Jetty HTTP callback. */ public Callback getCallback() { @@ -102,7 +98,7 @@ public List getCertificates() { } else { result = null; } - } else { + } else { result = null; } @@ -111,7 +107,7 @@ public List getCertificates() { /** * Returns the wrapped Jetty HTTP request. - * + * * @return The wrapped Jetty HTTP request. */ public Request getRequest() { @@ -120,7 +116,7 @@ public Request getRequest() { /** * Returns the wrapped Jetty HTTP response. - * + * * @return The wrapped Jetty HTTP response. */ public Response getResponse() { @@ -148,7 +144,7 @@ public int getClientPort() { /** * Returns the underlying Jetty's connection. - * + * * @return The underlying Jetty's connection. */ protected Connection getConnection() { @@ -157,7 +153,7 @@ protected Connection getConnection() { /** * Returns the underlying Jetty's endpoint. - * + * * @return The underlying Jetty's endpoint. */ protected EndPoint getEndPoint() { @@ -245,8 +241,7 @@ public boolean isConfidential() { @Override public boolean isConnectionBroken(Throwable exception) { - return (exception instanceof EofException) - || super.isConnectionBroken(exception); + return (exception instanceof EofException) || super.isConnectionBroken(exception); } @Override diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java index 2d6174190c..4f88618dbd 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java @@ -1,14 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import static org.restlet.engine.header.HeaderConstants.ATTRIBUTE_HEADERS; +import static org.restlet.engine.header.HeaderConstants.ATTRIBUTE_HTTPS_KEY_SIZE; +import static org.restlet.engine.header.HeaderConstants.ATTRIBUTE_HTTPS_SSL_SESSION_ID; +import static org.restlet.engine.header.HeaderConstants.ATTRIBUTE_VERSION; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.List; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.data.Header; import org.restlet.data.Method; @@ -17,196 +25,209 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -import java.io.IOException; -import java.security.cert.Certificate; -import java.util.List; -import java.util.logging.Level; - -import static org.restlet.engine.header.HeaderConstants.*; - /** * Converter of low-level HTTP server calls into high-level uniform calls. - * + * * @author Jerome Louvel */ public class ServerAdapter extends Adapter { - /** - * Constructor. - * - * @param context The client context. - */ - public ServerAdapter(Context context) { - super(context); - } - - /** - * Adds the entity headers for the handled uniform call. - * - * @param response The response returned. - */ - protected void addEntityHeaders(HttpResponse response) { - Series

responseHeaders = response.getHttpCall().getResponseHeaders(); - Representation entity = response.getEntity(); - HeaderUtils.addEntityHeaders(entity, responseHeaders); - } - - /** - * Adds the response headers for the handled uniform call. - * - * @param response The response returned. - */ - protected void addResponseHeaders(HttpResponse response) { - try { - // Add all the necessary headers - HeaderUtils.addGeneralHeaders(response, response.getHttpCall().getResponseHeaders()); - HeaderUtils.addResponseHeaders(response, response.getHttpCall().getResponseHeaders()); - - // Set the status code in the response - if (response.getStatus() != null) { - response.getHttpCall().setStatusCode(response.getStatus().getCode()); - response.getHttpCall().setReasonPhrase(response.getStatus().getReasonPhrase()); - } - } catch (Exception e) { - getLogger().log(Level.WARNING, "Exception intercepted while adding the response headers", e); - response.getHttpCall().setStatusCode(Status.SERVER_ERROR_INTERNAL.getCode()); - response.getHttpCall().setReasonPhrase(Status.SERVER_ERROR_INTERNAL.getReasonPhrase()); - } - } - - /** - * Commits the changes to a handled uniform call back into the original HTTP - * call. The default implementation first invokes the "addResponseHeaders" then - * asks the "htppCall" to send the response back to the client. - * - * @param response The high-level response. - */ - public void commit(HttpResponse response) { - try { - if ((response.getRequest().getMethod() != null) && response.getRequest().getMethod().equals(Method.HEAD)) { - addEntityHeaders(response); - response.setEntity(null); - } else if (Method.GET.equals(response.getRequest().getMethod()) - && Status.SUCCESS_OK.equals(response.getStatus()) && (!response.isEntityAvailable())) { - addEntityHeaders(response); - getLogger() - .warning("A response with a 200 (Ok) status should have an entity. Make sure that resource \"" - + response.getRequest().getResourceRef() - + "\" returns one or sets the status to 204 (No content)."); - } else if (response.getStatus().equals(Status.SUCCESS_NO_CONTENT)) { - addEntityHeaders(response); - - if (response.isEntityAvailable()) { - getLogger().fine( - "Responses with a 204 (No content) status generally don't have an entity. Only adding entity headers for resource \"" - + response.getRequest().getResourceRef() + "\"."); - response.setEntity(null); - } - } else if (response.getStatus().equals(Status.SUCCESS_RESET_CONTENT)) { - if (response.isEntityAvailable()) { - getLogger().warning( - "Responses with a 205 (Reset content) status can't have an entity. Ignoring the entity for resource \"" - + response.getRequest().getResourceRef() + "\"."); - response.setEntity(null); - } - } else if (response.getStatus().equals(Status.REDIRECTION_NOT_MODIFIED)) { - if (response.getEntity() != null) { - HeaderUtils.addNotModifiedEntityHeaders(response.getEntity(), - response.getHttpCall().getResponseHeaders()); - response.setEntity(null); - } - } else if (response.getStatus().isInformational()) { - if (response.isEntityAvailable()) { - getLogger().warning( - "Responses with an informational (1xx) status can't have an entity. Ignoring the entity for resource \"" - + response.getRequest().getResourceRef() + "\"."); - response.setEntity(null); - } - } else { - addEntityHeaders(response); - - if (!response.isEntityAvailable()) { - if ((response.getEntity() != null) && (response.getEntity().getSize() != 0)) { - getLogger().warning( - "A response with an unavailable and potentially non empty entity was returned. Ignoring the entity for resource \"" - + response.getRequest().getResourceRef() + "\"."); - } - - response.setEntity(null); - } - } - - // Add the response headers - addResponseHeaders(response); - - // Send the response to the client - response.getHttpCall().sendResponse(response); - } catch (Throwable t) { - if (response.getHttpCall().isConnectionBroken(t)) { - // output a single log line for this common case to avoid filling server logs - getLogger().log(Level.INFO, - "The connection was broken. It was probably closed by the client. Reason: " + t.getMessage()); - } else { - getLogger().log(Level.SEVERE, "An exception occurred writing the response entity", t); - response.getHttpCall().setStatusCode(Status.SERVER_ERROR_INTERNAL.getCode()); - response.getHttpCall().setReasonPhrase("An exception occurred writing the response entity"); - response.setEntity(null); - - try { - response.getHttpCall().sendResponse(response); - } catch (IOException ioe) { - getLogger().log(Level.WARNING, "Unable to send error response", ioe); - } - } - } finally { - response.getHttpCall().complete(); - - if (response.getOnSent() != null) { - response.getOnSent().handle(response.getRequest(), response); - } - } - } - - /** - * Converts a low-level HTTP call into a high-level uniform request. - * - * @param httpCall The low-level HTTP call. - * @return A new high-level uniform request. - */ - public HttpRequest toRequest(ServerCall httpCall) { - HttpRequest result = new HttpRequest(getContext(), httpCall); - result.getAttributes().put(ATTRIBUTE_HEADERS, httpCall.getRequestHeaders()); - - if (httpCall.getVersion() != null) { - result.getAttributes().put(ATTRIBUTE_VERSION, httpCall.getVersion()); - } - - if (httpCall.isConfidential()) { - List clientCertificates = httpCall.getCertificates(); - - if (clientCertificates != null) { - result.getClientInfo().setCertificates(clientCertificates); - } - - String cipherSuite = httpCall.getCipherSuite(); - - if (cipherSuite != null) { - result.getClientInfo().setCipherSuite(cipherSuite); - } - - Integer keySize = httpCall.getSslKeySize(); - - if (keySize != null) { - result.getAttributes().put(ATTRIBUTE_HTTPS_KEY_SIZE, keySize); - } - - String sslSessionId = httpCall.getSslSessionId(); - - if (sslSessionId != null) { - result.getAttributes().put(ATTRIBUTE_HTTPS_SSL_SESSION_ID, sslSessionId); - } - } - - return result; - } + /** + * Constructor. + * + * @param context The client context. + */ + public ServerAdapter(Context context) { + super(context); + } + + /** + * Adds the entity headers for the handled uniform call. + * + * @param response The response returned. + */ + protected void addEntityHeaders(HttpResponse response) { + Series
responseHeaders = response.getHttpCall().getResponseHeaders(); + Representation entity = response.getEntity(); + HeaderUtils.addEntityHeaders(entity, responseHeaders); + } + + /** + * Adds the response headers for the handled uniform call. + * + * @param response The response returned. + */ + protected void addResponseHeaders(HttpResponse response) { + try { + // Add all the necessary headers + HeaderUtils.addGeneralHeaders(response, response.getHttpCall().getResponseHeaders()); + HeaderUtils.addResponseHeaders(response, response.getHttpCall().getResponseHeaders()); + + // Set the status code in the response + if (response.getStatus() != null) { + response.getHttpCall().setStatusCode(response.getStatus().getCode()); + response.getHttpCall().setReasonPhrase(response.getStatus().getReasonPhrase()); + } + } catch (Exception e) { + getLogger() + .log( + Level.WARNING, + "Exception intercepted while adding the response headers", + e); + response.getHttpCall().setStatusCode(Status.SERVER_ERROR_INTERNAL.getCode()); + response.getHttpCall().setReasonPhrase(Status.SERVER_ERROR_INTERNAL.getReasonPhrase()); + } + } + + /** + * Commits the changes to a handled uniform call back into the original HTTP call. The default + * implementation first invokes the "addResponseHeaders" then asks the "htppCall" to send the + * response back to the client. + * + * @param response The high-level response. + */ + public void commit(HttpResponse response) { + try { + if ((response.getRequest().getMethod() != null) + && response.getRequest().getMethod().equals(Method.HEAD)) { + addEntityHeaders(response); + response.setEntity(null); + } else if (Method.GET.equals(response.getRequest().getMethod()) + && Status.SUCCESS_OK.equals(response.getStatus()) + && (!response.isEntityAvailable())) { + addEntityHeaders(response); + getLogger() + .warning( + "A response with a 200 (Ok) status should have an entity. Make sure that resource \"" + + response.getRequest().getResourceRef() + + "\" returns one or sets the status to 204 (No content)."); + } else if (response.getStatus().equals(Status.SUCCESS_NO_CONTENT)) { + addEntityHeaders(response); + + if (response.isEntityAvailable()) { + getLogger() + .fine( + "Responses with a 204 (No content) status generally don't have an entity. Only adding entity headers for resource \"" + + response.getRequest().getResourceRef() + + "\"."); + response.setEntity(null); + } + } else if (response.getStatus().equals(Status.SUCCESS_RESET_CONTENT)) { + if (response.isEntityAvailable()) { + getLogger() + .warning( + "Responses with a 205 (Reset content) status can't have an entity. Ignoring the entity for resource \"" + + response.getRequest().getResourceRef() + + "\"."); + response.setEntity(null); + } + } else if (response.getStatus().equals(Status.REDIRECTION_NOT_MODIFIED)) { + if (response.getEntity() != null) { + HeaderUtils.addNotModifiedEntityHeaders( + response.getEntity(), response.getHttpCall().getResponseHeaders()); + response.setEntity(null); + } + } else if (response.getStatus().isInformational()) { + if (response.isEntityAvailable()) { + getLogger() + .warning( + "Responses with an informational (1xx) status can't have an entity. Ignoring the entity for resource \"" + + response.getRequest().getResourceRef() + + "\"."); + response.setEntity(null); + } + } else { + addEntityHeaders(response); + + if (!response.isEntityAvailable()) { + if ((response.getEntity() != null) && (response.getEntity().getSize() != 0)) { + getLogger() + .warning( + "A response with an unavailable and potentially non empty entity was returned. Ignoring the entity for resource \"" + + response.getRequest().getResourceRef() + + "\"."); + } + + response.setEntity(null); + } + } + + // Add the response headers + addResponseHeaders(response); + + // Send the response to the client + response.getHttpCall().sendResponse(response); + } catch (Throwable t) { + if (response.getHttpCall().isConnectionBroken(t)) { + // output a single log line for this common case to avoid filling server logs + getLogger() + .log( + Level.INFO, + "The connection was broken. It was probably closed by the client. Reason: " + + t.getMessage()); + } else { + getLogger() + .log(Level.SEVERE, "An exception occurred writing the response entity", t); + response.getHttpCall().setStatusCode(Status.SERVER_ERROR_INTERNAL.getCode()); + response.getHttpCall() + .setReasonPhrase("An exception occurred writing the response entity"); + response.setEntity(null); + + try { + response.getHttpCall().sendResponse(response); + } catch (IOException ioe) { + getLogger().log(Level.WARNING, "Unable to send error response", ioe); + } + } + } finally { + response.getHttpCall().complete(); + + if (response.getOnSent() != null) { + response.getOnSent().handle(response.getRequest(), response); + } + } + } + + /** + * Converts a low-level HTTP call into a high-level uniform request. + * + * @param httpCall The low-level HTTP call. + * @return A new high-level uniform request. + */ + public HttpRequest toRequest(ServerCall httpCall) { + HttpRequest result = new HttpRequest(getContext(), httpCall); + result.getAttributes().put(ATTRIBUTE_HEADERS, httpCall.getRequestHeaders()); + + if (httpCall.getVersion() != null) { + result.getAttributes().put(ATTRIBUTE_VERSION, httpCall.getVersion()); + } + + if (httpCall.isConfidential()) { + List clientCertificates = httpCall.getCertificates(); + + if (clientCertificates != null) { + result.getClientInfo().setCertificates(clientCertificates); + } + + String cipherSuite = httpCall.getCipherSuite(); + + if (cipherSuite != null) { + result.getClientInfo().setCipherSuite(cipherSuite); + } + + Integer keySize = httpCall.getSslKeySize(); + + if (keySize != null) { + result.getAttributes().put(ATTRIBUTE_HTTPS_KEY_SIZE, keySize); + } + + String sslSessionId = httpCall.getSslSessionId(); + + if (sslSessionId != null) { + result.getAttributes().put(ATTRIBUTE_HTTPS_SSL_SESSION_ID, sslSessionId); + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java index 5a73ff07cc..062d628041 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java @@ -1,21 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.adapter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.security.cert.Certificate; +import java.util.Base64; +import java.util.List; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Response; import org.restlet.Server; import org.restlet.data.Digest; import org.restlet.data.Header; import org.restlet.engine.connector.ConnectorHelper; -import org.restlet.engine.header.*; +import org.restlet.engine.header.ContentType; +import org.restlet.engine.header.DispositionReader; +import org.restlet.engine.header.EncodingReader; +import org.restlet.engine.header.HeaderConstants; +import org.restlet.engine.header.HeaderReader; +import org.restlet.engine.header.HeaderUtils; +import org.restlet.engine.header.LanguageReader; +import org.restlet.engine.header.RangeReader; import org.restlet.engine.io.IoUtils; import org.restlet.engine.util.StringUtils; import org.restlet.representation.EmptyRepresentation; @@ -23,16 +37,6 @@ import org.restlet.representation.Representation; import org.restlet.service.ConnectorService; -import javax.net.ssl.SSLPeerUnverifiedException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PushbackInputStream; -import java.security.cert.Certificate; -import java.util.Base64; -import java.util.List; -import java.util.logging.Level; - /** * Abstract HTTP server connector call. * @@ -40,142 +44,140 @@ */ public abstract class ServerCall extends Call { - /** Indicates if the "host" header was already parsed. */ - private volatile boolean hostParsed; - - /** - * Constructor. - * - * @param server The parent server connector. - */ - public ServerCall(Server server) { - this((server == null) ? null : server.getAddress(), (server == null) ? 0 : server.getPort()); - } - - /** - * Constructor. - * - * @param serverAddress The server IP address. - * @param serverPort The server port. - */ - public ServerCall(String serverAddress, int serverPort) { - setServerAddress(serverAddress); - setServerPort(serverPort); - this.hostParsed = false; - } - - /** - * Ask the connector to abort the related network connection, for example - * immediately closing the socket. - * - * @return True if the connection was aborted. - */ - public abstract boolean abort(); - - /** - * Complete the response - */ - public void complete() { - - } - - /** - * Flushes the buffers onto the network so that for example you can force - * headers to be written before the entity is becoming available. - * - * @throws IOException - */ - public void flushBuffers() throws IOException { - } - - /** - * Returns the chain of client SSL certificates, if available and accessible. - * - * @return The chain of client SSL certificates, if available and accessible. - */ - public List getCertificates() { - return null; - } - - /** - * Returns the SSL Cipher Suite, if available and accessible. - * - * @return The SSL Cipher Suite, if available and accessible. - */ - public String getCipherSuite() { - return null; - } - - /** - * Returns the content length of the request entity if know, - * {@link Representation#UNKNOWN_SIZE} otherwise. - * - * @return The request content length. - */ - protected long getContentLength() { - return HeaderUtils.getContentLength(getRequestHeaders()); - } - - /** - * Returns the host domain name. - * - * @return The host domain name. - */ - @Override - public String getHostDomain() { - if (!this.hostParsed) { - parseHost(); - } - return super.getHostDomain(); - } - - /** - * Returns the host port. - * - * @return The host port. - */ - @Override - public int getHostPort() { - if (!this.hostParsed) { - parseHost(); - } - return super.getHostPort(); - } - - /** - * Returns the request entity if available. - * - * @return The request entity if available. - */ - public Representation getRequestEntity() { - Representation result; - long contentLength = getContentLength(); - boolean chunkedEncoding = HeaderUtils.isChunkedEncoding(getRequestHeaders()); - // In some cases, there is an entity without a content-length header - boolean connectionClosed = HeaderUtils.isConnectionClose(getRequestHeaders()); - - // Create the representation - if (((contentLength != Representation.UNKNOWN_SIZE) && (contentLength != 0)) || chunkedEncoding - || connectionClosed) { - // Create the result representation - InputStream requestStream = getRequestEntityStream(contentLength); - - if (connectionClosed) { - // We need to detect if there is really an entity or not as only - // the end of connection can let us know at this point - PushbackInputStream pbi = new PushbackInputStream(requestStream); - - try { - int next = pbi.read(); - - if (next != -1) { - pbi.unread(next); - requestStream = pbi; - } else { - requestStream = null; - } - } catch (IOException e) { - getLogger().fine("Unable to read request entity"); + /** Indicates if the "host" header was already parsed. */ + private volatile boolean hostParsed; + + /** + * Constructor. + * + * @param server The parent server connector. + */ + public ServerCall(Server server) { + this( + (server == null) ? null : server.getAddress(), + (server == null) ? 0 : server.getPort()); + } + + /** + * Constructor. + * + * @param serverAddress The server IP address. + * @param serverPort The server port. + */ + public ServerCall(String serverAddress, int serverPort) { + setServerAddress(serverAddress); + setServerPort(serverPort); + this.hostParsed = false; + } + + /** + * Ask the connector to abort the related network connection, for example immediately closing + * the socket. + * + * @return True if the connection was aborted. + */ + public abstract boolean abort(); + + /** Complete the response */ + public void complete() {} + + /** + * Flushes the buffers onto the network so that for example you can force headers to be written + * before the entity is becoming available. + * + * @throws IOException + */ + public void flushBuffers() throws IOException {} + + /** + * Returns the chain of client SSL certificates, if available and accessible. + * + * @return The chain of client SSL certificates, if available and accessible. + */ + public List getCertificates() { + return null; + } + + /** + * Returns the SSL Cipher Suite, if available and accessible. + * + * @return The SSL Cipher Suite, if available and accessible. + */ + public String getCipherSuite() { + return null; + } + + /** + * Returns the content length of the request entity if know, {@link Representation#UNKNOWN_SIZE} + * otherwise. + * + * @return The request content length. + */ + protected long getContentLength() { + return HeaderUtils.getContentLength(getRequestHeaders()); + } + + /** + * Returns the host domain name. + * + * @return The host domain name. + */ + @Override + public String getHostDomain() { + if (!this.hostParsed) { + parseHost(); + } + return super.getHostDomain(); + } + + /** + * Returns the host port. + * + * @return The host port. + */ + @Override + public int getHostPort() { + if (!this.hostParsed) { + parseHost(); + } + return super.getHostPort(); + } + + /** + * Returns the request entity if available. + * + * @return The request entity if available. + */ + public Representation getRequestEntity() { + Representation result; + long contentLength = getContentLength(); + boolean chunkedEncoding = HeaderUtils.isChunkedEncoding(getRequestHeaders()); + // In some cases, there is an entity without a content-length header + boolean connectionClosed = HeaderUtils.isConnectionClose(getRequestHeaders()); + + // Create the representation + if (((contentLength != Representation.UNKNOWN_SIZE) && (contentLength != 0)) + || chunkedEncoding + || connectionClosed) { + // Create the result representation + InputStream requestStream = getRequestEntityStream(contentLength); + + if (connectionClosed) { + // We need to detect if there is really an entity or not as only + // the end of the connection can let us know at this point + PushbackInputStream pbi = new PushbackInputStream(requestStream); + + try { + int next = pbi.read(); + + if (next != -1) { + pbi.unread(next); + requestStream = pbi; + } else { + requestStream = null; + } + } catch (IOException e) { + getLogger().fine("Unable to read request entity"); try { pbi.close(); @@ -183,332 +185,341 @@ public Representation getRequestEntity() { getLogger().fine("Unable to close request entity"); } } - } - - if (requestStream != null) { - result = new InputRepresentation(requestStream, null, contentLength); - } else { - result = new EmptyRepresentation(); - } - - result.setSize(contentLength); - } else { - result = new EmptyRepresentation(); - } - - // Extract some interesting header values - for (Header header : getRequestHeaders()) { - if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_ENCODING)) { - new EncodingReader(header.getValue()).addValues(result.getEncodings()); - } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_LANGUAGE)) { - new LanguageReader(header.getValue()).addValues(result.getLanguages()); - } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_TYPE)) { - ContentType contentType = new ContentType(header.getValue()); - result.setMediaType(contentType.getMediaType()); - result.setCharacterSet(contentType.getCharacterSet()); - } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_RANGE)) { - RangeReader.update(header.getValue(), result); - } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_MD5)) { - result.setDigest(new Digest(Digest.ALGORITHM_MD5, Base64.getDecoder().decode(header.getValue()))); - } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_DISPOSITION)) { - try { - result.setDisposition(new DispositionReader(header.getValue()).readValue()); - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.WARNING, - "Error during Content-Disposition header parsing. Header: " + header.getValue(), ioe); - } - } - } - - return result; - } - - /** - * Returns the request entity stream if it exists. - * - * @param size The expected entity size or -1 if unknown. - * @return The request entity stream if it exists. - */ - public abstract InputStream getRequestEntityStream(long size); - - /** - * Returns the request head stream if it exists. - * - * @return The request head stream if it exists. - */ - public abstract InputStream getRequestHeadStream(); - - /** - * Returns the response entity stream if it exists. - * - * @return The response entity stream if it exists. - */ - public abstract OutputStream getResponseEntityStream(); - - /** - * Returns the SSL key size, if available and accessible. - * - * @return The SSL key size, if available and accessible. - */ - public Integer getSslKeySize() { - return null; - } - - /** - * Returns the SSL session ID, in hexadecimal encoding, if available and - * accessible. - * - * @return The SSL session ID, in hexadecimal encoding, if available and - * accessible. - */ - public String getSslSessionId() { - byte[] byteArray = getSslSessionIdBytes(); - - if (byteArray != null) { - return IoUtils.toHexString(byteArray); - } else { - return null; - } - } - - /** - * Returns the SSL session ID, as a byte array, if available and accessible in - * that format (to be used by getSslSessionId). - * - * @return The SSL session ID, as a byte array, if available and accessible in - * that format. - */ - protected byte[] getSslSessionIdBytes() { - return null; - } - - @Override - protected boolean isClientKeepAlive() { - return !HeaderUtils.isConnectionClose(getRequestHeaders()); - } - - @Override - protected boolean isServerKeepAlive() { - return true; - } - - /** - * Parses the "host" header to set the server host and port properties. - */ - private void parseHost() { - String host = getRequestHeaders().getFirstValue(HeaderConstants.HEADER_HOST, true); - - if (host != null) { - // Take care of IPV6 addresses - int colonIndex = host.indexOf(':', host.indexOf(']')); - - if (colonIndex != -1) { - super.setHostDomain(host.substring(0, colonIndex)); - super.setHostPort(Integer.parseInt(host.substring(colonIndex + 1))); - } else { - super.setHostDomain(host); - super.setHostPort(getProtocol().getDefaultPort()); - } - } else { - getLogger().info("Couldn't find the mandatory \"Host\" HTTP header."); - } - - this.hostParsed = true; - } - - /** - * Reads the HTTP request head (request line and headers). - * - * @throws IOException - */ - protected void readRequestHead(InputStream headStream) throws IOException { - StringBuilder sb = new StringBuilder(); - - // Parse the request method - int next = headStream.read(); - while ((next != -1) && !HeaderUtils.isSpace(next)) { - sb.append((char) next); - next = headStream.read(); - } - - if (next == -1) { - throw new IOException("Unable to parse the request method. End of stream reached too early."); - } - - setMethod(sb.toString()); - sb.delete(0, sb.length()); - - // Parse the request URI - next = headStream.read(); - while ((next != -1) && !HeaderUtils.isSpace(next)) { - sb.append((char) next); - next = headStream.read(); - } - - if (next == -1) { - throw new IOException("Unable to parse the request URI. End of stream reached too early."); - } - setRequestUri(sb.toString()); - sb.delete(0, sb.length()); - - // Parse the HTTP version - next = headStream.read(); - while ((next != -1) && !HeaderUtils.isCarriageReturn(next)) { - sb.append((char) next); - next = headStream.read(); - } - - if (next == -1) { - throw new IOException("Unable to parse the HTTP version. End of stream reached too early."); - } - next = headStream.read(); - - if (HeaderUtils.isLineFeed(next)) { - setVersion(sb.toString()); - sb.delete(0, sb.length()); - - // Parse the headers - Header header = HeaderReader.readHeader(headStream, sb); - - while (header != null) { - getRequestHeaders().add(header); - header = HeaderReader.readHeader(headStream, sb); - } - } else { - throw new IOException( - "Unable to parse the HTTP version. The carriage return must be followed by a line feed."); - } - } - - /** - * Sends the response back to the client. Commits the status, headers and - * optional entity and send them over the network. The default implementation - * only writes the response entity on the response stream or channel. Subclasses - * will probably also copy the response headers and status. - * - * @param response The high-level response. - * @throws IOException if the Response could not be written to the network. - */ - public void sendResponse(Response response) throws IOException { - if (response != null) { - // Get the connector service to callback - Representation responseEntity = response.getEntity(); - ConnectorService connectorService = ConnectorHelper.getConnectorService(); - - if (connectorService != null) { - connectorService.beforeSend(responseEntity); - } - - OutputStream responseEntityStream = null; - try { - writeResponseHead(response); - - if (responseEntity != null) { - - responseEntityStream = getResponseEntityStream(); - writeResponseBody(responseEntity, responseEntityStream); - } - } finally { - if (responseEntityStream != null) { - try { - responseEntityStream.flush(); - responseEntityStream.close(); - } catch (IOException ioe) { - // The stream was probably already closed by the - // connector. Probably OK, low message priority. - getLogger().log(Level.FINE, "Exception while flushing and closing the entity stream.", ioe); - } - } - if (responseEntity != null) { - responseEntity.release(); - } - - if (connectorService != null) { - connectorService.afterSend(responseEntity); - } - } - } - } - - /** - * Indicates if the response should be chunked because its length is unknown. - * - * @param response The response to analyze. - * @return True if the response should be chunked. - */ - public boolean shouldResponseBeChunked(Response response) { - return (response.getEntity() != null) && !response.getEntity().hasKnownSize(); - } - - /** - * Effectively writes the response body. The entity to write is guaranteed to be - * non null. Attempts to write the entity on the response channel or response - * stream by default. - * - * @param entity The representation to write as entity of the - * body. - * @param responseEntityStream The response entity stream or null if a channel - * is used. - * @throws IOException - */ - protected void writeResponseBody(Representation entity, OutputStream responseEntityStream) throws IOException { - // Send the entity to the client - if (responseEntityStream != null) { - entity.write(responseEntityStream); - responseEntityStream.flush(); - } - } - - /** - * Writes the response status line and headers. Does nothing by default. - * - * @param response The response. - * @throws IOException - */ - protected void writeResponseHead(Response response) throws IOException { - // Do nothing by default - } - - /** - * Writes the response head to the given output stream. - * - * @param response The response. - * @param headStream The output stream to write to. - * @throws IOException - */ - protected void writeResponseHead(Response response, OutputStream headStream) throws IOException { - // Write the status line - String version = (getVersion() == null) ? "1.1" : getVersion(); - headStream.write(StringUtils.getAsciiBytes(version)); - headStream.write(' '); - headStream.write(StringUtils.getAsciiBytes(Integer.toString(getStatusCode()))); - headStream.write(' '); - - if (getReasonPhrase() != null) { - headStream.write(StringUtils.getLatin1Bytes(getReasonPhrase())); - } else { - headStream.write(StringUtils.getAsciiBytes(("Status " + getStatusCode()))); - } - - headStream.write(13); // CR - headStream.write(10); // LF - - // We don't support persistent connections yet - getResponseHeaders().set(HeaderConstants.HEADER_CONNECTION, "close", true); - - // Check if 'Transfer-Encoding' header should be set - if (shouldResponseBeChunked(response)) { - getResponseHeaders().add(HeaderConstants.HEADER_TRANSFER_ENCODING, "chunked"); - } - - // Write the response headers - for (Header header : getResponseHeaders()) { - HeaderUtils.writeHeaderLine(header, headStream); - } - - // Write the end of the headers section - headStream.write(13); // CR - headStream.write(10); // LF - headStream.flush(); - } + } + + if (requestStream != null) { + result = new InputRepresentation(requestStream, null, contentLength); + } else { + result = new EmptyRepresentation(); + } + + result.setSize(contentLength); + } else { + result = new EmptyRepresentation(); + } + + // Extract some interesting header values + for (Header header : getRequestHeaders()) { + if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_ENCODING)) { + new EncodingReader(header.getValue()).addValues(result.getEncodings()); + } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_LANGUAGE)) { + new LanguageReader(header.getValue()).addValues(result.getLanguages()); + } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_TYPE)) { + ContentType contentType = new ContentType(header.getValue()); + result.setMediaType(contentType.getMediaType()); + result.setCharacterSet(contentType.getCharacterSet()); + } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_RANGE)) { + RangeReader.update(header.getValue(), result); + } else if (header.getName().equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_MD5)) { + result.setDigest( + new Digest( + Digest.ALGORITHM_MD5, + Base64.getDecoder().decode(header.getValue()))); + } else if (header.getName() + .equalsIgnoreCase(HeaderConstants.HEADER_CONTENT_DISPOSITION)) { + try { + result.setDisposition(new DispositionReader(header.getValue()).readValue()); + } catch (IOException ioe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Error during Content-Disposition header parsing. Header: " + + header.getValue(), + ioe); + } + } + } + + return result; + } + + /** + * Returns the request entity stream if it exists. + * + * @param size The expected entity size or -1 if unknown. + * @return The request entity stream if it exists. + */ + public abstract InputStream getRequestEntityStream(long size); + + /** + * Returns the request head stream if it exists. + * + * @return The request head stream if it exists. + */ + public abstract InputStream getRequestHeadStream(); + + /** + * Returns the response entity stream if it exists. + * + * @return The response entity stream if it exists. + */ + public abstract OutputStream getResponseEntityStream(); + + /** + * Returns the SSL key size, if available and accessible. + * + * @return The SSL key size, if available and accessible. + */ + public Integer getSslKeySize() { + return null; + } + + /** + * Returns the SSL session ID, in hexadecimal encoding, if available and accessible. + * + * @return The SSL session ID, in hexadecimal encoding, if available and accessible. + */ + public String getSslSessionId() { + byte[] byteArray = getSslSessionIdBytes(); + + if (byteArray != null) { + return IoUtils.toHexString(byteArray); + } else { + return null; + } + } + + /** + * Returns the SSL session ID, as a byte array, if available and accessible in that format (to + * be used by getSslSessionId). + * + * @return The SSL session ID, as a byte array, if available and accessible in that format. + */ + protected byte[] getSslSessionIdBytes() { + return null; + } + + @Override + protected boolean isClientKeepAlive() { + return !HeaderUtils.isConnectionClose(getRequestHeaders()); + } + + @Override + protected boolean isServerKeepAlive() { + return true; + } + + /** Parses the "host" header to set the server host and port properties. */ + private void parseHost() { + String host = getRequestHeaders().getFirstValue(HeaderConstants.HEADER_HOST, true); + + if (host != null) { + // Take care of IPV6 addresses + int colonIndex = host.indexOf(':', host.indexOf(']')); + + if (colonIndex != -1) { + super.setHostDomain(host.substring(0, colonIndex)); + super.setHostPort(Integer.parseInt(host.substring(colonIndex + 1))); + } else { + super.setHostDomain(host); + super.setHostPort(getProtocol().getDefaultPort()); + } + } else { + getLogger().info("Couldn't find the mandatory \"Host\" HTTP header."); + } + + this.hostParsed = true; + } + + /** + * Reads the HTTP request head (request line and headers). + * + * @throws IOException + */ + protected void readRequestHead(InputStream headStream) throws IOException { + StringBuilder sb = new StringBuilder(); + + // Parse the request method + int next = headStream.read(); + while ((next != -1) && !HeaderUtils.isSpace(next)) { + sb.append((char) next); + next = headStream.read(); + } + + if (next == -1) { + throw new IOException( + "Unable to parse the request method. End of stream reached too early."); + } + + setMethod(sb.toString()); + sb.delete(0, sb.length()); + + // Parse the request URI + next = headStream.read(); + while ((next != -1) && !HeaderUtils.isSpace(next)) { + sb.append((char) next); + next = headStream.read(); + } + + if (next == -1) { + throw new IOException( + "Unable to parse the request URI. End of stream reached too early."); + } + setRequestUri(sb.toString()); + sb.delete(0, sb.length()); + + // Parse the HTTP version + next = headStream.read(); + while ((next != -1) && !HeaderUtils.isCarriageReturn(next)) { + sb.append((char) next); + next = headStream.read(); + } + + if (next == -1) { + throw new IOException( + "Unable to parse the HTTP version. End of stream reached too early."); + } + next = headStream.read(); + + if (HeaderUtils.isLineFeed(next)) { + setVersion(sb.toString()); + sb.delete(0, sb.length()); + + // Parse the headers + Header header = HeaderReader.readHeader(headStream, sb); + + while (header != null) { + getRequestHeaders().add(header); + header = HeaderReader.readHeader(headStream, sb); + } + } else { + throw new IOException( + "Unable to parse the HTTP version. The carriage return must be followed by a line feed."); + } + } + + /** + * Sends the response back to the client. Commits the status, headers, and optional entity and + * send them over the network. The default implementation only writes the response entity on the + * response stream or channel. Subclasses will probably also copy the response headers and + * status. + * + * @param response The high-level response. + * @throws IOException if the Response could not be written to the network. + */ + public void sendResponse(Response response) throws IOException { + if (response != null) { + // Get the connector service to callback + Representation responseEntity = response.getEntity(); + ConnectorService connectorService = ConnectorHelper.getConnectorService(); + + if (connectorService != null) { + connectorService.beforeSend(responseEntity); + } + + OutputStream responseEntityStream = null; + try { + writeResponseHead(response); + + if (responseEntity != null) { + + responseEntityStream = getResponseEntityStream(); + writeResponseBody(responseEntity, responseEntityStream); + } + } finally { + if (responseEntityStream != null) { + try { + responseEntityStream.flush(); + responseEntityStream.close(); + } catch (IOException ioe) { + // The stream was probably already closed by the + // connector. Probably OK, low message priority. + getLogger() + .log( + Level.FINE, + "Exception while flushing and closing the entity stream.", + ioe); + } + } + if (responseEntity != null) { + responseEntity.release(); + } + + if (connectorService != null) { + connectorService.afterSend(responseEntity); + } + } + } + } + + /** + * Indicates if the response should be chunked because its length is unknown. + * + * @param response The response to analyze. + * @return True if the response should be chunked. + */ + public boolean shouldResponseBeChunked(Response response) { + return (response.getEntity() != null) && !response.getEntity().hasKnownSize(); + } + + /** + * Effectively writes the response body. The entity to write is guaranteed to be non-null. + * Attempts to write the entity on the response channel or response stream by default. + * + * @param entity The representation to write as entity of the body. + * @param responseEntityStream The response entity stream or null if a channel is used. + * @throws IOException + */ + protected void writeResponseBody(Representation entity, OutputStream responseEntityStream) + throws IOException { + // Send the entity to the client + if (responseEntityStream != null) { + entity.write(responseEntityStream); + responseEntityStream.flush(); + } + } + + /** + * Writes the response status line and headers. Does nothing by default. + * + * @param response The response. + * @throws IOException + */ + protected void writeResponseHead(Response response) throws IOException { + // Do nothing by default + } + + /** + * Writes the response head to the given output stream. + * + * @param response The response. + * @param headStream The output stream to write to. + * @throws IOException + */ + protected void writeResponseHead(Response response, OutputStream headStream) + throws IOException { + // Write the status line + String version = (getVersion() == null) ? "1.1" : getVersion(); + headStream.write(StringUtils.getAsciiBytes(version)); + headStream.write(' '); + headStream.write(StringUtils.getAsciiBytes(Integer.toString(getStatusCode()))); + headStream.write(' '); + + if (getReasonPhrase() != null) { + headStream.write(StringUtils.getLatin1Bytes(getReasonPhrase())); + } else { + headStream.write(StringUtils.getAsciiBytes(("Status " + getStatusCode()))); + } + + headStream.write(13); // CR + headStream.write(10); // LF + + // We don't support persistent connections yet + getResponseHeaders().set(HeaderConstants.HEADER_CONNECTION, "close", true); + + // Check if 'Transfer-Encoding' header should be set + if (shouldResponseBeChunked(response)) { + getResponseHeaders().add(HeaderConstants.HEADER_TRANSFER_ENCODING, "chunked"); + } + + // Write the response headers + for (Header header : getResponseHeaders()) { + HeaderUtils.writeHeaderLine(header, headStream); + } + + // Write the end of the headers section + headStream.write(13); // CR + headStream.write(10); // LF + headStream.flush(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java b/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java index f204bab732..2d52b95c1c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java @@ -1,15 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; -import org.restlet.*; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.Protocol; import org.restlet.data.Reference; import org.restlet.data.Status; @@ -17,136 +23,146 @@ import org.restlet.routing.Filter; import org.restlet.service.Service; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - /** * Application implementation. - * + * * @author Jerome Louvel */ public class ApplicationHelper extends CompositeHelper { - /** - * Constructor. - * - * @param application The application to help. - */ - public ApplicationHelper(Application application) { - super(application); - } - - /** - * In addition to the default behavior, it saves the current application - * instance into the current thread. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public void handle(Request request, Response response) { - // Save the current application - // Plan to move current application as attribute of the Response in incoming 2.5 - // release - final Application currentApplication = getHelped() != null ? getHelped() : Application.getCurrent(); - Application.setCurrent(currentApplication); - - // Actually handle call - try { - super.handle(request, response); - } finally { - // restore the current application - Application.setCurrent(currentApplication); - } - } - - /** - * Sets the context. - * - * @param context The context. - */ - public void setContext(Context context) { - if (context != null) { - setOutboundNext(context.getClientDispatcher()); - } - } - - /** Start hook. */ - @Override - public synchronized void start() throws Exception { - Filter filter = null; - - for (Service service : getHelped().getServices()) { - if (service.isEnabled()) { - // Attach the service inbound filters - filter = service.createInboundFilter((getContext() == null) ? null : getContext().createChildContext()); - - if (filter != null) { - addInboundFilter(filter); - } - - // Attach the service outbound filters - filter = service - .createOutboundFilter((getContext() == null) ? null : getContext().createChildContext()); - - if (filter != null) { - addOutboundFilter(filter); - } - } - } - - // Attach the Application's server root Restlet - setInboundNext(getHelped().getInboundRoot()); - - if (getOutboundNext() == null) { - // Warn about chaining problem - getLogger().fine( - "By default, an application should be attached to a parent component in order to let application's outbound root handle calls properly."); - setOutboundNext(new Restlet() { - final Map clients = new ConcurrentHashMap(); - - @Override - public void handle(Request request, Response response) { - Protocol rProtocol = request.getProtocol(); - Reference rReference = request.getResourceRef(); - Protocol protocol = (rProtocol != null) ? rProtocol - : (rReference != null) ? rReference.getSchemeProtocol() : null; - - if (protocol != null) { - Client c = clients.get(protocol); - - if (c == null) { - c = new Client(protocol); - clients.put(protocol, c); - getLogger().fine("Added runtime client for protocol: " + protocol.getName()); - } - - c.handle(request, response); - } else { - response.setStatus(Status.SERVER_ERROR_INTERNAL, - "The server isn't properly configured to handle client calls."); - getLogger() - .warning("There is no protocol detected for this request: " + request.getResourceRef()); - } - } - - @Override - public synchronized void stop() throws Exception { - super.stop(); - for (Client client : clients.values()) { - client.stop(); - } - } - }); - } - } - - @Override - public synchronized void stop() throws Exception { - clear(); - } - - @Override - public void update() throws Exception { - } - + /** + * Constructor. + * + * @param application The application to help. + */ + public ApplicationHelper(Application application) { + super(application); + } + + /** + * In addition to the default behavior, it saves the current application instance into the + * current thread. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public void handle(Request request, Response response) { + // Save the current application + // Plan to move the current application as an attribute of the Response in the incoming 2.5 + // release + final Application currentApplication = + getHelped() != null ? getHelped() : Application.getCurrent(); + Application.setCurrent(currentApplication); + + // Actually handle call + try { + super.handle(request, response); + } finally { + // restore the current application + Application.setCurrent(currentApplication); + } + } + + /** + * Sets the context. + * + * @param context The context. + */ + public void setContext(Context context) { + if (context != null) { + setOutboundNext(context.getClientDispatcher()); + } + } + + /** Start hook. */ + @Override + public synchronized void start() throws Exception { + Filter filter; + + for (Service service : getHelped().getServices()) { + if (service.isEnabled()) { + // Attach the service inbound filters + Context context = (getContext() == null) ? null : getContext().createChildContext(); + filter = service.createInboundFilter(context); + + if (filter != null) { + addInboundFilter(filter); + } + + // Attach the service outbound filters + context = (getContext() == null) ? null : getContext().createChildContext(); + filter = service.createOutboundFilter(context); + + if (filter != null) { + addOutboundFilter(filter); + } + } + } + + // Attach the Application's server root Restlet + setInboundNext(getHelped().getInboundRoot()); + + if (getOutboundNext() == null) { + // Warn about a chaining problem + getLogger() + .fine( + "By default, an application should be attached to a parent component to let application's outbound root handle calls properly."); + setOutboundNext( + new Restlet() { + final Map clients = + new ConcurrentHashMap(); + + @Override + public void handle(Request request, Response response) { + Protocol rProtocol = request.getProtocol(); + Reference rReference = request.getResourceRef(); + Protocol protocol = + (rProtocol != null) + ? rProtocol + : (rReference != null) + ? rReference.getSchemeProtocol() + : null; + + if (protocol != null) { + Client c = clients.get(protocol); + + if (c == null) { + c = new Client(protocol); + clients.put(protocol, c); + getLogger() + .fine( + "Added runtime client for protocol: " + + protocol.getName()); + } + + c.handle(request, response); + } else { + response.setStatus( + Status.SERVER_ERROR_INTERNAL, + "The server isn't properly configured to handle client calls."); + getLogger() + .warning( + "There is no protocol detected for this request: " + + request.getResourceRef()); + } + } + + @Override + public synchronized void stop() throws Exception { + super.stop(); + for (Client client : clients.values()) { + client.stop(); + } + } + }); + } + } + + @Override + public synchronized void stop() throws Exception { + clear(); + } + + @Override + public void update() throws Exception {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java b/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java index 4992b4da5b..8f28cbfe6f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java @@ -1,89 +1,83 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.util.List; import org.restlet.Request; import org.restlet.representation.Variant; import org.restlet.service.MetadataService; -import java.util.List; - /** * Content negotiation algorithm. - * + * * @author Jerome Louvel */ public abstract class Conneg { - /** The request including client preferences. */ - private final Request request; - - /** - * Constructor. - * - * @param request The request including client preferences. - * @param metadataService The metadata service used to get default metadata - * values. - */ - public Conneg(Request request, MetadataService metadataService) { - this.request = request; - } + /** The request including client preferences. */ + private final Request request; - /** - * Returns the request including client preferences. - * - * @return The request including client preferences. - */ - public Request getRequest() { - return request; - } + /** + * Constructor. + * + * @param request The request including client preferences. + * @param metadataService The metadata service used to get default metadata values. + */ + public Conneg(Request request, MetadataService metadataService) { + this.request = request; + } - /** - * Returns the best variant representation for a given resource according the - * the client preferences.
- * A default language is provided in case the variants don't match the client - * preferences. - * - * @param variants The list of variants to compare. - * @return The preferred variant. - * @see Apache - * content negotiation algorithm - */ - public Variant getPreferredVariant(List variants) { - Variant result = null; + /** + * Returns the request including client preferences. + * + * @return The request including client preferences. + */ + public Request getRequest() { + return request; + } - if ((variants != null) && !variants.isEmpty()) { - float bestScore = -1.0F; - float current; + /** + * Returns the best variant representation for a given resource according the client + * preferences.
+ * A default language is provided in case the variants don't match the client preferences. + * + * @param variants The list of variants to compare. + * @return The preferred variant. + * @see Apache + * content negotiation algorithm + */ + public Variant getPreferredVariant(List variants) { + Variant result = null; - // Compute the score of each variant - for (Variant variant : variants) { - current = scoreVariant(variant); + if ((variants != null) && !variants.isEmpty()) { + float bestScore = -1.0F; + float current; - if (current > bestScore) { - bestScore = current; - result = variant; - } - } - } + // Compute the score of each variant + for (Variant variant : variants) { + current = scoreVariant(variant); - return result; - } + if (current > bestScore) { + bestScore = current; + result = variant; + } + } + } - /** - * Scores a variant relatively to enriched client preferences. - * - * @param variant The variant to score. - * @return The enriched client preferences. - */ - public abstract float scoreVariant(Variant variant); + return result; + } + /** + * Scores a variant relatively to enriched client preferences. + * + * @param variant The variant to score. + * @return The enriched client preferences. + */ + public abstract float scoreVariant(Variant variant); } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java index 62ea3270a4..57002fb3c1 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -17,346 +19,325 @@ import org.restlet.engine.util.SetUtils; import org.restlet.routing.Filter; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - /** - * Filter that helps support CORS requests. This filter lets the target - * resources specify the allowed methods. - * - * Example: - * + * Filter that helps support CORS requests. This filter lets the target resources specify the + * allowed methods. + * + *

Example: + * *

  * Router router = new Router(getContext());
- * 
+ *
  * CorsFilter corsFilter = new CorsFilter(getContext(), router);
  * corsFilter.setAllowedOrigins(new HashSet(Arrays.asList("http://server.com")));
  * corsFilter.setAllowedCredentials(true);
  * 
- * + * * @author Manuel Boillod */ public class CorsFilter extends Filter { - /** - * If true, copies the value of 'Access-Control-Request-Headers' request header - * into the 'Access-Control-Allow-Headers' response header. If false, use - * {@link #allowedHeaders}. Default is true. - */ - public boolean allowAllRequestedHeaders = true; - - /** - * If true, add 'Access-Control-Allow-Credentials' header. Default is false. - */ - private boolean allowedCredentials = false; - - /** - * The value of 'Access-Control-Allow-Headers' response header. Used only if - * {@link #allowAllRequestedHeaders} is false. - */ - private Set allowedHeaders = null; - - /** The value of 'Access-Control-Allow-Origin' header. Default is '*'. */ - private Set allowedOrigins = SetUtils.newHashSet("*"); - - /** Helper for generating CORS response. */ - private CorsResponseHelper corsResponseHelper; - - /** - * The set of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. By default: GET, PUT, - * POST, DELETE, PATCH. - */ - private Set defaultAllowedMethods = new HashSet<>( - Arrays.asList(Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); - - /** The value of 'Access-Control-Expose-Headers' response header. */ - private Set exposedHeaders = null; - - /** - * The value of 'Access-Control-Max-Age' response header. Default is that the - * header is not set. - */ - private int maxAge = -1; - - /** - * If true, the filter does not call the server resource for OPTIONS method of - * CORS request and set Access-Control-Allow-Methods header with - * {@link #defaultAllowedMethods}. Default is false. - */ - private boolean skippingResourceForCorsOptions = false; - - /** - * Constructor. - */ - public CorsFilter() { - this(null); - } - - /** - * Constructor. - * - * @param context The context. - */ - public CorsFilter(Context context) { - super(context, null); - } - - /** - * Constructor. - * - * @param context The context. - * @param next The next Restlet. - */ - public CorsFilter(Context context, Restlet next) { - super(context, next); - } - - /** - * Add CORS headers to response - * - * @param request The request to handle. - * @param response The response - */ - @Override - protected void afterHandle(Request request, Response response) { - getCorsResponseHelper().addCorsResponseHeaders(request, response); - } - - /** - * Skip the call to the server resource if the - * {@link #skippingResourceForCorsOptions} is true and if the current request - * use the OPTIONS method and is a CORS request. - * - * @param request The request to handle. - * @param response The response to update. - * @return The continuation status. Either {@link #CONTINUE} or {@link #SKIP} or - * {@link #STOP}. - */ - @Override - protected int beforeHandle(Request request, Response response) { - if (skippingResourceForCorsOptions && Method.OPTIONS.equals(request.getMethod()) - && getCorsResponseHelper().isCorsRequest(request)) { - response.setAllowedMethods(getDefaultAllowedMethods()); - return Filter.SKIP; - } else { - return Filter.CONTINUE; - } - } - - /** - * Returns the modifiable set of headers allowed by the actual request on the - * current resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Headers" header. - * - * @return The set of headers allowed by the actual request on the current - * resource. - */ - public Set getAllowedHeaders() { - return allowedHeaders; - } - - /** - * Returns the URI an origin server allows for the requested resource. Use "*" - * as a wildcard character.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Origin" header. - * - * @return The origin allowed by the requested resource. - */ - public Set getAllowedOrigins() { - return allowedOrigins; - } - - /** - * Returns a lazy-initialized instance of - * {@link org.restlet.engine.application.CorsResponseHelper}. - */ - protected CorsResponseHelper getCorsResponseHelper() { - if (corsResponseHelper == null) { - corsResponseHelper = new CorsResponseHelper(); - corsResponseHelper.setAllowedCredentials(allowedCredentials); - corsResponseHelper.setAllowedOrigins(allowedOrigins); - corsResponseHelper.setAllowAllRequestedHeaders(allowAllRequestedHeaders); - corsResponseHelper.setAllowedHeaders(allowedHeaders); - corsResponseHelper.setExposedHeaders(exposedHeaders); - corsResponseHelper.setMaxAge(maxAge); - } - return corsResponseHelper; - } - - /** - * Returns the list of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. - * - * @return The list of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. - */ - public Set getDefaultAllowedMethods() { - return defaultAllowedMethods; - } - - /** - * Returns a modifiable whitelist of headers an origin server allows for the - * requested resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Expose-Headers" header. - * - * @return The set of headers an origin server allows for the requested - * resource. - */ - public Set getExposedHeaders() { - return exposedHeaders; - } - - /** - * Indicates how long (in seconds) the results of a preflight request can be - * cached in a preflight result cache.
- * In case of a negative value, the results of a preflight request is not meant - * to be cached.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Max-Age" header. - * - * @return Indicates how long the results of a preflight request can be cached - * in a preflight result cache. - */ - public int getMaxAge() { - return maxAge; - } - - /** - * If true, indicates that the value of 'Access-Control-Request-Headers' request - * header will be copied into the 'Access-Control-Allow-Headers' response - * header. If false, use {@link #allowedHeaders}. - */ - public boolean isAllowAllRequestedHeaders() { - return allowAllRequestedHeaders; - } - - /** - * If true, adds 'Access-Control-Allow-Credentials' header. - * - * @return True, if the 'Access-Control-Allow-Credentials' header will be added. - */ - public boolean isAllowedCredentials() { - return allowedCredentials; - } - - /** - * If true, the filter does not call the server resource for OPTIONS method of - * CORS request and set Access-Control-Allow-Methods header with - * {@link #defaultAllowedMethods}. Default is false. - * - * @return True if the filter does not call the server resource for OPTIONS - * method of CORS request. - */ - public boolean isSkippingResourceForCorsOptions() { - return skippingResourceForCorsOptions; - } - - /** - * If true, adds 'Access-Control-Allow-Credentials' header. - * - * @param allowedCredentials True to add the 'Access-Control-Allow-Credentials' - * header. - * @return Itself for chaining methods calls. - */ - public CorsFilter setAllowedCredentials(boolean allowedCredentials) { - this.allowedCredentials = allowedCredentials; - return this; - } - - /** - * Sets the value of the 'Access-Control-Allow-Headers' response header. Used - * only if {@link #allowAllRequestedHeaders} is false. - * - * @param allowedHeaders The value of 'Access-Control-Allow-Headers' response - * header. - * @return Itself for chaining methods calls. - */ - public CorsFilter setAllowedHeaders(Set allowedHeaders) { - this.allowedHeaders = allowedHeaders; - return this; - } - - /** - * Sets the value of 'Access-Control-Allow-Origin' header. - * - * @param allowedOrigins The value of 'Access-Control-Allow-Origin' header. - * @return Itself for chaining methods calls. - */ - public CorsFilter setAllowedOrigins(Set allowedOrigins) { - this.allowedOrigins = allowedOrigins; - return this; - } - - /** - * If true, copies the value of 'Access-Control-Request-Headers' request header - * into the 'Access-Control-Allow-Headers' response header. If false, use - * {@link #allowedHeaders}. - * - * @param allowingAllRequestedHeaders True to copy the value of - * 'Access-Control-Request-Headers' request - * header into the - * 'Access-Control-Allow-Headers' response - * header. If false, use - * {@link #allowedHeaders}. - * @return Itself for chaining methods calls. - */ - public CorsFilter setAllowingAllRequestedHeaders(boolean allowingAllRequestedHeaders) { - this.allowAllRequestedHeaders = allowingAllRequestedHeaders; - return this; - } - - /** - * Sets the list of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. - * - * @param defaultAllowedMethods The list of methods allowed by default, used - * when {@link #skippingResourceForCorsOptions} is - * turned on. - * @return Itself for chaining methods calls. - */ - public CorsFilter setDefaultAllowedMethods(Set defaultAllowedMethods) { - this.defaultAllowedMethods = defaultAllowedMethods; - return this; - } - - /** - * Sets the value of 'Access-Control-Expose-Headers' response header. - * - * @param exposedHeaders The value of 'Access-Control-Expose-Headers' response - * header. - * @return Itself for chaining methods calls. - */ - public CorsFilter setExposedHeaders(Set exposedHeaders) { - this.exposedHeaders = exposedHeaders; - return this; - } - - /** - * Sets the value of 'Access-Control-Max-Age' response header.
- * In case of negative value, the header is not set. - * - * @param maxAge The value of 'Access-Control-Max-Age' response header. - */ - public CorsFilter setMaxAge(int maxAge) { - this.maxAge = maxAge; - return this; - } - - /** - * Sets the value of skipResourceForCorsOptions field. - * - * @param skipResourceForCorsOptions True if the filter does not call the server - * resource for OPTIONS method of CORS - * request. - * @return Itself for chaining methods calls. - */ - public CorsFilter setSkippingResourceForCorsOptions(boolean skipResourceForCorsOptions) { - this.skippingResourceForCorsOptions = skipResourceForCorsOptions; - return this; - } + /** + * If true, copies the value of 'Access-Control-Request-Headers' request header into the + * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. + * Default is true. + */ + public boolean allowAllRequestedHeaders = true; + + /** If true, add an 'Access-Control-Allow-Credentials' header. Default is false. */ + private boolean allowedCredentials = false; + + /** + * The value of 'Access-Control-Allow-Headers' response header. Used only if {@link + * #allowAllRequestedHeaders} is false. + */ + private Set allowedHeaders = null; + + /** The value of the 'Access-Control-Allow-Origin' header. Default is '*'. */ + private Set allowedOrigins = SetUtils.newHashSet("*"); + + /** Helper for generating CORS response. */ + private CorsResponseHelper corsResponseHelper; + + /** + * The set of methods allowed by default, used when {@link #skippingResourceForCorsOptions} is + * turned on. Default methods: GET, PUT, POST, DELETE, PATCH. + */ + private Set defaultAllowedMethods = + new HashSet<>( + List.of(Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); + + /** The value of 'Access-Control-Expose-Headers' response header. */ + private Set exposedHeaders = null; + + /** + * The value of the 'Access-Control-Max-Age' response header. By default, the header is not set. + */ + private int maxAge = -1; + + /** + * If true, the filter does not call the server resource for OPTIONS method of CORS request and + * set Access-Control-Allow-Methods header with {@link #defaultAllowedMethods}. Default is + * false. + */ + private boolean skippingResourceForCorsOptions = false; + + /** Constructor. */ + public CorsFilter() { + this(null); + } + + /** + * Constructor. + * + * @param context The context. + */ + public CorsFilter(Context context) { + super(context, null); + } + + /** + * Constructor. + * + * @param context The context. + * @param next The next Restlet. + */ + public CorsFilter(Context context, Restlet next) { + super(context, next); + } + + /** + * Add CORS headers to response + * + * @param request The request to handle. + * @param response The response + */ + @Override + protected void afterHandle(Request request, Response response) { + getCorsResponseHelper().addCorsResponseHeaders(request, response); + } + + /** + * Skip the call to the server resource if the {@link #skippingResourceForCorsOptions} is true + * and if the current request use the OPTIONS method and is a CORS request. + * + * @param request The request to handle. + * @param response The response to update. + * @return The continuation status. Either {@link #CONTINUE} or {@link #SKIP} or {@link #STOP}. + */ + @Override + protected int beforeHandle(Request request, Response response) { + if (skippingResourceForCorsOptions + && Method.OPTIONS.equals(request.getMethod()) + && getCorsResponseHelper().isCorsRequest(request)) { + response.setAllowedMethods(getDefaultAllowedMethods()); + return Filter.SKIP; + } else { + return Filter.CONTINUE; + } + } + + /** + * Returns the modifiable set of headers allowed by the actual request on the current resource. + *
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Headers" header. + * + * @return The set of headers allowed by the actual request on the current resource. + */ + public Set getAllowedHeaders() { + return allowedHeaders; + } + + /** + * Returns the URI an origin server allows for the requested resource. Use "*" as a wildcard + * character.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Origin" header. + * + * @return The origin allowed by the requested resource. + */ + public Set getAllowedOrigins() { + return allowedOrigins; + } + + /** + * Returns a lazy-initialized instance of {@link + * org.restlet.engine.application.CorsResponseHelper}. + */ + protected CorsResponseHelper getCorsResponseHelper() { + if (corsResponseHelper == null) { + corsResponseHelper = new CorsResponseHelper(); + corsResponseHelper.setAllowedCredentials(allowedCredentials); + corsResponseHelper.setAllowedOrigins(allowedOrigins); + corsResponseHelper.setAllowAllRequestedHeaders(allowAllRequestedHeaders); + corsResponseHelper.setAllowedHeaders(allowedHeaders); + corsResponseHelper.setExposedHeaders(exposedHeaders); + corsResponseHelper.setMaxAge(maxAge); + } + return corsResponseHelper; + } + + /** + * Returns the list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + * + * @return The list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + */ + public Set getDefaultAllowedMethods() { + return defaultAllowedMethods; + } + + /** + * Returns a modifiable whitelist of headers an origin server allows for the requested resource. + *
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Expose-Headers" header. + * + * @return The set of headers an origin server allows for the requested resource. + */ + public Set getExposedHeaders() { + return exposedHeaders; + } + + /** + * Indicates how long (in seconds) the results of a preflight request can be cached in a + * preflight result cache.
+ * In case of a negative value, the results of a preflight request are not meant to be cached. + *
+ * Note that when used with HTTP connectors, this property maps to the "Access-Control-Max-Age" + * header. + * + * @return Indicates how long the results of a preflight request can be cached in a preflight + * result cache. + */ + public int getMaxAge() { + return maxAge; + } + + /** + * If true, indicates that the value of 'Access-Control-Request-Headers' request header will be + * copied into the 'Access-Control-Allow-Headers' response header. If false, use {@link + * #allowedHeaders}. + */ + public boolean isAllowAllRequestedHeaders() { + return allowAllRequestedHeaders; + } + + /** + * If true, adds 'Access-Control-Allow-Credentials' header. + * + * @return True, if the 'Access-Control-Allow-Credentials' header will be added. + */ + public boolean isAllowedCredentials() { + return allowedCredentials; + } + + /** + * If true, the filter does not call the server resource for OPTIONS method of CORS request and + * set Access-Control-Allow-Methods header with {@link #defaultAllowedMethods}. Default is + * false. + * + * @return True if the filter does not call the server resource for OPTIONS method of CORS + * request. + */ + public boolean isSkippingResourceForCorsOptions() { + return skippingResourceForCorsOptions; + } + + /** + * If true, adds 'Access-Control-Allow-Credentials' header. + * + * @param allowedCredentials True to add the 'Access-Control-Allow-Credentials' header. + * @return Itself for chaining methods calls. + */ + public CorsFilter setAllowedCredentials(boolean allowedCredentials) { + this.allowedCredentials = allowedCredentials; + return this; + } + + /** + * Sets the value of the 'Access-Control-Allow-Headers' response header. Used only if {@link + * #allowAllRequestedHeaders} is false. + * + * @param allowedHeaders The value of 'Access-Control-Allow-Headers' response header. + * @return Itself for chaining methods calls. + */ + public CorsFilter setAllowedHeaders(Set allowedHeaders) { + this.allowedHeaders = allowedHeaders; + return this; + } + + /** + * Sets the value of the 'Access-Control-Allow-Origin' header. + * + * @param allowedOrigins The value of the 'Access-Control-Allow-Origin' header. + * @return Itself for chaining methods calls. + */ + public CorsFilter setAllowedOrigins(Set allowedOrigins) { + this.allowedOrigins = allowedOrigins; + return this; + } + + /** + * If true, copies the value of 'Access-Control-Request-Headers' request header into the + * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. + * + * @param allowingAllRequestedHeaders True to copy the value of 'Access-Control-Request-Headers' + * request header into the 'Access-Control-Allow-Headers' response header. If false, use + * {@link #allowedHeaders}. + * @return Itself for chaining methods calls. + */ + public CorsFilter setAllowingAllRequestedHeaders(boolean allowingAllRequestedHeaders) { + this.allowAllRequestedHeaders = allowingAllRequestedHeaders; + return this; + } + + /** + * Sets the list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + * + * @param defaultAllowedMethods The list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + * @return Itself for chaining methods calls. + */ + public CorsFilter setDefaultAllowedMethods(Set defaultAllowedMethods) { + this.defaultAllowedMethods = defaultAllowedMethods; + return this; + } + + /** + * Sets the value of 'Access-Control-Expose-Headers' response header. + * + * @param exposedHeaders The value of 'Access-Control-Expose-Headers' response header. + * @return Itself for chaining methods calls. + */ + public CorsFilter setExposedHeaders(Set exposedHeaders) { + this.exposedHeaders = exposedHeaders; + return this; + } + + /** + * Sets the value of the 'Access-Control-Max-Age' response header.
+ * In the case of negative value, the header is not set. + * + * @param maxAge The value of the 'Access-Control-Max-Age' response header. + */ + public CorsFilter setMaxAge(int maxAge) { + this.maxAge = maxAge; + return this; + } + + /** + * Sets the value of the skipResourceForCorsOptions field. + * + * @param skipResourceForCorsOptions True if the filter does not call the server resource for + * OPTIONS method of CORS request. + * @return Itself for chaining methods calls. + */ + public CorsFilter setSkippingResourceForCorsOptions(boolean skipResourceForCorsOptions) { + this.skippingResourceForCorsOptions = skipResourceForCorsOptions; + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java index e293396a8f..40c6dc375f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -16,331 +18,314 @@ import org.restlet.data.Status; import org.restlet.engine.util.SetUtils; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Logger; - /** * Helps to generate response CORS headers.
- * The CORS specification defines a subset of methods qualified as simple HEAD, - * GET and POST. Any other methods should send a preflight request with the - * method OPTIONS. - * + * The CORS specification defines a subset of methods qualified as simple HEAD, GET and POST. Any + * other methods should send a preflight request with the method OPTIONS. + * * @see W3C CORS Specification * @see Simple methods - * * @author Manuel Boillod */ public class CorsResponseHelper { - private static Logger LOGGER = Context.getCurrentLogger(); - - /** - * If true, copies the value of 'Access-Control-Request-Headers' request header - * into the 'Access-Control-Allow-Headers' response header. If false, use - * {@link #allowedHeaders}. Default is true. - */ - public boolean allowAllRequestedHeaders = true; - - /** - * If true, add 'Access-Control-Allow-Credentials' header. Default is false. - */ - public boolean allowedCredentials = false; - - /** - * The value of 'Access-Control-Allow-Headers' response header. Used only if - * {@link #allowAllRequestedHeaders} is false. - */ - public Set allowedHeaders = null; - - /** The value of 'Access-Control-Allow-Origin' header. Default is '*'. */ - public Set allowedOrigins = SetUtils.newHashSet("*"); - - /** The value of 'Access-Control-Expose-Headers' response header. */ - public Set exposedHeaders = null; - - /** - * The value of 'Access-Control-Max-Age' response header. Default is that the - * header is not set. - */ - public int maxAge = -1; - - /** - * Adds CORS headers to the given response. - * - * @param request The current request. - * @param response The response. - */ - public void addCorsResponseHeaders(Request request, Response response) { - - String origin = request.getHeaders().getFirstValue("Origin", true); - - if (origin == null) { - // Not a CORS request - return; - } - - Set allowedMethods = new HashSet<>(response.getAllowedMethods()); - // Header 'Allow' is not relevant in CORS request. - response.getAllowedMethods().clear(); - - if (!allowedOrigins.contains("*") && !allowedOrigins.contains(origin)) { - // Origin not allowed - LOGGER.fine("Origin " + origin + " not allowed for CORS request"); - return; - } - - boolean isPreflightRequest = Method.OPTIONS.equals(request.getMethod()); - - if (isPreflightRequest) { - - // Default OPTIONS method in a server resource returns a - // {@link Status#CLIENT_ERROR_METHOD_NOT_ALLOWED} if the method is - // not implemented - // or a {@link Status#SUCCESS_NO_CONTENT} or a {@link - // Status#SUCCESS_NO_CONTENT} if - // the method is implemented and the call succeed. - // Other status are considered as error. - - // Preflight request returns a 200 status except if server resource - // method . - // If the preflight request is not allowed, CORS response headers - // will not be added. - if (Status.SUCCESS_OK.equals(response.getStatus()) || Status.SUCCESS_NO_CONTENT.equals(response.getStatus()) - || Status.CLIENT_ERROR_METHOD_NOT_ALLOWED.equals(response.getStatus())) { - response.setStatus(Status.SUCCESS_OK); - } else { - LOGGER.fine("The CORS preflight request failed in server resource."); - return; - } - - Method requestedMethod = request.getAccessControlRequestMethod(); - if (requestedMethod == null) { - // Requested Method is required - LOGGER.fine("A CORS preflight request should specified header 'Access-Control-Request-Method'"); - return; - } - - if (!allowedMethods.contains(requestedMethod)) { - // Method not allowed - LOGGER.fine( - "The CORS preflight request ask for methods not allowed in header 'Access-Control-Request-Method'"); - return; - } - - Set requestedHeaders = request.getAccessControlRequestHeaders(); - if (requestedHeaders == null) { - requestedHeaders = SetUtils.newHashSet(); - } - - if (!allowAllRequestedHeaders - && (allowedHeaders == null || !isAllHeadersAllowed(allowedHeaders, requestedHeaders))) { - // Headers not allowed - LOGGER.fine( - "The CORS preflight request ask for headers not allowed in header 'Access-Control-Request-Headers'"); - return; - } - - // Header 'Access-Control-Allow-Methods' - response.setAccessControlAllowMethods(allowedMethods); - - // Header 'Access-Control-Allow-Headers' - response.setAccessControlAllowHeaders(requestedHeaders); - - if (getMaxAge() > 0) { - response.setAccessControlMaxAge(getMaxAge()); - } - } else { - // simple request - - // Header 'Access-Control-Expose-Headers' - if (exposedHeaders != null && !exposedHeaders.isEmpty()) { - response.setAccessControlExposeHeaders(exposedHeaders); - } - } - - // Header 'Access-Control-Allow-Credentials' - if (allowedCredentials) { - response.setAccessControlAllowCredentials(true); - } - - // Header 'Access-Control-Allow-Origin' - if (!allowedCredentials && allowedOrigins.contains("*")) { - response.setAccessControlAllowOrigin("*"); - } else { - response.setAccessControlAllowOrigin(origin); - } - } - - /** - * Returns the modifiable set of headers allowed by the actual request on the - * current resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Headers" header. - * - * @return The set of headers allowed by the actual request on the current - * resource. - */ - public Set getAllowedHeaders() { - return allowedHeaders; - } - - /** - * Returns the URI an origin server allows for the requested resource. Use "*" - * as a wildcard character.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Origin" header. - * - * @return The origin allowed by the requested resource. - */ - public Set getAllowedOrigins() { - return allowedOrigins; - } - - /** - * Returns a modifiable whitelist of headers an origin server allows for the - * requested resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Expose-Headers" header. - * - * @return The set of headers an origin server allows for the requested - * resource. - */ - public Set getExposedHeaders() { - return exposedHeaders; - } - - /** - * Indicates how long (in seconds) the results of a preflight request can be - * cached in a preflight result cache.
- * In case of a negative value, the results of a preflight request is not meant - * to be cached.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Max-Age" header. - * - * @return Indicates how long the results of a preflight request can be cached - * in a preflight result cache. - */ - public int getMaxAge() { - return maxAge; - } - - /** - * Returns true if all requested headers are allowed (case-insensitive). - * - * @param allowHeaders The allowed headers. - * @param requestedHeaders The requested headers. - * @return True if all requested headers are allowed (case-insensitive). - */ - private boolean isAllHeadersAllowed(Set allowHeaders, Set requestedHeaders) { - for (String requestedHeader : requestedHeaders) { - boolean headerAllowed = false; - for (String allowHeader : allowHeaders) { - if (allowHeader.equalsIgnoreCase(requestedHeader)) { - headerAllowed = true; - break; - } - } - if (!headerAllowed) { - return false; - } - } - return true; - } - - /** - * If true, indicates that the value of 'Access-Control-Request-Headers' request - * header will be copied into the 'Access-Control-Allow-Headers' response - * header. If false, use {@link #allowedHeaders}. - */ - public boolean isAllowAllRequestedHeaders() { - return allowAllRequestedHeaders; - } - - /** - * If true, adds 'Access-Control-Allow-Credentials' header. - * - * @return True, if the 'Access-Control-Allow-Credentials' header will be added. - */ - public boolean isAllowedCredentials() { - return allowedCredentials; - } - - /** - * Returns true if the request is a CORS request. - * - * @param request The current request. - * @return true if the request is a CORS request. - */ - public boolean isCorsRequest(Request request) { - return request.getHeaders().getFirstValue("Origin", true) != null; - } - - /** - * If true, copies the value of 'Access-Control-Request-Headers' request header - * into the 'Access-Control-Allow-Headers' response header. If false, use - * {@link #allowedHeaders}. - * - * @param allowAllRequestedHeaders True to copy the value of - * 'Access-Control-Request-Headers' request - * header into the - * 'Access-Control-Allow-Headers' response - * header. If false, use - * {@link #allowedHeaders}. - */ - public void setAllowAllRequestedHeaders(boolean allowAllRequestedHeaders) { - this.allowAllRequestedHeaders = allowAllRequestedHeaders; - } - - /** - * If true, adds 'Access-Control-Allow-Credentials' header. - * - * @param allowedCredentials True to add the 'Access-Control-Allow-Credentials' - * header. - */ - public void setAllowedCredentials(boolean allowedCredentials) { - this.allowedCredentials = allowedCredentials; - } - - /** - * Sets the value of the 'Access-Control-Allow-Headers' response header. Used - * only if {@link #allowAllRequestedHeaders} is false. - * - * @param allowedHeaders The value of 'Access-Control-Allow-Headers' response - * header. - */ - public void setAllowedHeaders(Set allowedHeaders) { - this.allowedHeaders = allowedHeaders; - } - - /** - * Sets the value of 'Access-Control-Allow-Origin' header. - * - * @param allowedOrigins The value of 'Access-Control-Allow-Origin' header. - */ - public void setAllowedOrigins(Set allowedOrigins) { - this.allowedOrigins = allowedOrigins; - } - - /** - * Sets the value of 'Access-Control-Expose-Headers' response header. - * - * @param exposedHeaders The value of 'Access-Control-Expose-Headers' response - * header. - */ - public void setExposedHeaders(Set exposedHeaders) { - this.exposedHeaders = exposedHeaders; - } - - /** - * Sets the value of 'Access-Control-Max-Age' response header.
- * In case of negative value, the header is not set. - * - * @param maxAge The value of 'Access-Control-Max-Age' response header. - */ - public void setMaxAge(int maxAge) { - this.maxAge = maxAge; - - } - + private static final Logger LOGGER = Context.getCurrentLogger(); + + /** + * If true, copies the value of 'Access-Control-Request-Headers' request header into the + * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. + * Default is true. + */ + public boolean allowAllRequestedHeaders = true; + + /** If true, add an 'Access-Control-Allow-Credentials' header. Default is false. */ + public boolean allowedCredentials = false; + + /** + * The value of 'Access-Control-Allow-Headers' response header. Used only if {@link + * #allowAllRequestedHeaders} is false. + */ + public Set allowedHeaders = null; + + /** The value of the 'Access-Control-Allow-Origin' header. Default is '*'. */ + public Set allowedOrigins = SetUtils.newHashSet("*"); + + /** The value of 'Access-Control-Expose-Headers' response header. */ + public Set exposedHeaders = null; + + /** + * The value of the 'Access-Control-Max-Age' response header. By default, the header is not set. + */ + public int maxAge = -1; + + /** + * Adds CORS headers to the given response. + * + * @param request The current request. + * @param response The response. + */ + public void addCorsResponseHeaders(Request request, Response response) { + + String origin = request.getHeaders().getFirstValue("Origin", true); + + if (origin == null) { + // Not a CORS request + return; + } + + Set allowedMethods = new HashSet<>(response.getAllowedMethods()); + // Header 'Allow' is not relevant in a CORS request. + response.getAllowedMethods().clear(); + + if (!allowedOrigins.contains("*") && !allowedOrigins.contains(origin)) { + // Origin isn't allowed + LOGGER.fine("Origin " + origin + " not allowed for CORS request"); + return; + } + + boolean isPreflightRequest = Method.OPTIONS.equals(request.getMethod()); + + if (isPreflightRequest) { + + // Default OPTIONS method in a server resource returns a + // {@link Status#CLIENT_ERROR_METHOD_NOT_ALLOWED} if the method is + // not implemented + // or a {@link Status#SUCCESS_NO_CONTENT} or a {@link + // Status#SUCCESS_NO_CONTENT} if + // the method is implemented and the call succeeds. + // Other status is considered as an error. + + // Preflight request returns a 200 status except if the server resource + // method is not allowed. + // If the preflight request is not allowed, CORS response headers + // will not be added. + if (Status.SUCCESS_OK.equals(response.getStatus()) + || Status.SUCCESS_NO_CONTENT.equals(response.getStatus()) + || Status.CLIENT_ERROR_METHOD_NOT_ALLOWED.equals(response.getStatus())) { + response.setStatus(Status.SUCCESS_OK); + } else { + LOGGER.fine("The CORS preflight request failed in server resource."); + return; + } + + Method requestedMethod = request.getAccessControlRequestMethod(); + if (requestedMethod == null) { + // Requested Method is required + LOGGER.fine( + "A CORS preflight request should specified header 'Access-Control-Request-Method'"); + return; + } + + if (!allowedMethods.contains(requestedMethod)) { + // Method isn't allowed + LOGGER.fine( + "The CORS preflight request ask for methods not allowed in header 'Access-Control-Request-Method'"); + return; + } + + Set requestedHeaders = request.getAccessControlRequestHeaders(); + if (requestedHeaders == null) { + requestedHeaders = SetUtils.newHashSet(); + } + + if (!allowAllRequestedHeaders + && (allowedHeaders == null + || !isAllHeadersAllowed(allowedHeaders, requestedHeaders))) { + // Headers aren't allowed + LOGGER.fine( + "The CORS preflight request ask for headers not allowed in header 'Access-Control-Request-Headers'"); + return; + } + + // Header 'Access-Control-Allow-Methods' + response.setAccessControlAllowMethods(allowedMethods); + + // Header 'Access-Control-Allow-Headers' + response.setAccessControlAllowHeaders(requestedHeaders); + + if (getMaxAge() > 0) { + response.setAccessControlMaxAge(getMaxAge()); + } + } else { + // simple request + + // Header 'Access-Control-Expose-Headers' + if (exposedHeaders != null && !exposedHeaders.isEmpty()) { + response.setAccessControlExposeHeaders(exposedHeaders); + } + } + + // Header 'Access-Control-Allow-Credentials' + if (allowedCredentials) { + response.setAccessControlAllowCredentials(true); + } + + // Header 'Access-Control-Allow-Origin' + if (!allowedCredentials && allowedOrigins.contains("*")) { + response.setAccessControlAllowOrigin("*"); + } else { + response.setAccessControlAllowOrigin(origin); + } + } + + /** + * Returns the modifiable set of headers allowed by the actual request on the current resource. + *
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Headers" header. + * + * @return The set of headers allowed by the actual request on the current resource. + */ + public Set getAllowedHeaders() { + return allowedHeaders; + } + + /** + * Returns the URI an origin server allows for the requested resource. Use "*" as a wildcard + * character.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Origin" header. + * + * @return The origin allowed by the requested resource. + */ + public Set getAllowedOrigins() { + return allowedOrigins; + } + + /** + * Returns a modifiable whitelist of headers an origin server allows for the requested resource. + *
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Expose-Headers" header. + * + * @return The set of headers an origin server allows for the requested resource. + */ + public Set getExposedHeaders() { + return exposedHeaders; + } + + /** + * Indicates how long (in seconds) the results of a preflight request can be cached in a + * preflight result cache.
+ * In case of a negative value, the results of a preflight request are not meant to be cached. + *
+ * Note that when used with HTTP connectors, this property maps to the "Access-Control-Max-Age" + * header. + * + * @return Indicates how long the results of a preflight request can be cached in a preflight + * result cache. + */ + public int getMaxAge() { + return maxAge; + } + + /** + * Returns true if all requested headers are allowed (case-insensitive). + * + * @param allowHeaders The allowed headers. + * @param requestedHeaders The requested headers. + * @return True if all requested headers are allowed (case-insensitive). + */ + private boolean isAllHeadersAllowed(Set allowHeaders, Set requestedHeaders) { + for (String requestedHeader : requestedHeaders) { + boolean headerAllowed = false; + for (String allowHeader : allowHeaders) { + if (allowHeader.equalsIgnoreCase(requestedHeader)) { + headerAllowed = true; + break; + } + } + if (!headerAllowed) { + return false; + } + } + return true; + } + + /** + * If true, indicates that the value of 'Access-Control-Request-Headers' request header will be + * copied into the 'Access-Control-Allow-Headers' response header. If false, use {@link + * #allowedHeaders}. + */ + public boolean isAllowAllRequestedHeaders() { + return allowAllRequestedHeaders; + } + + /** + * If true, adds 'Access-Control-Allow-Credentials' header. + * + * @return True, if the 'Access-Control-Allow-Credentials' header will be added. + */ + public boolean isAllowedCredentials() { + return allowedCredentials; + } + + /** + * Returns true if the request is a CORS request. + * + * @param request The current request. + * @return true if the request is a CORS request. + */ + public boolean isCorsRequest(Request request) { + return request.getHeaders().getFirstValue("Origin", true) != null; + } + + /** + * If true, copies the value of 'Access-Control-Request-Headers' request header into the + * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. + * + * @param allowAllRequestedHeaders True to copy the value of 'Access-Control-Request-Headers' + * request header into the 'Access-Control-Allow-Headers' response header. If false, use + * {@link #allowedHeaders}. + */ + public void setAllowAllRequestedHeaders(boolean allowAllRequestedHeaders) { + this.allowAllRequestedHeaders = allowAllRequestedHeaders; + } + + /** + * If true, adds 'Access-Control-Allow-Credentials' header. + * + * @param allowedCredentials True to add the 'Access-Control-Allow-Credentials' header. + */ + public void setAllowedCredentials(boolean allowedCredentials) { + this.allowedCredentials = allowedCredentials; + } + + /** + * Sets the value of the 'Access-Control-Allow-Headers' response header. Used only if {@link + * #allowAllRequestedHeaders} is false. + * + * @param allowedHeaders The value of 'Access-Control-Allow-Headers' response header. + */ + public void setAllowedHeaders(Set allowedHeaders) { + this.allowedHeaders = allowedHeaders; + } + + /** + * Sets the value of the 'Access-Control-Allow-Origin' header. + * + * @param allowedOrigins The value of the 'Access-Control-Allow-Origin' header. + */ + public void setAllowedOrigins(Set allowedOrigins) { + this.allowedOrigins = allowedOrigins; + } + + /** + * Sets the value of 'Access-Control-Expose-Headers' response header. + * + * @param exposedHeaders The value of 'Access-Control-Expose-Headers' response header. + */ + public void setExposedHeaders(Set exposedHeaders) { + this.exposedHeaders = exposedHeaders; + } + + /** + * Sets the value of the 'Access-Control-Max-Age' response header.
+ * In the case of negative value, the header is not set. + * + * @param maxAge The value of the 'Access-Control-Max-Age' response header. + */ + public void setMaxAge(int maxAge) { + this.maxAge = maxAge; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java index 7bd3384559..1ce956dda6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java @@ -1,19 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; -import org.restlet.data.Encoding; -import org.restlet.engine.io.IoUtils; -import org.restlet.representation.Representation; -import org.restlet.util.WrapperRepresentation; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -27,182 +21,193 @@ import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import java.util.zip.ZipInputStream; +import org.restlet.data.Encoding; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.Representation; +import org.restlet.util.WrapperRepresentation; /** - * Representation that decodes a wrapped representation if its encoding is - * supported. If at least one encoding of the wrapped representation is not - * supported, then the wrapped representation is not decoded. - * + * Representation that decodes a wrapped representation if its encoding is supported. If at least + * one encoding of the wrapped representation is not supported, then the wrapped representation is + * not decoded. + * * @author Jerome Louvel */ public class DecodeRepresentation extends WrapperRepresentation { - /** - * Returns the list of supported encodings. - * - * @return The list of supported encodings. - */ - public static List getSupportedEncodings() { - return Arrays.asList(Encoding.GZIP, Encoding.DEFLATE, Encoding.DEFLATE_NOWRAP, Encoding.ZIP, - Encoding.IDENTITY); - } - - /** Indicates if the decoding can happen. */ - private volatile boolean decoding; - - /** List of encodings still applied to the decodeRepresentation */ - private final List wrappedEncodings; - - /** - * Constructor. - * - * @param wrappedRepresentation The wrapped representation. - */ - public DecodeRepresentation(Representation wrappedRepresentation) { - super(wrappedRepresentation); - this.decoding = getSupportedEncodings().containsAll(wrappedRepresentation.getEncodings()); - this.wrappedEncodings = new CopyOnWriteArrayList(wrappedRepresentation.getEncodings()); - } - - @Override - public long getAvailableSize() { - return IoUtils.getAvailableSize(this); - } - - /** - * Returns a decoded stream for a given encoding and coded stream. - * - * @param encoding The encoding to use. - * @param encodedStream The encoded stream. - * @return The decoded stream. - * @throws IOException - */ - private InputStream getDecodedStream(Encoding encoding, InputStream encodedStream) throws IOException { - InputStream result = null; - - if (encodedStream != null) { - if (encoding.equals(Encoding.GZIP)) { - result = new GZIPInputStream(encodedStream); - } else if (encoding.equals(Encoding.DEFLATE)) { - result = new InflaterInputStream(encodedStream); - } else if (encoding.equals(Encoding.DEFLATE_NOWRAP)) { - result = new InflaterInputStream(encodedStream, new Inflater(true)); - } else if (encoding.equals(Encoding.ZIP)) { - final ZipInputStream stream = new ZipInputStream(encodedStream); - if (stream.getNextEntry() != null) { - result = stream; - } - } else if (encoding.equals(Encoding.IDENTITY)) { - throw new IOException("Decoder unecessary for identity decoding"); - } - } - - return result; - } - - /** - * Returns the encodings applied to the entity. - * - * @return The encodings applied to the entity. - */ - @Override - public List getEncodings() { - if (isDecoding()) { - return new ArrayList(); - } else { - return this.wrappedEncodings; - } - } - - @Override - public Reader getReader() throws IOException { - if (isDecoding()) { - return IoUtils.getReader(getStream(), getCharacterSet()); - } else { - return getWrappedRepresentation().getReader(); - } - } - - /** - * Returns the size in bytes of the decoded representation if known, - * UNKNOWN_SIZE (-1) otherwise. - * - * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise. - */ - @Override - public long getSize() { - long result = UNKNOWN_SIZE; - - if (isDecoding()) { - boolean identity = true; - for (final Iterator iter = this.wrappedEncodings.iterator(); identity && iter.hasNext();) { - identity = (iter.next().equals(Encoding.IDENTITY)); - } - if (identity) { - result = getWrappedRepresentation().getSize(); - } - } else { - result = getWrappedRepresentation().getSize(); - } - - return result; - } - - /** - * Returns a stream with the representation's content. - * - * @return A stream with the representation's content. - */ - @Override - public InputStream getStream() throws IOException { - InputStream result = getWrappedRepresentation().getStream(); - - if (isDecoding()) { - for (int i = this.wrappedEncodings.size() - 1; i >= 0; i--) { - if (!this.wrappedEncodings.get(i).equals(Encoding.IDENTITY)) { - result = getDecodedStream(this.wrappedEncodings.get(i), result); - } - } - } - - return result; - } - - /** - * Converts the representation to a string value. Be careful when using this - * method as the conversion of large content to a string fully stored in memory - * can result in OutOfMemoryErrors being thrown. - * - * @return The representation as a string value. - */ - @Override - public String getText() throws IOException { - if (isDecoding()) { - return IoUtils.toString(getStream(), getCharacterSet()); - } else { - return getWrappedRepresentation().getText(); - } - } - - /** - * Indicates if the decoding can happen. - * - * @return True if the decoding can happen. - */ - public boolean isDecoding() { - return this.decoding; - } - - /** - * Writes the representation to a byte stream. - * - * @param outputStream The output stream. - */ - @Override - public void write(OutputStream outputStream) throws IOException { - if (isDecoding()) { - IoUtils.copy(getStream(), outputStream); - } else { - getWrappedRepresentation().write(outputStream); - } - } + /** + * Returns the list of supported encodings. + * + * @return The list of supported encodings. + */ + public static List getSupportedEncodings() { + return Arrays.asList( + Encoding.GZIP, + Encoding.DEFLATE, + Encoding.DEFLATE_NOWRAP, + Encoding.ZIP, + Encoding.IDENTITY); + } + + /** Indicates if the decoding can happen. */ + private volatile boolean decoding; + + /** List of encodings still applied to the decodeRepresentation */ + private final List wrappedEncodings; + + /** + * Constructor. + * + * @param wrappedRepresentation The wrapped representation. + */ + public DecodeRepresentation(Representation wrappedRepresentation) { + super(wrappedRepresentation); + this.decoding = getSupportedEncodings().containsAll(wrappedRepresentation.getEncodings()); + this.wrappedEncodings = + new CopyOnWriteArrayList(wrappedRepresentation.getEncodings()); + } + + @Override + public long getAvailableSize() { + return IoUtils.getAvailableSize(this); + } + + /** + * Returns a decoded stream for a given encoding and coded stream. + * + * @param encoding The encoding to use. + * @param encodedStream The encoded stream. + * @return The decoded stream. + * @throws IOException + */ + private InputStream getDecodedStream(Encoding encoding, InputStream encodedStream) + throws IOException { + InputStream result = null; + + if (encodedStream != null) { + if (encoding.equals(Encoding.GZIP)) { + result = new GZIPInputStream(encodedStream); + } else if (encoding.equals(Encoding.DEFLATE)) { + result = new InflaterInputStream(encodedStream); + } else if (encoding.equals(Encoding.DEFLATE_NOWRAP)) { + result = new InflaterInputStream(encodedStream, new Inflater(true)); + } else if (encoding.equals(Encoding.ZIP)) { + final ZipInputStream stream = new ZipInputStream(encodedStream); + if (stream.getNextEntry() != null) { + result = stream; + } + } else if (encoding.equals(Encoding.IDENTITY)) { + throw new IOException("Decoder unnecessary for identity decoding"); + } + } + + return result; + } + + /** + * Returns the encodings applied to the entity. + * + * @return The encodings applied to the entity. + */ + @Override + public List getEncodings() { + if (isDecoding()) { + return new ArrayList<>(); + } else { + return this.wrappedEncodings; + } + } + + @Override + public Reader getReader() throws IOException { + if (isDecoding()) { + return IoUtils.getReader(getStream(), getCharacterSet()); + } else { + return getWrappedRepresentation().getReader(); + } + } + + /** + * Returns the size in bytes of the decoded representation if known, UNKNOWN_SIZE (-1) + * otherwise. + * + * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise. + */ + @Override + public long getSize() { + long result = UNKNOWN_SIZE; + + if (isDecoding()) { + boolean identity = true; + for (final Iterator iter = this.wrappedEncodings.iterator(); + identity && iter.hasNext(); ) { + identity = (iter.next().equals(Encoding.IDENTITY)); + } + if (identity) { + result = getWrappedRepresentation().getSize(); + } + } else { + result = getWrappedRepresentation().getSize(); + } + + return result; + } + + /** + * Returns a stream with the representation's content. + * + * @return A stream with the representation's content. + */ + @Override + public InputStream getStream() throws IOException { + InputStream result = getWrappedRepresentation().getStream(); + + if (isDecoding()) { + for (int i = this.wrappedEncodings.size() - 1; i >= 0; i--) { + if (!this.wrappedEncodings.get(i).equals(Encoding.IDENTITY)) { + result = getDecodedStream(this.wrappedEncodings.get(i), result); + } + } + } + + return result; + } + + /** + * Converts the representation to a string value. Be careful when using this method as the + * conversion of large content to a string fully stored in memory can result in + * OutOfMemoryErrors being thrown. + * + * @return The representation as a string value. + */ + @Override + public String getText() throws IOException { + if (isDecoding()) { + return IoUtils.toString(getStream(), getCharacterSet()); + } else { + return getWrappedRepresentation().getText(); + } + } + + /** + * Indicates if the decoding can happen. + * + * @return True if the decoding can happen. + */ + public boolean isDecoding() { + return this.decoding; + } + + /** + * Writes the representation to a byte stream. + * + * @param outputStream The output stream. + */ + @Override + public void write(OutputStream outputStream) throws IOException { + if (isDecoding()) { + IoUtils.copy(getStream(), outputStream); + } else { + getWrappedRepresentation().write(outputStream); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java b/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java index 126d7251e1..df577a18a1 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.util.Iterator; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -16,152 +16,146 @@ import org.restlet.representation.Representation; import org.restlet.routing.Filter; -import java.util.Iterator; - /** * Filter uncompressing entities. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class Decoder extends Filter { - /** - * Indicates if the request entity should be decoded. - */ - private final boolean decodingRequest; - - /** - * Indicates if the response entity should be decoded. - */ - private final boolean decodingResponse; - - /** - * Constructor to only decode request entities before handling. - * - * @param context The context. - */ - public Decoder(Context context) { - this(context, true, false); - } - - /** - * Constructor. - * - * @param context The context. - * @param decodingRequest Indicates if the request entity should be decoded. - * @param decodingResponse Indicates if the response entity should be decoded. - */ - public Decoder(Context context, boolean decodingRequest, boolean decodingResponse) { - super(context); - this.decodingRequest = decodingRequest; - this.decodingResponse = decodingResponse; - } - - /** - * Allows filtering after its handling by the target Restlet. Does nothing by - * default. - * - * @param request The request to filter. - * @param response The response to filter. - */ - @Override - public void afterHandle(Request request, Response response) { - // Check if decoding of the response entity is needed - if (isDecodingResponse() && canDecode(response.getEntity())) { - response.setEntity(decode(response.getEntity())); - } - } - - /** - * Allows filtering before its handling by the target Restlet. Does nothing by - * default. - * - * @param request The request to filter. - * @param response The response to filter. - * @return The continuation status. - */ - @Override - public int beforeHandle(Request request, Response response) { - // Check if decoding of the request entity is needed - if (isDecodingRequest() && canDecode(request.getEntity())) { - request.setEntity(decode(request.getEntity())); - } - - return CONTINUE; - } - - /** - * Indicates if a representation can be decoded. - * - * @param representation The representation to test. - * @return True if the call can be decoded. - */ - public boolean canDecode(Representation representation) { - // Test the existence of the representation and that at least an - // encoding applies. - boolean result = (representation != null) && (!representation.getEncodings().isEmpty()); - - if (result) { - boolean found = false; - - for (final Iterator iter = representation.getEncodings().iterator(); !found && iter.hasNext();) { - found = (!iter.next().equals(Encoding.IDENTITY)); - } - - result = found; - } - return result; - } - - /** - * Decodes a given representation if its encodings are supported by NRE. - * - * @param representation The representation to encode. - * @return The decoded representation or the original one if the encoding isn't - * supported by NRE. - */ - public Representation decode(Representation representation) { - Representation result = representation; - - // Check if all encodings of the representation are supported in order - // to avoid the creation of a useless decodeRepresentation object. - // False if an encoding is not supported - boolean supported = true; - // True if all representation's encodings are IDENTITY - boolean identityEncodings = true; - for (final Iterator iter = representation.getEncodings().iterator(); supported && iter.hasNext();) { - final Encoding encoding = iter.next(); - supported = DecodeRepresentation.getSupportedEncodings().contains(encoding); - identityEncodings &= encoding.equals(Encoding.IDENTITY); - } - - if (supported && !identityEncodings) { - result = new DecodeRepresentation(representation); - } - - return result; - } - - /** - * Indicates if the request entity should be decoded. - * - * @return True if the request entity should be decoded. - */ - public boolean isDecodingRequest() { - return this.decodingRequest; - } - - /** - * Indicates if the response entity should be decoded. - * - * @return True if the response entity should be decoded. - */ - public boolean isDecodingResponse() { - return this.decodingResponse; - } - + /** Indicates if the request entity should be decoded. */ + private final boolean decodingRequest; + + /** Indicates if the response entity should be decoded. */ + private final boolean decodingResponse; + + /** + * Constructor to only decode request entities before handling. + * + * @param context The context. + */ + public Decoder(Context context) { + this(context, true, false); + } + + /** + * Constructor. + * + * @param context The context. + * @param decodingRequest Indicates if the request entity should be decoded. + * @param decodingResponse Indicates if the response entity should be decoded. + */ + public Decoder(Context context, boolean decodingRequest, boolean decodingResponse) { + super(context); + this.decodingRequest = decodingRequest; + this.decodingResponse = decodingResponse; + } + + /** + * Allows filtering of a request and a response after the target Restlet handled the request. + * Does nothing by default. + * + * @param request The request to filter. + * @param response The response to filter. + */ + @Override + public void afterHandle(Request request, Response response) { + // Check if decoding of the response entity is needed + if (isDecodingResponse() && canDecode(response.getEntity())) { + response.setEntity(decode(response.getEntity())); + } + } + + /** + * Allows filtering before its handling by the target Restlet. Does nothing by default. + * + * @param request The request to filter. + * @param response The response to filter. + * @return The continuation status. + */ + @Override + public int beforeHandle(Request request, Response response) { + // Check if decoding of the request entity is needed + if (isDecodingRequest() && canDecode(request.getEntity())) { + request.setEntity(decode(request.getEntity())); + } + + return CONTINUE; + } + + /** + * Indicates if a representation can be decoded. + * + * @param representation The representation to test. + * @return True if the call can be decoded. + */ + public boolean canDecode(Representation representation) { + // Test the existence of the representation and that at least an + // encoding applies. + boolean result = (representation != null) && (!representation.getEncodings().isEmpty()); + + if (result) { + boolean found = false; + + for (final Iterator iter = representation.getEncodings().iterator(); + !found && iter.hasNext(); ) { + found = (!iter.next().equals(Encoding.IDENTITY)); + } + + result = found; + } + return result; + } + + /** + * Decodes a given representation if its encodings are supported by NRE. + * + * @param representation The representation to encode. + * @return The decoded representation or the original one if the encoding isn't supported by + * NRE. + */ + public Representation decode(Representation representation) { + Representation result = representation; + + // Check if all encodings of the representation are supported in order + // to avoid the creation of a useless decodeRepresentation object. + // False if an encoding is not supported + boolean supported = true; + // True if all representation's encodings are IDENTITY + boolean identityEncodings = true; + for (final Iterator iter = representation.getEncodings().iterator(); + supported && iter.hasNext(); ) { + final Encoding encoding = iter.next(); + supported = DecodeRepresentation.getSupportedEncodings().contains(encoding); + identityEncodings &= encoding.equals(Encoding.IDENTITY); + } + + if (supported && !identityEncodings) { + result = new DecodeRepresentation(representation); + } + + return result; + } + + /** + * Indicates if the request entity should be decoded. + * + * @return True if the request entity should be decoded. + */ + public boolean isDecodingRequest() { + return this.decodingRequest; + } + + /** + * Indicates if the response entity should be decoded. + * + * @return True if the response entity should be decoded. + */ + public boolean isDecodingResponse() { + return this.decodingResponse; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java index 6789147d26..eabdb44bed 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java @@ -1,21 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; -import org.restlet.data.Disposition; -import org.restlet.data.Encoding; -import org.restlet.engine.io.IoUtils; -import org.restlet.representation.Representation; -import org.restlet.util.WrapperList; -import org.restlet.util.WrapperRepresentation; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -24,240 +16,260 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.zip.*; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.restlet.data.Disposition; +import org.restlet.data.Encoding; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.Representation; +import org.restlet.util.WrapperList; +import org.restlet.util.WrapperRepresentation; /** * Content that encodes a wrapped content. Allows to apply only one encoding. - * + * * @author Jerome Louvel */ public class EncodeRepresentation extends WrapperRepresentation { - /** - * Returns the list of supported encodings. - * - * @return The list of supported encodings. - */ - public static List getSupportedEncodings() { - return Arrays.asList(Encoding.GZIP, Encoding.DEFLATE, Encoding.DEFLATE_NOWRAP, Encoding.ZIP, - Encoding.IDENTITY); - } - - /** Indicates if the encoding can happen. */ - private volatile boolean canEncode; - - /** The encoding to apply. */ - private volatile Encoding encoding; - - /** The applied encodings. */ - private volatile List encodings; - - /** - * Constructor. - * - * @param encoding Encoder algorithm. - * @param wrappedRepresentation The wrapped representation. - */ - public EncodeRepresentation(Encoding encoding, Representation wrappedRepresentation) { - super(wrappedRepresentation); - this.canEncode = getSupportedEncodings().contains(encoding); - this.encodings = null; - this.encoding = encoding; - } - - /** - * Indicates if the encoding can happen. - * - * @return True if the encoding can happen. - */ - public boolean canEncode() { - return this.canEncode; - } - - /** - * Returns the available size in bytes of the encoded representation if known, - * UNKNOWN_SIZE (-1) otherwise. - * - * @return The available size in bytes if known, UNKNOWN_SIZE (-1) otherwise. - */ - @Override - public long getAvailableSize() { - long result = UNKNOWN_SIZE; - - if (canEncode()) { - if (this.encoding.equals(Encoding.IDENTITY)) { - result = getWrappedRepresentation().getAvailableSize(); - } - } else { - result = getWrappedRepresentation().getAvailableSize(); - } - - return result; - } - - /** - * Returns the applied encodings. - * - * @return The applied encodings. - */ - @Override - public List getEncodings() { - if (this.encodings == null) { - this.encodings = new WrapperList() { - - @Override - public boolean add(Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.add(element); - } - - @Override - public void add(int index, Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - super.add(index, element); - } - - @Override - public boolean addAll(Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = elements.iterator(); !addNull - && iterator.hasNext();) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(elements); - } - - @Override - public boolean addAll(int index, Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = elements.iterator(); !addNull - && iterator.hasNext();) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(index, elements); - } - }; - this.encodings.addAll(getWrappedRepresentation().getEncodings()); - if (canEncode()) { - this.encodings.add(this.encoding); - } - } - return this.encodings; - } - - @Override - public Reader getReader() throws IOException { - if (canEncode()) { - return IoUtils.getReader(getStream(), getCharacterSet()); - } else { - return getWrappedRepresentation().getReader(); - } - } - - /** - * Returns the size in bytes of the encoded representation if known, - * UNKNOWN_SIZE (-1) otherwise. - * - * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise. - */ - @Override - public long getSize() { - long result = UNKNOWN_SIZE; - - if (canEncode()) { - if (this.encoding.equals(Encoding.IDENTITY)) { - result = getWrappedRepresentation().getSize(); - } - } else { - result = getWrappedRepresentation().getSize(); - } - - return result; - } - - @Override - public InputStream getStream() throws IOException { - if (canEncode()) { - return IoUtils.getStream(this); - } else { - return getWrappedRepresentation().getStream(); - } - } - - @Override - public String getText() throws IOException { - if (canEncode()) { - return IoUtils.toString(getStream(), getCharacterSet()); - } else { - return getWrappedRepresentation().getText(); - } - } - - @Override - public void write(OutputStream outputStream) throws IOException { - if (canEncode()) { - DeflaterOutputStream encoderOutputStream = null; - - if (this.encoding.equals(Encoding.GZIP)) { - encoderOutputStream = new GZIPOutputStream(outputStream); - } else if (this.encoding.equals(Encoding.DEFLATE)) { - encoderOutputStream = new DeflaterOutputStream(outputStream); - } else if (this.encoding.equals(Encoding.DEFLATE_NOWRAP)) { - encoderOutputStream = new DeflaterOutputStream(outputStream, - new Deflater(Deflater.DEFAULT_COMPRESSION, true)); - } else if (this.encoding.equals(Encoding.ZIP)) { - final ZipOutputStream stream = new ZipOutputStream(outputStream); - String name = "entry"; - - if (getWrappedRepresentation().getDisposition() != null) { - name = getWrappedRepresentation().getDisposition().getParameters() - .getFirstValue(Disposition.NAME_FILENAME, true, name); - } - - stream.putNextEntry(new ZipEntry(name)); - encoderOutputStream = stream; - } else if (this.encoding.equals(Encoding.IDENTITY)) { - // Encoder unnecessary for identity encoding - } - - if (encoderOutputStream != null) { - getWrappedRepresentation().write(encoderOutputStream); - encoderOutputStream.flush(); - encoderOutputStream.finish(); - } else { - getWrappedRepresentation().write(outputStream); - } - } else { - getWrappedRepresentation().write(outputStream); - } - } - - @Override - public void write(java.io.Writer writer) throws IOException { - if (canEncode()) { - OutputStream os = IoUtils.getStream(writer, getCharacterSet()); - write(os); - os.flush(); - } else { - getWrappedRepresentation().write(writer); - } - } - + /** + * Returns the list of supported encodings. + * + * @return The list of supported encodings. + */ + public static List getSupportedEncodings() { + return Arrays.asList( + Encoding.GZIP, + Encoding.DEFLATE, + Encoding.DEFLATE_NOWRAP, + Encoding.ZIP, + Encoding.IDENTITY); + } + + /** Indicates if the encoding can happen. */ + private volatile boolean canEncode; + + /** The encoding to apply. */ + private volatile Encoding encoding; + + /** The applied encodings. */ + private volatile List encodings; + + /** + * Constructor. + * + * @param encoding Encoder algorithm. + * @param wrappedRepresentation The wrapped representation. + */ + public EncodeRepresentation(Encoding encoding, Representation wrappedRepresentation) { + super(wrappedRepresentation); + this.canEncode = getSupportedEncodings().contains(encoding); + this.encodings = null; + this.encoding = encoding; + } + + /** + * Indicates if the encoding can happen. + * + * @return True if the encoding can happen. + */ + public boolean canEncode() { + return this.canEncode; + } + + /** + * Returns the available size in bytes of the encoded representation if known, UNKNOWN_SIZE (-1) + * otherwise. + * + * @return The available size in bytes if known, UNKNOWN_SIZE (-1) otherwise. + */ + @Override + public long getAvailableSize() { + long result = UNKNOWN_SIZE; + + if (canEncode()) { + if (this.encoding.equals(Encoding.IDENTITY)) { + result = getWrappedRepresentation().getAvailableSize(); + } + } else { + result = getWrappedRepresentation().getAvailableSize(); + } + + return result; + } + + /** + * Returns the applied encodings. + * + * @return The applied encodings. + */ + @Override + public List getEncodings() { + if (this.encodings == null) { + this.encodings = + new WrapperList<>() { + + @Override + public boolean add(Encoding element) { + if (element == null) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + return super.add(element); + } + + @Override + public void add(int index, Encoding element) { + if (element == null) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + super.add(index, element); + } + + @Override + public boolean addAll(Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (final Iterator iterator = + elements.iterator(); + !addNull && iterator.hasNext(); ) { + addNull = (iterator.next() == null); + } + } + if (addNull) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + return super.addAll(elements); + } + + @Override + public boolean addAll(int index, Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (final Iterator iterator = + elements.iterator(); + !addNull && iterator.hasNext(); ) { + addNull = (iterator.next() == null); + } + } + if (addNull) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + return super.addAll(index, elements); + } + }; + this.encodings.addAll(getWrappedRepresentation().getEncodings()); + if (canEncode()) { + this.encodings.add(this.encoding); + } + } + return this.encodings; + } + + @Override + public Reader getReader() throws IOException { + if (canEncode()) { + return IoUtils.getReader(getStream(), getCharacterSet()); + } else { + return getWrappedRepresentation().getReader(); + } + } + + /** + * Returns the size in bytes of the encoded representation if known, UNKNOWN_SIZE (-1) + * otherwise. + * + * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise. + */ + @Override + public long getSize() { + long result = UNKNOWN_SIZE; + + if (canEncode()) { + if (this.encoding.equals(Encoding.IDENTITY)) { + result = getWrappedRepresentation().getSize(); + } + } else { + result = getWrappedRepresentation().getSize(); + } + + return result; + } + + @Override + public InputStream getStream() throws IOException { + if (canEncode()) { + return IoUtils.getStream(this); + } else { + return getWrappedRepresentation().getStream(); + } + } + + @Override + public String getText() throws IOException { + if (canEncode()) { + return IoUtils.toString(getStream(), getCharacterSet()); + } else { + return getWrappedRepresentation().getText(); + } + } + + @Override + public void write(OutputStream outputStream) throws IOException { + if (canEncode()) { + DeflaterOutputStream encoderOutputStream = null; + + if (this.encoding.equals(Encoding.GZIP)) { + encoderOutputStream = new GZIPOutputStream(outputStream); + } else if (this.encoding.equals(Encoding.DEFLATE)) { + encoderOutputStream = new DeflaterOutputStream(outputStream); + } else if (this.encoding.equals(Encoding.DEFLATE_NOWRAP)) { + encoderOutputStream = + new DeflaterOutputStream( + outputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); + } else if (this.encoding.equals(Encoding.ZIP)) { + final ZipOutputStream stream = new ZipOutputStream(outputStream); + String name = "entry"; + + if (getWrappedRepresentation().getDisposition() != null) { + name = + getWrappedRepresentation() + .getDisposition() + .getParameters() + .getFirstValue(Disposition.NAME_FILENAME, true, name); + } + + stream.putNextEntry(new ZipEntry(name)); + encoderOutputStream = stream; + } else if (this.encoding.equals(Encoding.IDENTITY)) { + // Encoder unnecessary for identity encoding + } + + if (encoderOutputStream != null) { + getWrappedRepresentation().write(encoderOutputStream); + encoderOutputStream.flush(); + encoderOutputStream.finish(); + } else { + getWrappedRepresentation().write(outputStream); + } + } else { + getWrappedRepresentation().write(outputStream); + } + } + + @Override + public void write(java.io.Writer writer) throws IOException { + if (canEncode()) { + OutputStream os = IoUtils.getStream(writer, getCharacterSet()); + write(os); + os.flush(); + } else { + getWrappedRepresentation().write(writer); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java b/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java index a4736ba441..0cf4671210 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.util.List; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -19,113 +19,112 @@ import org.restlet.routing.Filter; import org.restlet.service.EncoderService; -import java.util.Iterator; -import java.util.List; - /** - * Filter compressing entities. The best encoding is automatically selected - * based on the preferences of the client and on the encoding supported by NRE: - * GZip, Zip and Deflate.
- * If the {@link org.restlet.representation.Representation} has an unknown size, - * it will always be a candidate for encoding. Candidate representations need to - * respect media type criteria by the lists of accepted and ignored media types. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Filter compressing entities. The best encoding is automatically selected based on the preferences + * of the client and on the encoding supported by NRE: GZip, Zip, and Deflate.
+ * If the {@link org.restlet.representation.Representation} has an unknown size, it will always be a + * candidate for encoding. Candidate representations need to respect media type criteria by the + * lists of accepted and ignored media types. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Lars Heuer * @author Jerome Louvel */ public class Encoder extends Filter { - /** Indicates if the request entity should be encoded. */ - private final boolean encodingRequest; - - /** Indicates if the response entity should be encoded. */ - private final boolean encodingResponse; - - /** The parent encoder service. */ - private final EncoderService encoderService; - - /** - * Constructor. - * - * @param context The context. - * @param encodingRequest Indicates if the request entities should be encoded. - * @param encodingResponse Indicates if the response entities should be encoded. - * @param encoderService The parent encoder service. - */ - public Encoder(Context context, boolean encodingRequest, boolean encodingResponse, EncoderService encoderService) { - super(context); - this.encodingRequest = encodingRequest; - this.encodingResponse = encodingResponse; - this.encoderService = encoderService; - } - - /** - * Allows filtering after its handling by the target Restlet. Does nothing by - * default. - * - * @param request The request to filter. - * @param response The response to filter. - */ - @Override - public void afterHandle(Request request, Response response) { - // Check if encoding of the response entity is needed - if (isEncodingResponse() && getEncoderService().canEncode(response.getEntity())) { - response.setEntity(encode(request.getClientInfo(), response.getEntity())); - } - } - - /** - * Allows filtering before its handling by the target Restlet. Does nothing by - * default. - * - * @param request The request to filter. - * @param response The response to filter. - * @return The continuation status. - */ - @Override - public int beforeHandle(Request request, Response response) { - // Check if encoding of the request entity is needed - if (isEncodingRequest() && getEncoderService().canEncode(request.getEntity())) { - request.setEntity(encode(request.getClientInfo(), request.getEntity())); - } - - return CONTINUE; - } - - /** - * Encodes a given representation if an encoding is supported by the client. - * - * @param client The client preferences to use. - * @param representation The representation to encode. - * @return The encoded representation or the original one if no encoding - * supported by the client. - */ - public Representation encode(ClientInfo client, Representation representation) { - Representation result = representation; - Encoding bestEncoding = getBestEncoding(client); - - if (bestEncoding != null) { - result = new EncodeRepresentation(bestEncoding, representation); - } - - return result; - } - - /** - * Returns the best supported encoding for a given client. - * - * @param client The client preferences to use. - * @return The best supported encoding for the given call. - */ - public Encoding getBestEncoding(ClientInfo client) { - Encoding bestEncoding = null; - Encoding currentEncoding = null; - Preference currentPref = null; - float bestScore = 0F; + /** Indicates if the request entity should be encoded. */ + private final boolean encodingRequest; + + /** Indicates if the response entity should be encoded. */ + private final boolean encodingResponse; + + /** The parent encoder service. */ + private final EncoderService encoderService; + + /** + * Constructor. + * + * @param context The context. + * @param encodingRequest Indicates if the request entities should be encoded. + * @param encodingResponse Indicates if the response entities should be encoded. + * @param encoderService The parent encoder service. + */ + public Encoder( + Context context, + boolean encodingRequest, + boolean encodingResponse, + EncoderService encoderService) { + super(context); + this.encodingRequest = encodingRequest; + this.encodingResponse = encodingResponse; + this.encoderService = encoderService; + } + + /** + * Allows filtering of a request and a response after the target Restlet handled the request. + * Does nothing by default. + * + * @param request The request to filter. + * @param response The response to filter. + */ + @Override + public void afterHandle(Request request, Response response) { + // Check if encoding of the response entity is needed + if (isEncodingResponse() && getEncoderService().canEncode(response.getEntity())) { + response.setEntity(encode(request.getClientInfo(), response.getEntity())); + } + } + + /** + * Allows filtering before its handling by the target Restlet. Does nothing by default. + * + * @param request The request to filter. + * @param response The response to filter. + * @return The continuation status. + */ + @Override + public int beforeHandle(Request request, Response response) { + // Check if encoding of the request entity is needed + if (isEncodingRequest() && getEncoderService().canEncode(request.getEntity())) { + request.setEntity(encode(request.getClientInfo(), request.getEntity())); + } + + return CONTINUE; + } + + /** + * Encodes a given representation if the client supports an encoding. + * + * @param client The client preferences to use. + * @param representation The representation to encode. + * @return The encoded representation or the original one if no encoding supported by the + * client. + */ + public Representation encode(ClientInfo client, Representation representation) { + Representation result = representation; + Encoding bestEncoding = getBestEncoding(client); + + if (bestEncoding != null) { + result = new EncodeRepresentation(bestEncoding, representation); + } + + return result; + } + + /** + * Returns the best supported encoding for a given client. + * + * @param client The client preferences to use. + * @return The best supported encoding for the given call. + */ + public Encoding getBestEncoding(ClientInfo client) { + Encoding bestEncoding = null; + Encoding currentEncoding = null; + Preference currentPref = null; + float bestScore = 0F; for (Encoding encoding : getSupportedEncodings()) { currentEncoding = encoding; @@ -144,44 +143,43 @@ public Encoding getBestEncoding(ClientInfo client) { } } - return bestEncoding; - } - - /** - * Returns the parent encoder service. - * - * @return The parent encoder service. - */ - public EncoderService getEncoderService() { - return encoderService; - } - - /** - * Returns the list of supported encodings. By default it calls - * {@link EncodeRepresentation#getSupportedEncodings()} static method. - * - * @return The list of supported encodings. - */ - public List getSupportedEncodings() { - return EncodeRepresentation.getSupportedEncodings(); - } - - /** - * Indicates if the request entity should be encoded. - * - * @return True if the request entity should be encoded. - */ - public boolean isEncodingRequest() { - return this.encodingRequest; - } - - /** - * Indicates if the response entity should be encoded. - * - * @return True if the response entity should be encoded. - */ - public boolean isEncodingResponse() { - return this.encodingResponse; - } - + return bestEncoding; + } + + /** + * Returns the parent encoder service. + * + * @return The parent encoder service. + */ + public EncoderService getEncoderService() { + return encoderService; + } + + /** + * Returns the list of supported encodings. By default, it calls {@link + * EncodeRepresentation#getSupportedEncodings()} static method. + * + * @return The list of supported encodings. + */ + public List getSupportedEncodings() { + return EncodeRepresentation.getSupportedEncodings(); + } + + /** + * Indicates if the request entity should be encoded. + * + * @return True if the request entity should be encoded. + */ + public boolean isEncodingRequest() { + return this.encodingRequest; + } + + /** + * Indicates if the response entity should be encoded. + * + * @return True if the response entity should be encoded. + */ + public boolean isEncodingResponse() { + return this.encodingResponse; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java index 49451e3e52..26743b1ca0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java @@ -1,193 +1,210 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; -import org.restlet.Request; -import org.restlet.data.*; -import org.restlet.service.MetadataService; - import java.util.ArrayList; import java.util.List; +import org.restlet.Request; +import org.restlet.data.CharacterSet; +import org.restlet.data.ClientInfo; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Preference; +import org.restlet.service.MetadataService; /** - * Content negotiation algorithm that flexibly interprets the content - * negotiation preferences to try to always return a variant even if the client - * preferences don't exactly match. - * + * Content negotiation algorithm that flexibly interprets the content negotiation preferences to try + * to always return a variant even if the client preferences don't exactly match. + * * @author Jerome Louvel */ public class FlexibleConneg extends StrictConneg { - /** The enriched list of character set preferences. */ - private volatile List> characterSetPrefs; - - /** The enriched list of encoding preferences. */ - private volatile List> encodingPrefs; - - /** The enriched list of language preferences. */ - private volatile List> languagePrefs; - - /** The enriched list of media type preferences. */ - private volatile List> mediaTypePrefs; - - /** - * Constructor. - * - * @param request The request including client preferences. - * @param metadataService The metadata service used to get default metadata - * values. - */ - public FlexibleConneg(Request request, MetadataService metadataService) { - super(request, metadataService); - ClientInfo clientInfo = request.getClientInfo(); - - if (clientInfo != null) { - // Get the enriched user preferences - this.languagePrefs = getEnrichedPreferences(clientInfo.getAcceptedLanguages(), - (metadataService == null) ? null : metadataService.getDefaultLanguage(), Language.ALL); - this.mediaTypePrefs = getEnrichedPreferences(clientInfo.getAcceptedMediaTypes(), - (metadataService == null) ? null : metadataService.getDefaultMediaType(), MediaType.ALL); - this.characterSetPrefs = getEnrichedPreferences(clientInfo.getAcceptedCharacterSets(), - (metadataService == null) ? null : metadataService.getDefaultCharacterSet(), CharacterSet.ALL); - this.encodingPrefs = getEnrichedPreferences(clientInfo.getAcceptedEncodings(), - (metadataService == null) ? null : metadataService.getDefaultEncoding(), Encoding.ALL); - } - } - - /** - * Returns true if the metadata can be added. - * - * @param - * @param metadata The metadata to add. - * @param undesired The list of prohibited metadata. - * @return True if the metadata can be added. - */ - protected boolean canAdd(T metadata, List undesired) { - boolean add = true; - if (undesired != null) { - for (T u : undesired) { - if (u.equals(metadata)) { - add = false; - break; - } - } - } - - return add; - } - - /** - * Returns the enriched list of character set preferences. - * - * @return The enriched list of character set preferences. - */ - protected List> getCharacterSetPrefs() { - return characterSetPrefs; - } - - /** - * Returns the enriched list of encoding preferences. - * - * @return The enriched list of encoding preferences. - */ - protected List> getEncodingPrefs() { - return encodingPrefs; - } - - /** - * Returns an enriched list of preferences. Contains the user preferences, - * implied user parent preferences (quality between 0.005 and 0.006), default - * preference (quality of 0.003), default parent preference (quality of 0.002), - * all preference (quality of 0.001).
- *
- * This necessary to compensate the misconfiguration of many browsers which - * don't expose all the metadata actually understood by end users. - * - * @param - * @param userPreferences The user preferences to enrich. - * @param defaultValue The default value. - * @param allValue The ALL value. - * @return The enriched user preferences. - */ - @SuppressWarnings("unchecked") - protected List> getEnrichedPreferences(List> userPreferences, - T defaultValue, T allValue) { - // 0) Add the user preferences - List> result = new ArrayList<>(userPreferences); + /** The enriched list of character set preferences. */ + private volatile List> characterSetPrefs; + + /** The enriched list of encoding preferences. */ + private volatile List> encodingPrefs; + + /** The enriched list of language preferences. */ + private volatile List> languagePrefs; + + /** The enriched list of media type preferences. */ + private volatile List> mediaTypePrefs; + + /** + * Constructor. + * + * @param request The request including client preferences. + * @param metadataService The metadata service used to get default metadata values. + */ + public FlexibleConneg(Request request, MetadataService metadataService) { + super(request, metadataService); + ClientInfo clientInfo = request.getClientInfo(); + + if (clientInfo != null) { + // Get the enriched user preferences + this.languagePrefs = + getEnrichedPreferences( + clientInfo.getAcceptedLanguages(), + (metadataService == null) ? null : metadataService.getDefaultLanguage(), + Language.ALL); + this.mediaTypePrefs = + getEnrichedPreferences( + clientInfo.getAcceptedMediaTypes(), + (metadataService == null) + ? null + : metadataService.getDefaultMediaType(), + MediaType.ALL); + this.characterSetPrefs = + getEnrichedPreferences( + clientInfo.getAcceptedCharacterSets(), + (metadataService == null) + ? null + : metadataService.getDefaultCharacterSet(), + CharacterSet.ALL); + this.encodingPrefs = + getEnrichedPreferences( + clientInfo.getAcceptedEncodings(), + (metadataService == null) ? null : metadataService.getDefaultEncoding(), + Encoding.ALL); + } + } + + /** + * Returns true if the metadata can be added. + * + * @param + * @param metadata The metadata to add. + * @param undesired The list of prohibited metadata. + * @return True if the metadata can be added. + */ + protected boolean canAdd(T metadata, List undesired) { + boolean add = true; + if (undesired != null) { + for (T u : undesired) { + if (u.equals(metadata)) { + add = false; + break; + } + } + } + + return add; + } + + /** + * Returns the enriched list of character set preferences. + * + * @return The enriched list of character set preferences. + */ + protected List> getCharacterSetPrefs() { + return characterSetPrefs; + } + + /** + * Returns the enriched list of encoding preferences. + * + * @return The enriched list of encoding preferences. + */ + protected List> getEncodingPrefs() { + return encodingPrefs; + } + + /** + * Returns an enriched list of preferences. Contains the user preferences, implied user parent + * preferences (quality between 0.005 and 0.006), default preference (quality of 0.003), default + * parent preference (quality of 0.002), all preference (quality of 0.001).
+ *
+ * This necessary to compensate the misconfiguration of many browsers which don't expose all the + * metadata actually understood by end users. + * + * @param + * @param userPreferences The user preferences to enrich. + * @param defaultValue The default value. + * @param allValue The ALL value. + * @return The enriched user preferences. + */ + @SuppressWarnings("unchecked") + protected List> getEnrichedPreferences( + List> userPreferences, T defaultValue, T allValue) { + // 0) Add the user preferences + List> result = new ArrayList<>(userPreferences); // 1) List all undesired metadata - List undesired = null; - for (Preference pref : userPreferences) { - if (pref.getQuality() == 0) { - if (undesired == null) { - undesired = new ArrayList(); - } - undesired.add(pref.getMetadata()); - } - } - - // 2) Add the user parent preferences - T parent; - for (int i = 0; i < result.size(); i++) { - Preference userPref = result.get(i); - parent = (T) userPref.getMetadata().getParent(); - - // Add the parent if it is not proscribed. - if (parent != null) { - if (canAdd(parent, undesired)) { - result.add(new Preference(parent, 0.005f + (0.001f * userPref.getQuality()))); - } - } - } - - // 3) Add the default preference - if (defaultValue != null && canAdd(defaultValue, undesired)) { - Preference defaultPref = new Preference(defaultValue, 0.003f); - result.add(defaultPref); - T defaultParent = (T) defaultValue.getParent(); - - if (defaultParent != null && canAdd(defaultParent, undesired)) { - result.add(new Preference(defaultParent, 0.002f)); - } - } - - // 5) Add "all" preference - for (int i = result.size() - 1; i >= 0; i--) { - // Remove any existing preference - if (result.get(i).getMetadata().equals(allValue)) { - result.remove(i); - } - } - - result.add(new Preference(allValue, 0.001f)); - - // 6) Return the enriched preferences - return result; - } - - /** - * Returns the enriched list of language preferences. - * - * @return The enriched list of language preferences. - */ - protected List> getLanguagePrefs() { - return languagePrefs; - } - - /** - * Returns the enriched list of media type preferences. - * - * @return The enriched list of media type preferences. - */ - protected List> getMediaTypePrefs() { - return mediaTypePrefs; - } - + List undesired = null; + for (Preference pref : userPreferences) { + if (pref.getQuality() == 0) { + if (undesired == null) { + undesired = new ArrayList(); + } + undesired.add(pref.getMetadata()); + } + } + + // 2) Add the user parent preferences + T parent; + for (int i = 0; i < result.size(); i++) { + Preference userPref = result.get(i); + parent = (T) userPref.getMetadata().getParent(); + + // Add the parent if it is not proscribed. + if (parent != null) { + if (canAdd(parent, undesired)) { + result.add( + new Preference(parent, 0.005f + (0.001f * userPref.getQuality()))); + } + } + } + + // 3) Add the default preference + if (defaultValue != null && canAdd(defaultValue, undesired)) { + Preference defaultPref = new Preference(defaultValue, 0.003f); + result.add(defaultPref); + T defaultParent = (T) defaultValue.getParent(); + + if (defaultParent != null && canAdd(defaultParent, undesired)) { + result.add(new Preference(defaultParent, 0.002f)); + } + } + + // 5) Add "all" preference + for (int i = result.size() - 1; i >= 0; i--) { + // Remove any existing preference + if (result.get(i).getMetadata().equals(allValue)) { + result.remove(i); + } + } + + result.add(new Preference(allValue, 0.001f)); + + // 6) Return the enriched preferences + return result; + } + + /** + * Returns the enriched list of language preferences. + * + * @return The enriched list of language preferences. + */ + protected List> getLanguagePrefs() { + return languagePrefs; + } + + /** + * Returns the enriched list of media type preferences. + * + * @return The enriched list of media type preferences. + */ + protected List> getMediaTypePrefs() { + return mediaTypePrefs; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java b/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java index 6ca1189c15..8802bc5f33 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java @@ -1,93 +1,95 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; -import org.restlet.data.*; +import org.restlet.data.CharacterSet; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; /** * Associates an extension name and a metadata. - * + * * @author Alex Milowski (alexml@milowski.org) * @author Thierry Boileau */ public class MetadataExtension { - /** The mapped metadata. */ - private final Metadata metadata; - - /** The name of the extension. */ - private final String name; + /** The mapped metadata. */ + private final Metadata metadata; - /** - * Constructor. - * - * @param name The extension name. - * @param metadata The metadata. - */ - public MetadataExtension(String name, Metadata metadata) { - this.name = name; - this.metadata = metadata; - } + /** The name of the extension. */ + private final String name; - /** - * Returns the character set. - * - * @return the character set. - */ - public CharacterSet getCharacterSet() { - return (CharacterSet) getMetadata(); - } + /** + * Constructor. + * + * @param name The extension name. + * @param metadata The metadata. + */ + public MetadataExtension(String name, Metadata metadata) { + this.name = name; + this.metadata = metadata; + } - /** - * Returns the encoding. - * - * @return the encoding. - */ - public Encoding getEncoding() { - return (Encoding) getMetadata(); - } + /** + * Returns the character set. + * + * @return the character set. + */ + public CharacterSet getCharacterSet() { + return (CharacterSet) getMetadata(); + } - /** - * Returns the language. - * - * @return the language. - */ - public Language getLanguage() { - return (Language) getMetadata(); - } + /** + * Returns the encoding. + * + * @return the encoding. + */ + public Encoding getEncoding() { + return (Encoding) getMetadata(); + } - /** - * Returns the media type. - * - * @return the media type. - */ - public MediaType getMediaType() { - return (MediaType) getMetadata(); - } + /** + * Returns the language. + * + * @return the language. + */ + public Language getLanguage() { + return (Language) getMetadata(); + } - /** - * Returns the metadata. - * - * @return the metadata. - */ - public Metadata getMetadata() { - return this.metadata; - } + /** + * Returns the media type. + * + * @return the media type. + */ + public MediaType getMediaType() { + return (MediaType) getMetadata(); + } - /** - * Returns the extension name. - * - * @return The extension name. - */ - public String getName() { - return this.name; - } + /** + * Returns the metadata. + * + * @return the metadata. + */ + public Metadata getMetadata() { + return this.metadata; + } + /** + * Returns the extension name. + * + * @return The extension name. + */ + public String getName() { + return this.name; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java index 12abd5857c..01d313d923 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import static org.restlet.data.Range.isBytesRange; + import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -17,11 +18,8 @@ import org.restlet.routing.Filter; import org.restlet.service.RangeService; -import static org.restlet.data.Range.isBytesRange; - /** - * Filter that is in charge to check the responses to requests for partial - * content. + * Filter that is in charge to check the responses to requests for partial content. * * @author Thierry Boileau */ @@ -48,43 +46,51 @@ protected void afterHandle(Request request, Response response) { if (response.getStatus().isSuccess()) { if (Status.SUCCESS_PARTIAL_CONTENT.equals(response.getStatus())) { if (!rangedEntity) { - getLogger().warning( - "When returning a \"206 Partial content\" status, your response entity must be properly ranged."); + getLogger() + .warning( + "When returning a \"206 Partial content\" status, your response entity must be properly ranged."); } else { // We assume that the response entity has been properly ranged. } - } else if (request.getRanges().size() > 1) { // At this time, lists of ranges are not supported. + } else if (request.getRanges().size() + > 1) { // At this time, lists of ranges are not supported. // Return a server error as this feature isn't supported yet response.setStatus(Status.SERVER_ERROR_NOT_IMPLEMENTED); getLogger().warning("Multiple ranges are not supported at this time."); response.setEntity(null); - } else if (request.getRanges().size() == 1 && - (!request.getConditions().hasSomeRange() - || request.getConditions().getRangeStatus(response.getEntity()).isSuccess())) { - Range requestedRange = request.getRanges().get(0); + } else if (request.getRanges().size() == 1 + && (!request.getConditions().hasSomeRange() + || request.getConditions() + .getRangeStatus(response.getEntity()) + .isSuccess())) { + Range requestedRange = request.getRanges().getFirst(); if ((!response.getEntity().hasKnownSize()) && ((requestedRange.getIndex() == Range.INDEX_LAST - || requestedRange.getSize() == Range.SIZE_MAX) - && !(requestedRange.getIndex() == Range.INDEX_LAST - && requestedRange.getSize() == Range.SIZE_MAX))) { + || requestedRange.getSize() == Range.SIZE_MAX) + && !(requestedRange.getIndex() == Range.INDEX_LAST + && requestedRange.getSize() == Range.SIZE_MAX))) { // The end index cannot be properly computed response.setStatus(Status.SERVER_ERROR_INTERNAL); - getLogger().warning( - "Unable to serve this range since at least the end index of the range cannot be computed."); + getLogger() + .warning( + "Unable to serve this range since at least the end index of the range cannot be computed."); response.setEntity(null); } else if (!requestedRange.equals(responseRange)) { if (rangedEntity) { - getLogger().info( - "The range of the response entity is not equal to the requested one."); + getLogger() + .info( + "The range of the response entity is not equal to the requested one."); } if (response.getEntity().hasKnownSize() - && requestedRange.getSize() > response.getEntity().getAvailableSize()) { + && requestedRange.getSize() + > response.getEntity().getAvailableSize()) { requestedRange.setSize(Range.SIZE_MAX); } - response.setEntity(new RangeRepresentation(response.getEntity(), requestedRange)); + response.setEntity( + new RangeRepresentation(response.getEntity(), requestedRange)); response.setStatus(Status.SUCCESS_PARTIAL_CONTENT); } } @@ -103,5 +109,4 @@ protected void afterHandle(Request request, Response response) { public RangeService getRangeService() { return getApplication().getRangeService(); } - } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java index c0923b6226..07baf5f071 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java @@ -1,113 +1,107 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; import org.restlet.data.Range; import org.restlet.engine.io.IoUtils; import org.restlet.engine.io.RangeInputStream; import org.restlet.representation.Representation; import org.restlet.util.WrapperRepresentation; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; - /** - * Representation that exposes only a range of the content of a wrapped - * representation. - * + * Representation that exposes only a range of the content of a wrapped representation. + * * @author Jerome Louvel */ public class RangeRepresentation extends WrapperRepresentation { - /** The range specific to this wrapper. */ - private volatile Range range; - - /** - * Constructor. - * - * @param wrappedRepresentation The wrapped representation with a complete - * content. - */ - public RangeRepresentation(Representation wrappedRepresentation) { - this(wrappedRepresentation, null); - } - - /** - * Constructor. - * - * @param wrappedRepresentation The wrapped representation with a complete - * content. - * @param range The range to expose. - */ - public RangeRepresentation(Representation wrappedRepresentation, Range range) { - super(wrappedRepresentation); - if (wrappedRepresentation.getRange() != null) { - throw new IllegalArgumentException("The wrapped representation must not have a range set."); - } - setRange(range); - } - - @Override - public long getAvailableSize() { - return IoUtils.getAvailableSize(this); - } - - /** - * Returns the range specific to this wrapper. The wrapped representation must - * not have a range set itself. - * - * @return The range specific to this wrapper. - */ - @Override - public Range getRange() { - return this.range; - } - - @Override - public Reader getReader() throws IOException { - return IoUtils.getReader(getStream(), getCharacterSet()); - } - - @Override - public InputStream getStream() throws IOException { - return new RangeInputStream(super.getStream(), getSize(), getRange()); - } - - @Override - public String getText() throws IOException { - return IoUtils.getText(this); - } - - /** - * Sets the range specific to this wrapper. This will not affect the wrapped - * representation. - * - * @param range The range specific to this wrapper. - */ - @Override - public void setRange(Range range) { - this.range = range; - } - - @Override - public void write(java.io.Writer writer) throws IOException { - OutputStream os = IoUtils.getStream(writer, getCharacterSet()); - write(os); - os.flush(); - } - - @Override - public void write(OutputStream outputStream) throws IOException { - IoUtils.copy(getStream(), outputStream); - } - + /** The range specific to this wrapper. */ + private volatile Range range; + + /** + * Constructor. + * + * @param wrappedRepresentation The wrapped representation with a complete content. + */ + public RangeRepresentation(Representation wrappedRepresentation) { + this(wrappedRepresentation, null); + } + + /** + * Constructor. + * + * @param wrappedRepresentation The wrapped representation with a complete content. + * @param range The range to expose. + */ + public RangeRepresentation(Representation wrappedRepresentation, Range range) { + super(wrappedRepresentation); + if (wrappedRepresentation.getRange() != null) { + throw new IllegalArgumentException( + "The wrapped representation must not have a range set."); + } + setRange(range); + } + + @Override + public long getAvailableSize() { + return IoUtils.getAvailableSize(this); + } + + /** + * Returns the range specific to this wrapper. The wrapped representation must not have a range + * set itself. + * + * @return The range specific to this wrapper. + */ + @Override + public Range getRange() { + return this.range; + } + + @Override + public Reader getReader() throws IOException { + return IoUtils.getReader(getStream(), getCharacterSet()); + } + + @Override + public InputStream getStream() throws IOException { + return new RangeInputStream(super.getStream(), getSize(), getRange()); + } + + @Override + public String getText() throws IOException { + return IoUtils.getText(this); + } + + /** + * Sets the range specific to this wrapper. This will not affect the wrapped representation. + * + * @param range The range specific to this wrapper. + */ + @Override + public void setRange(Range range) { + this.range = range; + } + + @Override + public void write(java.io.Writer writer) throws IOException { + OutputStream os = IoUtils.getStream(writer, getCharacterSet()); + write(os); + os.flush(); + } + + @Override + public void write(OutputStream outputStream) throws IOException { + IoUtils.copy(getStream(), outputStream); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java index 1b8254939d..28b26b09b8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -16,155 +16,149 @@ import org.restlet.routing.Filter; import org.restlet.service.StatusService; -import java.util.logging.Level; - /** - * Filter associating a response entity based on the status. In order to - * customize the default representation, just subclass this class and override - * the "getRepresentation" method.
- * If any exception occurs during the call handling, a "server internal error" - * status is automatically associated to the call. Of course, you can - * personalize the representation of this error. Also, if no status is set - * (null), then the "success OK" status is assumed. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Filter associating a response entity based on the status. To customize the default + * representation, just subclass this class and override the "getRepresentation" method.
+ * If any exception occurs during the call handling, a "server internal error" status is + * automatically associated with the call. Of course, you can personalize the representation of this + * error. Also, if no status is set (null), then the "success OK" status is assumed. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class StatusFilter extends Filter { - /** Indicates if existing representations should be overwritten. */ - private volatile boolean overwriting; - - /** The helped status service. */ - private volatile StatusService statusService; - - /** - * Constructor. - * - * @param context The context. - * @param overwriting Indicates whether an existing representation should be - * overwritten. - */ - public StatusFilter(Context context, boolean overwriting) { - super(context); - this.overwriting = overwriting; - this.statusService = null; - } - - /** - * Constructor from a status service. - * - * @param context The context. - * @param statusService The helped status service. - */ - public StatusFilter(Context context, StatusService statusService) { - this(context, statusService.isOverwriting()); - this.statusService = statusService; - } - - /** - * Allows filtering after its handling by the target Restlet. If the status is - * not set, set {@link org.restlet.data.Status#SUCCESS_OK} by default. - * - * If this is an error status, try to get a representation of it with - * {@link org.restlet.service.StatusService#toRepresentation(Status, Request, Response)} - * . - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public void afterHandle(Request request, Response response) { - // If no status is set, then the "success ok" status is assumed. - if (response.getStatus() == null) { - response.setStatus(Status.SUCCESS_OK); - } - - // Do we need to get a representation for the current status? - try { - if (response.getStatus().isError() && ((response.getEntity() == null) || isOverwriting())) { - response.setEntity(getStatusService().toRepresentation(response.getStatus(), request, response)); - } - } catch (Exception e) { - getLogger().log(Level.WARNING, "Unable to get the custom status representation", e); - } - } - - /** - * Handles the call by distributing it to the next Restlet. If a throwable is - * caught, the - * {@link org.restlet.service.StatusService#toStatus(Throwable, Request, Response)} - * method is invoked. - * - * @param request The request to handle. - * @param response The response to update. - * @return The continuation status. - */ - @Override - protected int doHandle(Request request, Response response) { - // Normally handle the call - try { - super.doHandle(request, response); - } catch (Throwable throwable) { - Status status = getStatusService().toStatus(throwable, request, response); - - final Level level; - if (status.isServerError()) { - level = Level.WARNING; - } else if (status.isConnectorError()) { - level = Level.INFO; - } else if (status.isClientError()) { - level = Level.FINE; - } else { - level = Level.FINE; - } - getLogger().log(level, "Exception or error caught by status service", throwable); - - if (response != null) { - response.setStatus(status); - } - } - - return CONTINUE; - } - - /** - * Returns the helped status service. - * - * @return The helped status service. - */ - public StatusService getStatusService() { - return statusService; - } - - /** - * Indicates if existing representations should be overwritten. - * - * @return True if existing representations should be overwritten. - */ - public boolean isOverwriting() { - return overwriting; - } - - /** - * Indicates if existing representations should be overwritten. - * - * @param overwriting True if existing representations should be overwritten. - */ - public void setOverwriting(boolean overwriting) { - this.overwriting = overwriting; - } - - /** - * Sets the helped status service. - * - * @param statusService The helped status service. - */ - public void setStatusService(StatusService statusService) { - this.statusService = statusService; - } - + /** Indicates if existing representations should be overwritten. */ + private volatile boolean overwriting; + + /** The helped status service. */ + private volatile StatusService statusService; + + /** + * Constructor. + * + * @param context The context. + * @param overwriting Indicates whether an existing representation should be overwritten. + */ + public StatusFilter(Context context, boolean overwriting) { + super(context); + this.overwriting = overwriting; + this.statusService = null; + } + + /** + * Constructor from a status service. + * + * @param context The context. + * @param statusService The helped status service. + */ + public StatusFilter(Context context, StatusService statusService) { + this(context, statusService.isOverwriting()); + this.statusService = statusService; + } + + /** + * Allows filtering after its handling by the target Restlet. If the status is not set, set + * {@link org.restlet.data.Status#SUCCESS_OK} by default. + * + *

If this is an error status, try to get a representation of it with {@link + * org.restlet.service.StatusService#toRepresentation(Status, Request, Response)} . + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public void afterHandle(Request request, Response response) { + // If no status is set, then the "success ok" status is assumed. + if (response.getStatus() == null) { + response.setStatus(Status.SUCCESS_OK); + } + + // Do we need to get a representation for the current status? + try { + if (response.getStatus().isError() + && ((response.getEntity() == null) || isOverwriting())) { + response.setEntity( + getStatusService() + .toRepresentation(response.getStatus(), request, response)); + } + } catch (Exception e) { + getLogger().log(Level.WARNING, "Unable to get the custom status representation", e); + } + } + + /** + * Handles the call by distributing it to the next Restlet. If a throwable is caught, the {@link + * org.restlet.service.StatusService#toStatus(Throwable, Request, Response)} method is invoked. + * + * @param request The request to handle. + * @param response The response to update. + * @return The continuation status. + */ + @Override + protected int doHandle(Request request, Response response) { + // Normally handle the call + try { + super.doHandle(request, response); + } catch (Throwable throwable) { + Status status = getStatusService().toStatus(throwable, request, response); + + final Level level; + if (status.isServerError()) { + level = Level.WARNING; + } else if (status.isConnectorError()) { + level = Level.INFO; + } else if (status.isClientError()) { + level = Level.FINE; + } else { + level = Level.FINE; + } + getLogger().log(level, "Exception or error caught by status service", throwable); + + if (response != null) { + response.setStatus(status); + } + } + + return CONTINUE; + } + + /** + * Returns the helped status service. + * + * @return The helped status service. + */ + public StatusService getStatusService() { + return statusService; + } + + /** + * Indicates if existing representations should be overwritten. + * + * @return True if existing representations should be overwritten. + */ + public boolean isOverwriting() { + return overwriting; + } + + /** + * Indicates if existing representations should be overwritten. + * + * @param overwriting True if existing representations should be overwritten. + */ + public void setOverwriting(boolean overwriting) { + this.overwriting = overwriting; + } + + /** + * Sets the helped status service. + * + * @param statusService The helped status service. + */ + public void setStatusService(StatusService statusService) { + this.statusService = statusService; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java b/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java index 42d6f1762a..4baf352bdb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java @@ -1,202 +1,205 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; -import org.restlet.data.Status; - import java.io.Serializable; +import org.restlet.data.Status; /** * Representation of a {@link Status}. - * + * * @author Manuel Boillod */ public class StatusInfo implements Serializable { - private static final long serialVersionUID = 1L; - - /** The specification code. */ - private volatile int code; - - /** The email address of the administrator to contact in case of error. */ - private volatile String contactEmail; - - /** The longer description. */ - private volatile String description; - - /** The home URI to propose in case of error. */ - private volatile String homeRef; - - /** The short reason phrase. */ - private volatile String reasonPhrase; - - /** The URI of the specification describing the method. */ - private volatile String uri; - - /** - * Empty Constructor - */ - public StatusInfo() { - } - - /** - * Constructor. - * - * @param code The specification code. - * @param description The longer description. - * @param reasonPhrase The short reason phrase. - */ - public StatusInfo(int code, String description, String reasonPhrase) { - this(code, description, reasonPhrase, null, null, null); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param description The longer description. - * @param reasonPhrase The short reason phrase. - * @param uri The URI of the specification describing the method. - * @param contactEmail The email address of the administrator to contact in case - * of error. - * @param homeRef The home URI to propose in case of error. - */ - public StatusInfo(int code, String description, String reasonPhrase, String uri, String contactEmail, - String homeRef) { - super(); - this.code = code; - this.description = description; - this.reasonPhrase = reasonPhrase; - this.uri = uri; - this.contactEmail = contactEmail; - this.homeRef = homeRef; - } - - /** - * Constructor. - * - * @param status The represented status. - */ - public StatusInfo(Status status) { - this(status, null, null); - } - - /** - * Constructor. - * - * @param status The represented status. - * @param contactEmail The email address of the administrator to contact in case - * of error. - * @param homeRef The home URI to propose in case of error. - */ - public StatusInfo(Status status, String contactEmail, String homeRef) { - this(status.getCode(), status.getDescription(), status.getReasonPhrase(), status.getUri(), contactEmail, - homeRef); - } - - /** - * Returns the code of the status. - * - * @return The code of the status. - */ - public int getCode() { - return code; - } - - /** - * Returns the email address of the administrator to contact in case of error. - * - * @return The email address. - */ - public String getContactEmail() { - return contactEmail; - } - - /** - * Returns the description of the status. - * - * @return The description of the status. - */ - public String getDescription() { - return description; - } - - /** - * Returns the home URI to propose in case of error. - * - * @return The home URI. - */ - public String getHomeRef() { - return homeRef; - } - - /** - * Returns the short description of the status. - * - * @return The short description of the status. - */ - public String getReasonPhrase() { - return reasonPhrase; - } - - /** - * Returns the URI of the specification describing the status. - * - * @return The URI of the specification describing the status. - */ - public String getUri() { - return this.uri; - } - - /** - * Sets the code of the status. - * - * @param code The code of the status. - */ - public void setCode(int code) { - this.code = code; - } - - /** - * Sets the email address of the administrator to contact in case of error. - * - * @param email The email address. - */ - public void setContactEmail(String email) { - this.contactEmail = email; - } - - /** - * Sets the description of the status. - * - * @param description The description of the status. - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Sets the home URI to propose in case of error. - * - * @param homeRef The home URI. - */ - public void setHomeRef(String homeRef) { - this.homeRef = homeRef; - } - - /** - * Sets the short description of the status. - * - * @param reasonPhrase The short description of the status. - */ - public void setReasonPhrase(String reasonPhrase) { - this.reasonPhrase = reasonPhrase; - } + private static final long serialVersionUID = 1L; + + /** The specification code. */ + private volatile int code; + + /** The email address of the administrator to contact in case of error. */ + private volatile String contactEmail; + + /** The longer description. */ + private volatile String description; + + /** The home URI to propose in case of error. */ + private volatile String homeRef; + + /** The short reason phrase. */ + private volatile String reasonPhrase; + + /** The URI of the specification describing the method. */ + private volatile String uri; + + /** Empty Constructor */ + public StatusInfo() {} + + /** + * Constructor. + * + * @param code The specification code. + * @param description The longer description. + * @param reasonPhrase The short reason phrase. + */ + public StatusInfo(int code, String description, String reasonPhrase) { + this(code, description, reasonPhrase, null, null, null); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param description The longer description. + * @param reasonPhrase The short reason phrase. + * @param uri The URI of the specification describing the method. + * @param contactEmail The email address of the administrator to contact in case of error. + * @param homeRef The home URI to propose in case of error. + */ + public StatusInfo( + int code, + String description, + String reasonPhrase, + String uri, + String contactEmail, + String homeRef) { + super(); + this.code = code; + this.description = description; + this.reasonPhrase = reasonPhrase; + this.uri = uri; + this.contactEmail = contactEmail; + this.homeRef = homeRef; + } + + /** + * Constructor. + * + * @param status The represented status. + */ + public StatusInfo(Status status) { + this(status, null, null); + } + + /** + * Constructor. + * + * @param status The represented status. + * @param contactEmail The email address of the administrator to contact in case of error. + * @param homeRef The home URI to propose in case of error. + */ + public StatusInfo(Status status, String contactEmail, String homeRef) { + this( + status.getCode(), + status.getDescription(), + status.getReasonPhrase(), + status.getUri(), + contactEmail, + homeRef); + } + + /** + * Returns the code of the status. + * + * @return The code of the status. + */ + public int getCode() { + return code; + } + + /** + * Returns the email address of the administrator to contact in case of error. + * + * @return The email address. + */ + public String getContactEmail() { + return contactEmail; + } + + /** + * Returns the description of the status. + * + * @return The description of the status. + */ + public String getDescription() { + return description; + } + + /** + * Returns the home URI to propose in case of error. + * + * @return The home URI. + */ + public String getHomeRef() { + return homeRef; + } + + /** + * Returns the short description of the status. + * + * @return The short description of the status. + */ + public String getReasonPhrase() { + return reasonPhrase; + } + + /** + * Returns the URI of the specification describing the status. + * + * @return The URI of the specification describing the status. + */ + public String getUri() { + return this.uri; + } + + /** + * Sets the code of the status. + * + * @param code The code of the status. + */ + public void setCode(int code) { + this.code = code; + } + + /** + * Sets the email address of the administrator to contact in case of error. + * + * @param email The email address. + */ + public void setContactEmail(String email) { + this.contactEmail = email; + } + + /** + * Sets the description of the status. + * + * @param description The description of the status. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Sets the home URI to propose in case of error. + * + * @param homeRef The home URI. + */ + public void setHomeRef(String homeRef) { + this.homeRef = homeRef; + } + + /** + * Sets the short description of the status. + * + * @param reasonPhrase The short description of the status. + */ + public void setReasonPhrase(String reasonPhrase) { + this.reasonPhrase = reasonPhrase; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java index b7440c32b9..a1d6e99947 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java @@ -1,327 +1,344 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; -import org.restlet.data.*; +import org.restlet.data.CharacterSet; +import org.restlet.data.Encoding; +import org.restlet.data.Form; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Parameter; +import org.restlet.data.Preference; import org.restlet.engine.resource.MethodAnnotationInfo; import org.restlet.engine.resource.VariantInfo; import org.restlet.representation.Variant; import org.restlet.service.MetadataService; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; - /** - * Content negotiation algorithm that strictly interprets the content - * negotiation preferences. - * + * Content negotiation algorithm that strictly interprets the content negotiation preferences. + * * @author Jerome Louvel */ public class StrictConneg extends Conneg { - /** - * Constructor. - * - * @param request The request including client preferences. - * @param metadataService The metadata service used to get default metadata - * values. - */ - public StrictConneg(Request request, MetadataService metadataService) { - super(request, metadataService); - } - - /** - * Returns the enriched list of character set preferences. - * - * @return The enriched list of character set preferences. - */ - protected List> getCharacterSetPrefs() { - return getRequest().getClientInfo().getAcceptedCharacterSets(); - } - - /** - * Returns the enriched list of encoding preferences. - * - * @return The enriched list of encoding preferences. - */ - protected List> getEncodingPrefs() { - return getRequest().getClientInfo().getAcceptedEncodings(); - } - - /** - * Returns the enriched list of language preferences. - * - * @return The enriched list of language preferences. - */ - protected List> getLanguagePrefs() { - return getRequest().getClientInfo().getAcceptedLanguages(); - } - - /** - * Returns the enriched list of media type preferences. - * - * @return The enriched list of media type preferences. - */ - protected List> getMediaTypePrefs() { - return getRequest().getClientInfo().getAcceptedMediaTypes(); - } - - /** - * Scores the annotation descriptor. By default, it assess the quality of the - * query parameters with the URI query constraint defined in the annotation - * value if any. - * - * @param annotation The annotation descriptor to score. - * @return The annotation descriptor score. - */ - protected float scoreAnnotation(MethodAnnotationInfo annotation) { - if (annotation == null) { - return 0.0F; - } - - float score = doScoreAnnotation(annotation); - - if (Context.getCurrentLogger().isLoggable(Level.FINE)) { - Context.getCurrentLogger().fine("Score of annotation \"" + annotation + "\"= " + score); - } - return score; - } - - private float doScoreAnnotation(MethodAnnotationInfo annotation) { - if (annotation.getQuery() == null) { - if ((getRequest().getResourceRef() == null) || (getRequest().getResourceRef().getQuery() == null)) { - // No query filter, but no query provided, average fit - return 0.5F; - } - - // No query filter, but a query provided, lower fit - return 0.25F; - } - - if ((getRequest().getResourceRef() == null) || (getRequest().getResourceRef().getQuery() == null)) { - // Query constraint defined, but no query provided, no fit - return -1.0F; - } - - // Query constraint defined and a query provided, see if fit - Form constraintParams = new Form(annotation.getQuery()); - Form actualParams = getRequest().getResourceRef().getQueryAsForm(); - Set matchedParams = new HashSet(); - Parameter constraintParam; - Parameter actualParam; - - boolean allConstraintsMatched = true; - boolean constraintMatched = false; - - // Verify that each query constraint has been matched - for (int i = 0; allConstraintsMatched && (i < constraintParams.size()); i++) { - constraintParam = constraintParams.get(i); - constraintMatched = false; - - for (int j = 0; !constraintMatched && (j < actualParams.size()); j++) { - actualParam = actualParams.get(j); - - if (constraintParam.getName().equals(actualParam.getName())) { - // Potential match found based on name - if ((constraintParam.getValue() == null) - || constraintParam.getValue().equals(actualParam.getValue())) { - // Actual match found! - constraintMatched = true; - matchedParams.add(actualParam); - } - } - } - - allConstraintsMatched = allConstraintsMatched && constraintMatched; - } - - if (allConstraintsMatched) { - // Test if all actual query parameters matched a constraint, so increase score - if (actualParams.size() == matchedParams.size()) { - // All filter parameters matched, no additional parameter found - return 1.0F; - } - // All filter parameters matched, but additional parameters found - return 0.75F; - } - - return -1.0F; - } - - /** - * Scores a character set relatively to enriched client preferences. - * - * @param characterSet The character set to score. - * @return The score. - */ - public float scoreCharacterSet(CharacterSet characterSet) { - return scoreMetadata(characterSet, getCharacterSetPrefs()); - } - - /** - * Scores encodings relatively to enriched client preferences. - * - * @param encodings The encodings to score. - * @return The score. - */ - public float scoreEncodings(List encodings) { - return scoreMetadata(encodings, getEncodingPrefs()); - } - - /** - * Scores languages relatively to enriched client preferences. - * - * @param languages The languages to score. - * @return The score. - */ - public float scoreLanguages(List languages) { - return scoreMetadata(languages, getLanguagePrefs()); - } - - /** - * Scores a media type relatively to enriched client preferences. - * - * @param mediaType The media type to score. - * @return The score. - */ - public float scoreMediaType(MediaType mediaType) { - float result = -1.0F; - float current; - - if (mediaType != null) { - for (Preference pref : getMediaTypePrefs()) { - if (pref.getMetadata().includes(mediaType, false)) { - current = pref.getQuality(); - } else { - current = -1.0F; - } - - if (current > result) { - result = current; - } - } - } else { - result = 0.0F; - } - - return result; - } - - /** - * Scores a list of metadata relatively to enriched client preferences. - * - * @param metadataList The list of metadata to score. - * @return The score. - */ - protected float scoreMetadata(List metadataList, List> prefs) { - float result = -1.0F; - float current; - - if ((metadataList != null) && !metadataList.isEmpty()) { - for (Preference pref : prefs) { - for (T metadata : metadataList) { - if (pref.getMetadata().includes(metadata)) { - current = pref.getQuality(); - } else { - current = -1.0F; - } - - if (current > result) { - result = current; - } - } - } - } else { - result = 0.0F; - } - - return result; - } - - /** - * Scores a metadata relatively to enriched client preferences. - * - * @param metadata The metadata to score. - * @return The score. - */ - protected float scoreMetadata(T metadata, List> prefs) { - float result = -1.0F; - float current; - - if (metadata != null) { - for (Preference pref : prefs) { - if (pref.getMetadata().includes(metadata)) { - current = pref.getQuality(); - } else { - current = -1.0F; - } - - if (current > result) { - result = current; - } - } - } else { - result = 0.0F; - } - - return result; - } - - /** - * Scores a variant relatively to enriched client preferences. The language has - * a weight of 4, the media type 3, the character set 2 and the encoding 1. - * - * @param variant The variant to score. - * @return The enriched client preferences. - */ - public float scoreVariant(Variant variant) { - float result = -1.0F; - float languageScore = scoreLanguages(variant.getLanguages()); - - if (languageScore != -1.0F) { - float mediaTypeScore = scoreMediaType(variant.getMediaType()); - - if (mediaTypeScore != -1.0F) { - float characterSetScore = scoreCharacterSet(variant.getCharacterSet()); - - if (characterSetScore != -1.0F) { - float encodingScore = scoreEncodings(variant.getEncodings()); - - if (encodingScore != -1.0F) { - if (variant instanceof VariantInfo) { - float annotationScore = scoreAnnotation(((VariantInfo) variant).getAnnotationInfo()); - - // Return the weighted average score - result = ((languageScore * 4.0F) + (mediaTypeScore * 3.0F) + (characterSetScore * 2.0F) - + (encodingScore * 1.0F) + (annotationScore * 2.0F)) / 12.0F; - // Take into account the affinity with the input - // entity - result = result * ((VariantInfo) variant).getInputScore(); - } else { - // Return the weighted average score - result = ((languageScore * 4.0F) + (mediaTypeScore * 3.0F) + (characterSetScore * 2.0F) - + (encodingScore * 1.0F)) / 10.0F; - } - } - } - } - } - - if (Context.getCurrentLogger().isLoggable(Level.FINE)) { - Context.getCurrentLogger().fine("Total score of variant \"" + variant + "\"= " + result); - } - - return result; - } + /** + * Constructor. + * + * @param request The request including client preferences. + * @param metadataService The metadata service used to get default metadata values. + */ + public StrictConneg(Request request, MetadataService metadataService) { + super(request, metadataService); + } + + /** + * Returns the enriched list of character set preferences. + * + * @return The enriched list of character set preferences. + */ + protected List> getCharacterSetPrefs() { + return getRequest().getClientInfo().getAcceptedCharacterSets(); + } + + /** + * Returns the enriched list of encoding preferences. + * + * @return The enriched list of encoding preferences. + */ + protected List> getEncodingPrefs() { + return getRequest().getClientInfo().getAcceptedEncodings(); + } + + /** + * Returns the enriched list of language preferences. + * + * @return The enriched list of language preferences. + */ + protected List> getLanguagePrefs() { + return getRequest().getClientInfo().getAcceptedLanguages(); + } + + /** + * Returns the enriched list of media type preferences. + * + * @return The enriched list of media type preferences. + */ + protected List> getMediaTypePrefs() { + return getRequest().getClientInfo().getAcceptedMediaTypes(); + } + + /** + * Scores the annotation descriptor. By default, it assess the quality of the query parameters + * with the URI query constraint defined in the annotation value if any. + * + * @param annotation The annotation descriptor to score. + * @return The annotation descriptor score. + */ + protected float scoreAnnotation(MethodAnnotationInfo annotation) { + if (annotation == null) { + return 0.0F; + } + + float score = doScoreAnnotation(annotation); + + if (Context.getCurrentLogger().isLoggable(Level.FINE)) { + Context.getCurrentLogger().fine("Score of annotation \"" + annotation + "\"= " + score); + } + return score; + } + + private float doScoreAnnotation(MethodAnnotationInfo annotation) { + if (annotation.getQuery() == null) { + if ((getRequest().getResourceRef() == null) + || (getRequest().getResourceRef().getQuery() == null)) { + // No query filter, but no query provided, average fit + return 0.5F; + } + + // No query filter, but a query provided, lower fit + return 0.25F; + } + + if ((getRequest().getResourceRef() == null) + || (getRequest().getResourceRef().getQuery() == null)) { + // Query constraint defined, but no query provided, no fit + return -1.0F; + } + + // Query constraint defined, and a query provided, see if fit + final Form constraintParams = new Form(annotation.getQuery()); + final Form actualParams = getRequest().getResourceRef().getQueryAsForm(); + final Set matchedParams = new HashSet<>(); + + Parameter constraintParam; + Parameter actualParam; + + boolean allConstraintsMatched = true; + boolean constraintMatched; + + // Verify that each query constraint has been matched + for (int i = 0; allConstraintsMatched && (i < constraintParams.size()); i++) { + constraintParam = constraintParams.get(i); + constraintMatched = false; + + for (int j = 0; !constraintMatched && (j < actualParams.size()); j++) { + actualParam = actualParams.get(j); + + if (constraintParam.getName().equals(actualParam.getName())) { + // Potential match found based on name + if ((constraintParam.getValue() == null) + || constraintParam.getValue().equals(actualParam.getValue())) { + // Actual match found! + constraintMatched = true; + matchedParams.add(actualParam); + } + } + } + + allConstraintsMatched = constraintMatched; + } + + if (allConstraintsMatched) { + // Test if all actual query parameters matched a constraint, so increase score + if (actualParams.size() == matchedParams.size()) { + // All filter parameters matched, no additional parameter found + return 1.0F; + } + // All filter parameters matched, but additional parameters found + return 0.75F; + } + + return -1.0F; + } + + /** + * Scores a character set relatively to enriched client preferences. + * + * @param characterSet The character set to score. + * @return The score. + */ + public float scoreCharacterSet(CharacterSet characterSet) { + return scoreMetadata(characterSet, getCharacterSetPrefs()); + } + + /** + * Scores encodings relatively to enriched client preferences. + * + * @param encodings The encodings to score. + * @return The score. + */ + public float scoreEncodings(List encodings) { + return scoreMetadata(encodings, getEncodingPrefs()); + } + + /** + * Scores languages relatively to enriched client preferences. + * + * @param languages The languages to score. + * @return The score. + */ + public float scoreLanguages(List languages) { + return scoreMetadata(languages, getLanguagePrefs()); + } + + /** + * Scores a media type relatively to enriched client preferences. + * + * @param mediaType The media type to score. + * @return The score. + */ + public float scoreMediaType(MediaType mediaType) { + float result = -1.0F; + float current; + + if (mediaType != null) { + for (Preference pref : getMediaTypePrefs()) { + if (pref.getMetadata().includes(mediaType, false)) { + current = pref.getQuality(); + } else { + current = -1.0F; + } + + if (current > result) { + result = current; + } + } + } else { + result = 0.0F; + } + + return result; + } + + /** + * Scores a list of metadata relatively to enriched client preferences. + * + * @param metadataList The list of metadata to score. + * @return The score. + */ + protected float scoreMetadata( + List metadataList, List> prefs) { + float result = -1.0F; + float current; + + if ((metadataList != null) && !metadataList.isEmpty()) { + for (Preference pref : prefs) { + for (T metadata : metadataList) { + if (pref.getMetadata().includes(metadata)) { + current = pref.getQuality(); + } else { + current = -1.0F; + } + + if (current > result) { + result = current; + } + } + } + } else { + result = 0.0F; + } + + return result; + } + + /** + * Scores a metadata relatively to enriched client preferences. + * + * @param metadata The metadata to score. + * @return The score. + */ + protected float scoreMetadata(T metadata, List> prefs) { + float result = -1.0F; + float current; + + if (metadata != null) { + for (Preference pref : prefs) { + if (pref.getMetadata().includes(metadata)) { + current = pref.getQuality(); + } else { + current = -1.0F; + } + + if (current > result) { + result = current; + } + } + } else { + result = 0.0F; + } + + return result; + } + + /** + * Scores a variant relatively to enriched client preferences. The language has a weight of 4, + * the media type 3, the character set 2, and the encoding 1. + * + * @param variant The variant to score. + * @return The enriched client preferences. + */ + public float scoreVariant(Variant variant) { + float result = -1.0F; + float languageScore = scoreLanguages(variant.getLanguages()); + + if (languageScore != -1.0F) { + float mediaTypeScore = scoreMediaType(variant.getMediaType()); + + if (mediaTypeScore != -1.0F) { + float characterSetScore = scoreCharacterSet(variant.getCharacterSet()); + + if (characterSetScore != -1.0F) { + float encodingScore = scoreEncodings(variant.getEncodings()); + + if (encodingScore != -1.0F) { + if (variant instanceof VariantInfo) { + float annotationScore = + scoreAnnotation(((VariantInfo) variant).getAnnotationInfo()); + + // Return the weighted average score + result = + ((languageScore * 4.0F) + + (mediaTypeScore * 3.0F) + + (characterSetScore * 2.0F) + + (encodingScore * 1.0F) + + (annotationScore * 2.0F)) + / 12.0F; + // Take into account the affinity with the input + // entity + result *= ((VariantInfo) variant).getInputScore(); + } else { + // Return the weighted average score + result = + ((languageScore * 4.0F) + + (mediaTypeScore * 3.0F) + + (characterSetScore * 2.0F) + + (encodingScore * 1.0F)) + / 10.0F; + } + } + } + } + } + + if (Context.getCurrentLogger().isLoggable(Level.FINE)) { + Context.getCurrentLogger() + .fine("Total score of variant \"" + variant + "\"= " + result); + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java index 69b7a008d9..ce562c5b1f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java @@ -1,18 +1,38 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; +import org.restlet.data.CharacterSet; +import org.restlet.data.ClientInfo; +import org.restlet.data.Encoding; +import org.restlet.data.Form; +import org.restlet.data.Header; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Method; +import org.restlet.data.Preference; +import org.restlet.data.Reference; import org.restlet.engine.Engine; import org.restlet.engine.header.HeaderConstants; import org.restlet.engine.header.PreferenceReader; @@ -22,558 +42,563 @@ import org.restlet.service.TunnelService; import org.restlet.util.Series; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.*; -import java.util.Map.Entry; - /** - * Filter tunneling browser calls into full REST calls. The request method can - * be changed (via POST requests only) as well as the accepted media types, - * languages, encodings and character sets. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Filter tunneling browser calls into full REST calls. The request method can be changed (via POST + * requests only) as well as the accepted media types, languages, encodings, and character sets. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class TunnelFilter extends Filter { - /** - * Used to describe the replacement value for an old client preference and for a - * a series of specific agent (i.e., web client) attributes. - * - * @author Thierry Boileau - */ - private static class HeaderReplacer { - - static class Builder { - Map agentAttributes = new HashMap(); - - String newValue; - - String oldValue; - - HeaderReplacer build() { - return new HeaderReplacer(oldValue, newValue, agentAttributes); - } - - void putAgentAttribute(String key, String value) { - agentAttributes.put(key, value); - } - - void setNewValue(String newValue) { - this.newValue = newValue; - } - - void setOldValue(String oldValue) { - this.oldValue = oldValue; - } - } - - /** Agent attributes that must be checked. */ - private final Map agentAttributes; - - /** New header value. */ - private final String headerNew; - - /** Old header value. */ - private final String headerOld; - - HeaderReplacer(String headerOld, String headerNew, Map agentAttributes) { - this.headerOld = headerOld; - this.headerNew = headerNew; - this.agentAttributes = Collections.unmodifiableMap(agentAttributes); - } - - public Map getAgentAttributes() { - return agentAttributes; - } - - public String getHeaderNew() { - return headerNew; - } - - public String getHeaderOld() { - return headerOld; - } - - /** - * Indicates if the current header replacer matches the request attributes. - * - * @param agentAttributes The user agent attributes to match. - * @param headerOld The facultative value of the current's request header - * to match. - * @return true if the given request's attributes match the current header - * replacer. - */ - public boolean matchesConditions(Map agentAttributes, String headerOld) { - // Check the conditions - boolean checked = true; - // Check that the agent properties match the properties - // set by the rule. - for (Iterator> iterator = getAgentAttributes().entrySet().iterator(); checked - && iterator.hasNext();) { - Entry entry = iterator.next(); - String attribute = agentAttributes.get(entry.getKey()); - checked = (attribute != null && attribute.equalsIgnoreCase(entry.getValue())); - } - if (checked && getHeaderOld() != null) { - // If the rule defines an old header value, check that it is the - // same than the user agent's header value. - checked = getHeaderOld().equals(headerOld); - } - return checked; - } - - } - - /** Used to replace accept-encoding header values. */ - private final List acceptEncodingReplacers = getAcceptEncodingReplacers(); - - /** Used to replace accept header values. */ - private final List acceptReplacers = getAcceptReplacers(); - - /** - * Constructor. - * - * @param context The parent context. - */ - public TunnelFilter(Context context) { - super(context); - } - - @Override - public int beforeHandle(Request request, Response response) { - if (getTunnelService().isUserAgentTunnel()) { - processUserAgent(request); - } - - if (getTunnelService().isExtensionsTunnel()) { - processExtensions(request); - } - - if (getTunnelService().isQueryTunnel()) { - processQuery(request); - } - - if (getTunnelService().isHeadersTunnel()) { - processHeaders(request); - } - - return CONTINUE; - } - - /** - * Returns the list of new accept-encoding header values. Each of them describe - * also a set of conditions required to set the new value. This method is used - * only to initialize the headerReplacers field. - * - * @return The list of new accept-encoding header values. - */ - private List getAcceptEncodingReplacers() { - // Load the accept.properties file. - return getheaderReplacers(Engine.getResource("org/restlet/service/accept-encoding.properties"), - "acceptEncodingOld", "acceptEncodingNew"); - } - - /** - * Returns the list of new accept header values. Each of them describe also a - * set of conditions required to set the new value. This method is used only to - * initialize the headerReplacers field. - * - * @return The list of new accept header values. - */ - private List getAcceptReplacers() { - // Load the accept.properties file. - return getheaderReplacers(Engine.getResource("org/restlet/service/accept.properties"), "acceptOld", - "acceptNew"); - } - - /** - * Returns the list of new header values. Each of them describe also a set of - * conditions required to set the new value. This method is used only to - * initialize the headerReplacers field. - * - * @param userAgentPropertiesUrl The URL of the properties file that describe - * replacement values based on the user agent - * string. - * @param oldHeaderName The name of the property that gives the value - * of the header to be replaced (could be null - - * in that case, the new value is unconditionnaly - * set. - * @param newHeaderName The name of the property that gives the - * replacement value. - * @return The list of new header values. - */ - private List getheaderReplacers(final URL userAgentPropertiesUrl, String oldHeaderName, - String newHeaderName) { - List headerReplacers = new ArrayList(); - - if (userAgentPropertiesUrl != null) { - BufferedReader reader; - try { - reader = new BufferedReader( - new InputStreamReader(userAgentPropertiesUrl.openStream(), CharacterSet.UTF_8.getName()), - IoUtils.BUFFER_SIZE); - - HeaderReplacer.Builder headerReplacerBuilder = new HeaderReplacer.Builder(); - - try { - // Read the entire file, excluding comment lines starting - // with "#" character. - String line = reader.readLine(); - for (; line != null; line = reader.readLine()) { - if (!line.startsWith("#")) { - final String[] keyValue = line.split(":"); - if (keyValue.length == 2) { - final String key = keyValue[0].trim(); - final String value = keyValue[1].trim(); - if (oldHeaderName.equalsIgnoreCase(key)) { - headerReplacerBuilder.setOldValue((value.isEmpty()) ? null : value); - } else if (newHeaderName.equalsIgnoreCase(key)) { - headerReplacerBuilder.setNewValue(value); - headerReplacers.add(headerReplacerBuilder.build()); - - headerReplacerBuilder = new HeaderReplacer.Builder(); - } else { - headerReplacerBuilder.putAgentAttribute(key, value); - } - } - } - } - } finally { - reader.close(); - } - } catch (IOException e) { - getContext().getLogger() - .warning("Cannot read '" + userAgentPropertiesUrl.toString() + "' due to: " + e.getMessage()); - } - } - - return headerReplacers; - } - - /** - * Returns the metadata associated to the given extension using the - * {@link MetadataService}. - * - * @param extension The extension to lookup. - * @return The matched metadata. - */ - private Metadata getMetadata(String extension) { - return getMetadataService().getMetadata(extension); - } - - /** - * Returns the metadata service of the parent application. - * - * @return The metadata service of the parent application. - */ - public MetadataService getMetadataService() { - return getApplication().getMetadataService(); - } - - /** - * Returns the tunnel service of the parent application. - * - * @return The tunnel service of the parent application. - */ - public TunnelService getTunnelService() { - return getApplication().getTunnelService(); - } - - /** - * Updates the client preferences based on file-like extensions. The matched - * extensions are removed from the last segment. - * - * See also section 3.6.1 of JAX-RS specification - * (https://jsr311.dev.java.net) - * - * @param request The request to update. - * @return True if the query has been updated, false otherwise. - */ - private boolean processExtensions(Request request) { - final TunnelService tunnelService = getTunnelService(); - boolean extensionsModified = false; - - // Tunnel the client preferences only for GET or HEAD requests - final Method method = request.getMethod(); - if (tunnelService.isPreferencesTunnel() && (method.equals(Method.GET) || method.equals(Method.HEAD))) { - final Reference resourceRef = request.getResourceRef(); - - if (resourceRef.hasExtensions()) { - final ClientInfo clientInfo = request.getClientInfo(); - boolean encodingFound = false; - boolean characterSetFound = false; - boolean mediaTypeFound = false; - boolean languageFound = false; - String extensions = resourceRef.getExtensions(); - - // Discover extensions from right to left and stop at the first - // unknown extension. Only one extension per type of metadata is - // also allowed: i.e., one language, one media type, one - // encoding, one character set. - while (true) { - final int lastIndexOfPoint = extensions.lastIndexOf('.'); - final String extension = extensions.substring(lastIndexOfPoint + 1); - final Metadata metadata = getMetadata(extension); - - if (!mediaTypeFound && (metadata instanceof MediaType)) { - updateMetadata(clientInfo, metadata); - mediaTypeFound = true; - } else if (!languageFound && (metadata instanceof Language)) { - updateMetadata(clientInfo, metadata); - languageFound = true; - } else if (!characterSetFound && (metadata instanceof CharacterSet)) { - updateMetadata(clientInfo, metadata); - characterSetFound = true; - } else if (!encodingFound && (metadata instanceof Encoding)) { - updateMetadata(clientInfo, metadata); - encodingFound = true; - } else { - // extension does not match -> break loop - break; - } - if (lastIndexOfPoint > 0) { - extensions = extensions.substring(0, lastIndexOfPoint); - } else { - // no more extensions -> break loop - extensions = ""; - break; - } - } - - // Update the extensions if necessary - if (encodingFound || characterSetFound || mediaTypeFound || languageFound) { - resourceRef.setExtensions(extensions); - extensionsModified = true; - } - } - } - - return extensionsModified; - } - - /** - * Updates the request method based on specific header. - * - * @param request The request to update. - */ - @SuppressWarnings("unchecked") - private void processHeaders(Request request) { - final TunnelService tunnelService = getTunnelService(); - - if (tunnelService.isMethodTunnel()) { - // get the headers - Series

extraHeaders = (Series
) request.getAttributes() - .get(HeaderConstants.ATTRIBUTE_HEADERS); - - if (extraHeaders != null) { - // look for the new value of the method - final String newMethodValue = extraHeaders.getFirstValue(getTunnelService().getMethodHeader(), true); - - if (newMethodValue != null && !newMethodValue.trim().isEmpty()) { - // set the current method to the new method - request.setMethod(Method.valueOf(newMethodValue)); - } - } - } - } - - /** - * Updates the request method and client preferences based on query parameters. - * The matched parameters are removed from the query. - * - * @param request The request to update. - * @return True if the query has been updated, false otherwise. - */ - private boolean processQuery(Request request) { - TunnelService tunnelService = getTunnelService(); - boolean queryModified = false; - Reference resourceRef = request.getResourceRef(); - - if (resourceRef.hasQuery()) { - Form query = resourceRef.getQueryAsForm(CharacterSet.UTF_8); - - // Tunnel the request method - Method method = request.getMethod(); - if (tunnelService.isMethodTunnel()) { - String methodName = query.getFirstValue(tunnelService.getMethodParameter()); - - Method tunnelledMethod = Method.valueOf(methodName); - // The OPTIONS method can be tunneled via GET requests. - if (tunnelledMethod != null && (Method.POST.equals(method) || Method.OPTIONS.equals(tunnelledMethod))) { - request.setMethod(tunnelledMethod); - query.removeFirst(tunnelService.getMethodParameter()); - queryModified = true; - } - } - - // Tunnel the client preferences - if (tunnelService.isPreferencesTunnel()) { - // Get the parameter names to look for - String charSetParameter = tunnelService.getCharacterSetParameter(); - String encodingParameter = tunnelService.getEncodingParameter(); - String languageParameter = tunnelService.getLanguageParameter(); - String mediaTypeParameter = tunnelService.getMediaTypeParameter(); - - // Get the preferences from the query - String acceptedCharSet = query.getFirstValue(charSetParameter); - String acceptedEncoding = query.getFirstValue(encodingParameter); - String acceptedLanguage = query.getFirstValue(languageParameter); - String acceptedMediaType = query.getFirstValue(mediaTypeParameter); - - // Updates the client preferences - ClientInfo clientInfo = request.getClientInfo(); - Metadata metadata = getMetadata(acceptedCharSet); - - if ((metadata == null) && (acceptedCharSet != null)) { - metadata = CharacterSet.valueOf(acceptedCharSet); - } - - if (metadata instanceof CharacterSet) { - updateMetadata(clientInfo, metadata); - query.removeFirst(charSetParameter); - queryModified = true; - } - - metadata = getMetadata(acceptedEncoding); - - if ((metadata == null) && (acceptedEncoding != null)) { - metadata = Encoding.valueOf(acceptedEncoding); - } - - if (metadata instanceof Encoding) { - updateMetadata(clientInfo, metadata); - query.removeFirst(encodingParameter); - queryModified = true; - } - - metadata = getMetadata(acceptedLanguage); - - if ((metadata == null) && (acceptedLanguage != null)) { - metadata = Language.valueOf(acceptedLanguage); - } - - if (metadata instanceof Language) { - updateMetadata(clientInfo, metadata); - query.removeFirst(languageParameter); - queryModified = true; - } - - metadata = getMetadata(acceptedMediaType); - - if ((metadata == null) && (acceptedMediaType != null)) { - metadata = MediaType.valueOf(acceptedMediaType); - } - - if (metadata instanceof MediaType) { - updateMetadata(clientInfo, metadata); - query.removeFirst(mediaTypeParameter); - queryModified = true; - } - } - - // Update the query if it has been modified - if (queryModified) { - request.getResourceRef().setQuery(query.getQueryString(CharacterSet.UTF_8)); - } - } - - return queryModified; - } - - /** - * Updates the client preferences according to the user agent properties (name, - * version, etc.) taken from the "agent.properties" file located in the - * classpath. See {@link ClientInfo#getAgentAttributes()} for more details.
- * The list of new media type preferences is loaded from a property file called - * "accept.properties" located in the classpath in the subdirectory - * "org/restlet/service". This property file is composed of blocks of - * properties. One "block" of properties starts either with the beginning of the - * properties file or with the end of the previous block. One block ends with - * the "acceptNew" property which contains the value of the new Accept header. - * Here is a sample block. - * - *
-	 * agentName: firefox
-	 * acceptOld: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,\*\/\*;q=0.5
-	 * acceptNew: application/xhtml+xml,text/html,text/xml;q=0.9,application/xml;q=0.9,text/plain;q=0.8,image/png,\*\/\*;q=0.5
-	 * 
- * - * Each declared property is a condition that must be filled in order to update - * the client preferences. For example, "agentName: firefox" expresses the fact - * this block concerns only "firefox" clients. - * - * The "acceptOld" property allows checking the value of the current "Accept" - * header. If the latest equals to the value of the "acceptOld" property, then - * the preferences will be updated. This is useful for Ajax clients that look - * like their browser (same agentName, agentVersion, etc.) but can provide their - * own "Accept" header. - * - * @param request the request to update. - */ - private void processUserAgent(Request request) { - final Map agentAttributes = request.getClientInfo().getAgentAttributes(); - if (agentAttributes != null) { - if (!this.acceptReplacers.isEmpty() || !this.acceptEncodingReplacers.isEmpty()) { - // Get the old Accept header value - @SuppressWarnings("unchecked") - Series
headers = (Series
) request.getAttributes() - .get(HeaderConstants.ATTRIBUTE_HEADERS); - - String acceptOld = (headers != null) ? headers.getFirstValue(HeaderConstants.HEADER_ACCEPT, true) - : null; - // Check each replacer - for (HeaderReplacer headerReplacer : this.acceptReplacers) { - if (headerReplacer.matchesConditions(agentAttributes, acceptOld)) { - ClientInfo clientInfo = new ClientInfo(); - PreferenceReader.addMediaTypes(headerReplacer.getHeaderNew(), clientInfo); - request.getClientInfo().setAcceptedMediaTypes(clientInfo.getAcceptedMediaTypes()); - break; - } - } - String acceptEncodingOld = (headers != null) - ? headers.getFirstValue(HeaderConstants.HEADER_ACCEPT_ENCODING, true) - : null; - // Check each replacer - for (HeaderReplacer headerReplacer : this.acceptEncodingReplacers) { - if (headerReplacer.matchesConditions(agentAttributes, acceptEncodingOld)) { - ClientInfo clientInfo = new ClientInfo(); - PreferenceReader.addEncodings(headerReplacer.getHeaderNew(), clientInfo); - request.getClientInfo().setAcceptedEncodings(clientInfo.getAcceptedEncodings()); - break; - } - } - } - } - } - - /** - * Updates the client info with the given metadata. It clears existing - * preferences for the same type of metadata if necessary. - * - * @param clientInfo The client info to update. - * @param metadata The metadata to use. - */ - private void updateMetadata(ClientInfo clientInfo, Metadata metadata) { - if (metadata instanceof CharacterSet) { - clientInfo.getAcceptedCharacterSets().clear(); - clientInfo.getAcceptedCharacterSets().add(new Preference<>((CharacterSet) metadata)); - } else if (metadata instanceof Encoding) { - clientInfo.getAcceptedEncodings().clear(); - clientInfo.getAcceptedEncodings().add(new Preference<>((Encoding) metadata)); - } else if (metadata instanceof Language) { - clientInfo.getAcceptedLanguages().clear(); - clientInfo.getAcceptedLanguages().add(new Preference<>((Language) metadata)); - } else if (metadata instanceof MediaType) { - clientInfo.getAcceptedMediaTypes().clear(); - clientInfo.getAcceptedMediaTypes().add(new Preference<>((MediaType) metadata)); - } - } - + /** + * Used to describe the replacement value for an old client preference and for a series of + * specific agent (i.e., web client) attributes. + * + * @author Thierry Boileau + */ + private static class HeaderReplacer { + + static class Builder { + Map agentAttributes = new HashMap(); + + String newValue; + + String oldValue; + + HeaderReplacer build() { + return new HeaderReplacer(oldValue, newValue, agentAttributes); + } + + void putAgentAttribute(String key, String value) { + agentAttributes.put(key, value); + } + + void setNewValue(String newValue) { + this.newValue = newValue; + } + + void setOldValue(String oldValue) { + this.oldValue = oldValue; + } + } + + /** Agent attributes that must be checked. */ + private final Map agentAttributes; + + /** New header value. */ + private final String headerNew; + + /** Old header value. */ + private final String headerOld; + + HeaderReplacer(String headerOld, String headerNew, Map agentAttributes) { + this.headerOld = headerOld; + this.headerNew = headerNew; + this.agentAttributes = Collections.unmodifiableMap(agentAttributes); + } + + public Map getAgentAttributes() { + return agentAttributes; + } + + public String getHeaderNew() { + return headerNew; + } + + public String getHeaderOld() { + return headerOld; + } + + /** + * Indicates if the current header replacer matches the request attributes. + * + * @param agentAttributes The user agent attributes to match. + * @param headerOld The facultative value of the current's request header to match. + * @return true if the given request's attributes match the current header replacer. + */ + public boolean matchesConditions(Map agentAttributes, String headerOld) { + // Check the conditions + boolean checked = true; + // Check that the agent properties match the properties + // set by the rule. + for (Iterator> iterator = + getAgentAttributes().entrySet().iterator(); + checked && iterator.hasNext(); ) { + Entry entry = iterator.next(); + String attribute = agentAttributes.get(entry.getKey()); + checked = (attribute != null && attribute.equalsIgnoreCase(entry.getValue())); + } + if (checked && getHeaderOld() != null) { + // If the rule defines an old header value, check that it is the + // same as the user agent's header value. + checked = getHeaderOld().equals(headerOld); + } + return checked; + } + } + + /** Used to replace accept-encoding header values. */ + private final List acceptEncodingReplacers = getAcceptEncodingReplacers(); + + /** Used to replace accept header values. */ + private final List acceptReplacers = getAcceptReplacers(); + + /** + * Constructor. + * + * @param context The parent context. + */ + public TunnelFilter(Context context) { + super(context); + } + + @Override + public int beforeHandle(Request request, Response response) { + if (getTunnelService().isUserAgentTunnel()) { + processUserAgent(request); + } + + if (getTunnelService().isExtensionsTunnel()) { + processExtensions(request); + } + + if (getTunnelService().isQueryTunnel()) { + processQuery(request); + } + + if (getTunnelService().isHeadersTunnel()) { + processHeaders(request); + } + + return CONTINUE; + } + + /** + * Returns the list of new accept-encoding header values. Each of them describes also a set of + * conditions required to set the new value. This method is used only to initialize the + * headerReplacers field. + * + * @return The list of new accept-encoding header values. + */ + private List getAcceptEncodingReplacers() { + // Load the accept.properties file. + return getHeaderReplacers( + Engine.getResource("org/restlet/service/accept-encoding.properties"), + "acceptEncodingOld", + "acceptEncodingNew"); + } + + /** + * Returns the list of new accept header values. Each of them describes also a set of conditions + * required to set the new value. This method is used only to initialize the headerReplacers + * field. + * + * @return The list of new accept header values. + */ + private List getAcceptReplacers() { + // Load the accept.properties file. + return getHeaderReplacers( + Engine.getResource("org/restlet/service/accept.properties"), + "acceptOld", + "acceptNew"); + } + + /** + * Returns the list of new header values. Each of them describes also a set of conditions + * required to set the new value. This method is used only to initialize the headerReplacers + * field. + * + * @param userAgentPropertiesUrl The URL of the properties file that describe replacement values + * based on the user agent string. + * @param oldHeaderName The name of the property that gives the value of the header to be + * replaced (could be null - in that case, the new value is unconditionally set. + * @param newHeaderName The name of the property that gives the replacement value. + * @return The list of new header values. + */ + private List getHeaderReplacers( + final URL userAgentPropertiesUrl, String oldHeaderName, String newHeaderName) { + List headerReplacers = new ArrayList(); + + if (userAgentPropertiesUrl != null) { + BufferedReader reader; + try { + reader = + new BufferedReader( + new InputStreamReader( + userAgentPropertiesUrl.openStream(), + CharacterSet.UTF_8.getName()), + IoUtils.BUFFER_SIZE); + + HeaderReplacer.Builder headerReplacerBuilder = new HeaderReplacer.Builder(); + + try { + // Read the entire file, excluding comment lines starting + // with "#" character. + String line = reader.readLine(); + for (; line != null; line = reader.readLine()) { + if (!line.startsWith("#")) { + final String[] keyValue = line.split(":"); + if (keyValue.length == 2) { + final String key = keyValue[0].trim(); + final String value = keyValue[1].trim(); + if (oldHeaderName.equalsIgnoreCase(key)) { + headerReplacerBuilder.setOldValue( + (value.isEmpty()) ? null : value); + } else if (newHeaderName.equalsIgnoreCase(key)) { + headerReplacerBuilder.setNewValue(value); + headerReplacers.add(headerReplacerBuilder.build()); + + headerReplacerBuilder = new HeaderReplacer.Builder(); + } else { + headerReplacerBuilder.putAgentAttribute(key, value); + } + } + } + } + } finally { + reader.close(); + } + } catch (IOException e) { + getContext() + .getLogger() + .warning( + "Cannot read '" + + userAgentPropertiesUrl + + "' due to: " + + e.getMessage()); + } + } + + return headerReplacers; + } + + /** + * Returns the metadata associated with the given extension using the {@link MetadataService}. + * + * @param extension The extension to lookup. + * @return The matched metadata. + */ + private Metadata getMetadata(String extension) { + return getMetadataService().getMetadata(extension); + } + + /** + * Returns the metadata service of the parent application. + * + * @return The metadata service of the parent application. + */ + public MetadataService getMetadataService() { + return getApplication().getMetadataService(); + } + + /** + * Returns the tunnel service of the parent application. + * + * @return The tunnel service of the parent application. + */ + public TunnelService getTunnelService() { + return getApplication().getTunnelService(); + } + + /** + * Updates the client preferences based on file-like extensions. The matched extensions are + * removed from the last segment. + * + *

See also section 3.6.1 of JAX-RS specification (https://jsr311.dev.java.net) + * + * @param request The request to update. + * @return True if the query has been updated, false otherwise. + */ + private boolean processExtensions(Request request) { + final TunnelService tunnelService = getTunnelService(); + boolean extensionsModified = false; + + // Tunnel the client preferences only for GET or HEAD requests + final Method method = request.getMethod(); + if (tunnelService.isPreferencesTunnel() + && (method.equals(Method.GET) || method.equals(Method.HEAD))) { + final Reference resourceRef = request.getResourceRef(); + + if (resourceRef.hasExtensions()) { + final ClientInfo clientInfo = request.getClientInfo(); + boolean encodingFound = false; + boolean characterSetFound = false; + boolean mediaTypeFound = false; + boolean languageFound = false; + String extensions = resourceRef.getExtensions(); + + // Discover extensions from right to left and stop at the first + // unknown extension. Only one extension per type of metadata is + // also allowed: i.e., one language, one media type, one + // encoding, one character set. + while (true) { + final int lastIndexOfPoint = extensions.lastIndexOf('.'); + final String extension = extensions.substring(lastIndexOfPoint + 1); + final Metadata metadata = getMetadata(extension); + + if (!mediaTypeFound && (metadata instanceof MediaType)) { + updateMetadata(clientInfo, metadata); + mediaTypeFound = true; + } else if (!languageFound && (metadata instanceof Language)) { + updateMetadata(clientInfo, metadata); + languageFound = true; + } else if (!characterSetFound && (metadata instanceof CharacterSet)) { + updateMetadata(clientInfo, metadata); + characterSetFound = true; + } else if (!encodingFound && (metadata instanceof Encoding)) { + updateMetadata(clientInfo, metadata); + encodingFound = true; + } else { + // extension does not match -> break loop + break; + } + if (lastIndexOfPoint > 0) { + extensions = extensions.substring(0, lastIndexOfPoint); + } else { + // no more extensions -> break loop + extensions = ""; + break; + } + } + + // Update the extensions if necessary + if (encodingFound || characterSetFound || mediaTypeFound || languageFound) { + resourceRef.setExtensions(extensions); + extensionsModified = true; + } + } + } + + return extensionsModified; + } + + /** + * Updates the request method based on a specific header. + * + * @param request The request to update. + */ + @SuppressWarnings("unchecked") + private void processHeaders(Request request) { + final TunnelService tunnelService = getTunnelService(); + + if (tunnelService.isMethodTunnel()) { + // get the headers + Series

extraHeaders = + (Series
) request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); + + if (extraHeaders != null) { + // look for the new value of the method + final String newMethodValue = + extraHeaders.getFirstValue(getTunnelService().getMethodHeader(), true); + + if (newMethodValue != null && !newMethodValue.trim().isEmpty()) { + // set the current method to the new method + request.setMethod(Method.valueOf(newMethodValue)); + } + } + } + } + + /** + * Updates the request method and client preferences based on query parameters. The matched + * parameters are removed from the query. + * + * @param request The request to update. + * @return True if the query has been updated, false otherwise. + */ + private boolean processQuery(Request request) { + TunnelService tunnelService = getTunnelService(); + boolean queryModified = false; + Reference resourceRef = request.getResourceRef(); + + if (resourceRef.hasQuery()) { + Form query = resourceRef.getQueryAsForm(CharacterSet.UTF_8); + + // Tunnel the request method + Method method = request.getMethod(); + if (tunnelService.isMethodTunnel()) { + String methodName = query.getFirstValue(tunnelService.getMethodParameter()); + + Method tunnelledMethod = Method.valueOf(methodName); + // The OPTIONS method can be tunneled via GET requests. + if (tunnelledMethod != null + && (Method.POST.equals(method) || Method.OPTIONS.equals(tunnelledMethod))) { + request.setMethod(tunnelledMethod); + query.removeFirst(tunnelService.getMethodParameter()); + queryModified = true; + } + } + + // Tunnel the client preferences + if (tunnelService.isPreferencesTunnel()) { + // Get the parameter names to look for + String charSetParameter = tunnelService.getCharacterSetParameter(); + String encodingParameter = tunnelService.getEncodingParameter(); + String languageParameter = tunnelService.getLanguageParameter(); + String mediaTypeParameter = tunnelService.getMediaTypeParameter(); + + // Get the preferences from the query + String acceptedCharSet = query.getFirstValue(charSetParameter); + String acceptedEncoding = query.getFirstValue(encodingParameter); + String acceptedLanguage = query.getFirstValue(languageParameter); + String acceptedMediaType = query.getFirstValue(mediaTypeParameter); + + // Updates the client preferences + ClientInfo clientInfo = request.getClientInfo(); + Metadata metadata = getMetadata(acceptedCharSet); + + if ((metadata == null) && (acceptedCharSet != null)) { + metadata = CharacterSet.valueOf(acceptedCharSet); + } + + if (metadata instanceof CharacterSet) { + updateMetadata(clientInfo, metadata); + query.removeFirst(charSetParameter); + queryModified = true; + } + + metadata = getMetadata(acceptedEncoding); + + if ((metadata == null) && (acceptedEncoding != null)) { + metadata = Encoding.valueOf(acceptedEncoding); + } + + if (metadata instanceof Encoding) { + updateMetadata(clientInfo, metadata); + query.removeFirst(encodingParameter); + queryModified = true; + } + + metadata = getMetadata(acceptedLanguage); + + if ((metadata == null) && (acceptedLanguage != null)) { + metadata = Language.valueOf(acceptedLanguage); + } + + if (metadata instanceof Language) { + updateMetadata(clientInfo, metadata); + query.removeFirst(languageParameter); + queryModified = true; + } + + metadata = getMetadata(acceptedMediaType); + + if ((metadata == null) && (acceptedMediaType != null)) { + metadata = MediaType.valueOf(acceptedMediaType); + } + + if (metadata instanceof MediaType) { + updateMetadata(clientInfo, metadata); + query.removeFirst(mediaTypeParameter); + queryModified = true; + } + } + + // Update the query if it has been modified + if (queryModified) { + request.getResourceRef().setQuery(query.getQueryString(CharacterSet.UTF_8)); + } + } + + return queryModified; + } + + /** + * Updates the client preferences according to the user agent properties (name, version, etc.) + * taken from the "agent.properties" file located in the classpath. See {@link + * ClientInfo#getAgentAttributes()} for more details.
+ * The list of new media type preferences is loaded from a property file called + * "accept.properties" located in the classpath in the subdirectory "org/restlet/service". This + * property file is composed of blocks of properties. One "block" of properties starts either + * with the beginning of the properties file or with the end of the previous block. One block + * ends with the "acceptNew" property which contains the value of the new Accept header. Here is + * a sample block. + * + *
+     * agentName: firefox
+     * acceptOld: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,\*\/\*;q=0.5
+     * acceptNew: application/xhtml+xml,text/html,text/xml;q=0.9,application/xml;q=0.9,text/plain;q=0.8,image/png,\*\/\*;q=0.5
+     * 
+ * + * Each declared property is a condition that must be filled to update the client preferences. + * For example, "agentName: firefox" expresses the fact this block concerns only "firefox" + * clients. + * + *

The "acceptOld" property allows checking the value of the current "Accept" header. If the + * latest equals to the value of the "acceptOld" property, then the preferences will be updated. + * This is useful for Ajax clients that look like their browser (same agentName, agentVersion, + * etc.) but can provide their own "Accept" header. + * + * @param request the request to update. + */ + private void processUserAgent(Request request) { + final Map agentAttributes = request.getClientInfo().getAgentAttributes(); + if (agentAttributes != null) { + if (!this.acceptReplacers.isEmpty() || !this.acceptEncodingReplacers.isEmpty()) { + // Get the old Accept header value + @SuppressWarnings("unchecked") + Series

headers = + (Series
) + request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); + + String acceptOld = + (headers != null) + ? headers.getFirstValue(HeaderConstants.HEADER_ACCEPT, true) + : null; + // Check each replacer + for (HeaderReplacer headerReplacer : this.acceptReplacers) { + if (headerReplacer.matchesConditions(agentAttributes, acceptOld)) { + ClientInfo clientInfo = new ClientInfo(); + PreferenceReader.addMediaTypes(headerReplacer.getHeaderNew(), clientInfo); + request.getClientInfo() + .setAcceptedMediaTypes(clientInfo.getAcceptedMediaTypes()); + break; + } + } + String acceptEncodingOld = + (headers != null) + ? headers.getFirstValue( + HeaderConstants.HEADER_ACCEPT_ENCODING, true) + : null; + // Check each replacer + for (HeaderReplacer headerReplacer : this.acceptEncodingReplacers) { + if (headerReplacer.matchesConditions(agentAttributes, acceptEncodingOld)) { + ClientInfo clientInfo = new ClientInfo(); + PreferenceReader.addEncodings(headerReplacer.getHeaderNew(), clientInfo); + request.getClientInfo() + .setAcceptedEncodings(clientInfo.getAcceptedEncodings()); + break; + } + } + } + } + } + + /** + * Updates the client info with the given metadata. It clears existing preferences for the same + * type of metadata if necessary. + * + * @param clientInfo The client info to update. + * @param metadata The metadata to use. + */ + private void updateMetadata(ClientInfo clientInfo, Metadata metadata) { + if (metadata instanceof CharacterSet) { + clientInfo.getAcceptedCharacterSets().clear(); + clientInfo.getAcceptedCharacterSets().add(new Preference<>((CharacterSet) metadata)); + } else if (metadata instanceof Encoding) { + clientInfo.getAcceptedEncodings().clear(); + clientInfo.getAcceptedEncodings().add(new Preference<>((Encoding) metadata)); + } else if (metadata instanceof Language) { + clientInfo.getAcceptedLanguages().clear(); + clientInfo.getAcceptedLanguages().add(new Preference<>((Language) metadata)); + } else if (metadata instanceof MediaType) { + clientInfo.getAcceptedMediaTypes().clear(); + clientInfo.getAcceptedMediaTypes().add(new Preference<>((MediaType) metadata)); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java b/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java index 009c7fbe30..c03f88a76a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; +import java.util.logging.Level; import org.restlet.Client; import org.restlet.Request; import org.restlet.Response; @@ -16,70 +16,73 @@ import org.restlet.routing.Route; import org.restlet.routing.Router; -import java.util.logging.Level; - /** * Router scorer based on a target client connector. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class ClientRoute extends Route { - /** - * Constructor. - * - * @param router The parent router. - * @param target The target client. - */ - public ClientRoute(Router router, Client target) { - super(router, target); - } + /** + * Constructor. + * + * @param router The parent router. + * @param target The target client. + */ + public ClientRoute(Router router, Client target) { + super(router, target); + } - /** - * Returns the target client. - * - * @return The target client. - */ - public Client getClient() { - return (Client) getNext(); - } + /** + * Returns the target client. + * + * @return The target client. + */ + public Client getClient() { + return (Client) getNext(); + } - /** - * Returns the score for a given call (between 0 and 1.0). - * - * @param request The request to score. - * @param response The response to score. - * @return The score for a given call (between 0 and 1.0). - */ - @Override - public float score(Request request, Response response) { - float result = 0F; + /** + * Returns the score for a given call (between 0 and 1.0). + * + * @param request The request to score. + * @param response The response to score. + * @return The score for a given call (between 0 and 1.0). + */ + @Override + public float score(Request request, Response response) { + float result = 0F; - // Add the protocol score - final Protocol protocol = request.getProtocol(); + // Add the protocol score + final Protocol protocol = request.getProtocol(); - if (protocol == null) { - getLogger().warning("Unable to determine the protocol to use for this call."); - } else if (getClient().getProtocols().contains(protocol)) { - result = 1.0F; - } + if (protocol == null) { + getLogger().warning("Unable to determine the protocol to use for this call."); + } else if (getClient().getProtocols().contains(protocol)) { + result = 1.0F; + } - if (getLogger().isLoggable(Level.FINER)) { - getLogger().finer("Call score for the \"" + getClient().getProtocols().toString() + "\" client: " + result); - } + if (getLogger().isLoggable(Level.FINER)) { + getLogger() + .finer( + "Call score for the \"" + + getClient().getProtocols().toString() + + "\" client: " + + result); + } - return result; - } + return result; + } - /** - * Sets the next client. - * - * @param next The next client. - */ - public void setNext(Client next) { - super.setNext(next); - } + /** + * Sets the next client. + * + * @param next The next client. + */ + public void setNext(Client next) { + super.setNext(next); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java index 64bdc29dee..53ceb99634 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java @@ -1,84 +1,88 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; -import org.restlet.*; -import org.restlet.routing.Router; - import java.util.logging.Level; +import org.restlet.Client; +import org.restlet.Component; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.routing.Router; /** - * Router that collects calls from all applications and dispatches them to the - * appropriate client connectors. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Router that collects calls from all applications and dispatches them to the appropriate client + * connectors. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class ClientRouter extends Router { - /** The parent component. */ - private volatile Component component; + /** The parent component. */ + private final Component component; - /** - * Constructor. - * - * @param component The parent component. - */ - public ClientRouter(Component component) { - super((component == null) ? null : component.getContext().createChildContext()); - this.component = component; - } + /** + * Constructor. + * + * @param component The parent component. + */ + public ClientRouter(Component component) { + super((component == null) ? null : component.getContext().createChildContext()); + this.component = component; + } - @Override - protected void logRoute(org.restlet.routing.Route route) { - if (getLogger().isLoggable(Level.FINE)) { - if (route instanceof ClientRoute) { - Client client = ((ClientRoute) route).getClient(); + @Override + protected void logRoute(org.restlet.routing.Route route) { + if (getLogger().isLoggable(Level.FINE)) { + if (route instanceof ClientRoute) { + Client client = ((ClientRoute) route).getClient(); - getLogger().fine("This client was selected: \"" + client.getProtocols() + "\""); - } else { - super.logRoute(route); - } - } - } + getLogger().fine("This client was selected: \"" + client.getProtocols() + "\""); + } else { + super.logRoute(route); + } + } + } - @Override - public Restlet getNext(Request request, Response response) { - Restlet result = super.getNext(request, response); + @Override + public Restlet getNext(Request request, Response response) { + Restlet result = super.getNext(request, response); - if (result == null) { - getLogger().warning("The protocol used by this request is not declared in the list of client connectors. (" - + request.getResourceRef().getSchemeProtocol() - + "). In case you are using an instance of the Component class, check its \"clients\" property."); - } - return result; - } + if (result == null) { + getLogger() + .warning( + "The protocol used by this request is not declared in the list of client connectors. (" + + request.getResourceRef().getSchemeProtocol() + + "). In case you are using an instance of the Component class, check its \"clients\" property."); + } + return result; + } - /** - * Returns the parent component. - * - * @return The parent component. - */ - private Component getComponent() { - return this.component; - } + /** + * Returns the parent component. + * + * @return The parent component. + */ + private Component getComponent() { + return this.component; + } - /** Starts the Restlet. */ - @Override - public synchronized void start() throws Exception { - for (final Client client : getComponent().getClients()) { - getRoutes().add(new ClientRoute(this, client)); - } + /** Starts the Restlet. */ + @Override + public synchronized void start() throws Exception { + for (final Client client : getComponent().getClients()) { + getRoutes().add(new ClientRoute(this, client)); + } - super.start(); - } + super.start(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java index 831602a815..4989aa0787 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; +import java.util.Iterator; import org.restlet.Component; import org.restlet.Request; import org.restlet.Response; @@ -17,119 +17,122 @@ import org.restlet.engine.util.TemplateDispatcher; import org.restlet.routing.VirtualHost; -import java.util.Iterator; - /** * Component client dispatcher. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state as member variables. - * + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state as member variables. + * * @author Jerome Louvel */ public class ComponentClientDispatcher extends TemplateDispatcher { - /** The component context. */ - private ComponentContext componentContext; - - /** - * Constructor. - * - * @param componentContext The component context. - */ - public ComponentClientDispatcher(ComponentContext componentContext) { - this.componentContext = componentContext; - } - - @Override - protected int doHandle(Request request, Response response) { - int result = CONTINUE; - Protocol protocol = request.getProtocol(); - - if (protocol.equals(Protocol.RIAP)) { - // Let's dispatch it - LocalReference cr = new LocalReference(request.getResourceRef()); - Component component = getComponent(); - - if (component != null) { - if (cr.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { - // This causes the baseRef of the resource reference to be - // set as if it had actually arrived from a server - // connector. - request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); - - // Ask the private internal route to handle the call - component.getInternalRouter().handle(request, response); - } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_HOST) { - VirtualHost host = null; - VirtualHost currentHost = null; - final Integer hostHashCode = VirtualHost.getCurrent(); - - // Lookup the virtual host - for (final Iterator hostIter = getComponent().getHosts().iterator(); (host == null) - && hostIter.hasNext();) { - currentHost = hostIter.next(); - - if (currentHost.hashCode() == hostHashCode) { - host = currentHost; - } - } - - if ((host == null) && (component.getDefaultHost() != null)) { - if (component.getDefaultHost().hashCode() == hostHashCode) { - host = component.getDefaultHost(); - } - } - - if (host != null) { - // This causes the baseRef of the resource reference to - // be set as if it had actually arrived from a server - // connector. - request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); - - // Ask the virtual host to handle the call - host.handle(request, response); - } else { - getLogger().warning("No virtual host is available to route the RIAP Host request."); - result = STOP; - } - } else { - getLogger().warning("Unknown RIAP authority. Only \"component\" is supported."); - result = STOP; - } - } else { - getLogger().warning("No component is available to route the RIAP request."); - result = STOP; - } - } else { - getComponentContext().getComponentHelper().getClientRouter().handle(request, response); - } - - return result; - } - - /** - * Returns the parent component. - * - * @return The parent component. - */ - private Component getComponent() { - Component result = null; - - if ((getComponentContext() != null) && (getComponentContext().getComponentHelper() != null)) { - result = getComponentContext().getComponentHelper().getHelped(); - } - - return result; - - } - - /** - * Returns the component context. - * - * @return The component context. - */ - private ComponentContext getComponentContext() { - return componentContext; - } + /** The component context. */ + private final ComponentContext componentContext; + + /** + * Constructor. + * + * @param componentContext The component context. + */ + public ComponentClientDispatcher(ComponentContext componentContext) { + this.componentContext = componentContext; + } + + @Override + protected int doHandle(Request request, Response response) { + int result = CONTINUE; + Protocol protocol = request.getProtocol(); + + if (protocol.equals(Protocol.RIAP)) { + // Let's dispatch it + LocalReference cr = new LocalReference(request.getResourceRef()); + Component component = getComponent(); + + if (component != null) { + if (cr.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { + // This causes the baseRef of the resource reference to be + // set as if it had actually arrived from a server + // connector. + request.getResourceRef() + .setBaseRef(request.getResourceRef().getHostIdentifier()); + + // Ask the private internal route to handle the call + component.getInternalRouter().handle(request, response); + } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_HOST) { + VirtualHost host = null; + VirtualHost currentHost = null; + final Integer hostHashCode = VirtualHost.getCurrent(); + + // Lookup the virtual host + for (final Iterator hostIter = + getComponent().getHosts().iterator(); + (host == null) && hostIter.hasNext(); ) { + currentHost = hostIter.next(); + + if (currentHost.hashCode() == hostHashCode) { + host = currentHost; + } + } + + if ((host == null) && (component.getDefaultHost() != null)) { + if (component.getDefaultHost().hashCode() == hostHashCode) { + host = component.getDefaultHost(); + } + } + + if (host != null) { + // This causes the baseRef of the resource reference to + // be set as if it had actually arrived from a server + // connector. + request.getResourceRef() + .setBaseRef(request.getResourceRef().getHostIdentifier()); + + // Ask the virtual host to handle the call + host.handle(request, response); + } else { + getLogger() + .warning( + "No virtual host is available to route the RIAP Host request."); + result = STOP; + } + } else { + getLogger().warning("Unknown RIAP authority. Only \"component\" is supported."); + result = STOP; + } + } else { + getLogger().warning("No component is available to route the RIAP request."); + result = STOP; + } + } else { + getComponentContext().getComponentHelper().getClientRouter().handle(request, response); + } + + return result; + } + + /** + * Returns the parent component. + * + * @return The parent component. + */ + private Component getComponent() { + Component result = null; + + if ((getComponentContext() != null) + && (getComponentContext().getComponentHelper() != null)) { + result = getComponentContext().getComponentHelper().getHelped(); + } + + return result; + } + + /** + * Returns the component context. + * + * @return The component context. + */ + private ComponentContext getComponentContext() { + return componentContext; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java index 1154caa4f6..b6ac2822c9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; import org.restlet.Context; @@ -15,47 +14,47 @@ /** * Context allowing access to the component's connectors. - * + * * @author Jerome Louvel */ public class ComponentContext extends Context { - /** The component helper. */ - private volatile ComponentHelper componentHelper; - - /** - * Constructor. - * - * @param componentHelper The component helper. - */ - public ComponentContext(ComponentHelper componentHelper) { - super(LogUtils.getLoggerName("org.restlet", componentHelper.getHelped())); - this.componentHelper = componentHelper; - setClientDispatcher(new ComponentClientDispatcher(this)); - setServerDispatcher(new ComponentServerDispatcher(this)); - setExecutorService(componentHelper.getHelped().getTaskService()); - } - - @Override - public Context createChildContext() { - return new ChildContext(getComponentHelper().getHelped().getContext()); - } - - /** - * Returns the component helper. - * - * @return The component helper. - */ - protected ComponentHelper getComponentHelper() { - return this.componentHelper; - } - - /** - * Sets the component helper. - * - * @param componentHelper The component helper. - */ - protected void setComponentHelper(ComponentHelper componentHelper) { - this.componentHelper = componentHelper; - } + /** The component helper. */ + private volatile ComponentHelper componentHelper; + + /** + * Constructor. + * + * @param componentHelper The component helper. + */ + public ComponentContext(ComponentHelper componentHelper) { + super(LogUtils.getLoggerName("org.restlet", componentHelper.getHelped())); + this.componentHelper = componentHelper; + setClientDispatcher(new ComponentClientDispatcher(this)); + setServerDispatcher(new ComponentServerDispatcher(this)); + setExecutorService(componentHelper.getHelped().getTaskService()); + } + + @Override + public Context createChildContext() { + return new ChildContext(getComponentHelper().getHelped().getContext()); + } + + /** + * Returns the component helper. + * + * @return The component helper. + */ + protected ComponentHelper getComponentHelper() { + return this.componentHelper; + } + + /** + * Sets the component helper. + * + * @param componentHelper The component helper. + */ + protected void setComponentHelper(ComponentHelper componentHelper) { + this.componentHelper = componentHelper; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java index d8d5d75c2c..eb88cd961b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java @@ -1,15 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; -import org.restlet.*; +import java.util.Iterator; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Component; +import org.restlet.Context; +import org.restlet.Restlet; +import org.restlet.Server; import org.restlet.data.Protocol; import org.restlet.engine.CompositeHelper; import org.restlet.routing.Filter; @@ -17,220 +22,228 @@ import org.restlet.routing.VirtualHost; import org.restlet.service.Service; -import java.util.Iterator; - /** * Component helper. - * + * * @author Jerome Louvel */ public class ComponentHelper extends CompositeHelper { - /** The internal client router. */ - private final ClientRouter clientRouter; - - /** The internal server router. */ - private volatile ServerRouter serverRouter; - - /** - * Constructor. - * - * @param component The helper component. - */ - public ComponentHelper(Component component) { - super(component); - component.setContext(new ComponentContext(this)); - this.clientRouter = new ClientRouter(getHelped()); - this.serverRouter = new ServerRouter(getHelped()); - } - - /** - * Check the applications attached to a virtual host. - * - * @param host The parent virtual host. - * @return True if the check succeeded. - * @throws Exception - */ - private boolean checkVirtualHost(VirtualHost host) throws Exception { - boolean result = true; - - if (host != null) { - for (Route route : host.getRoutes()) { - Restlet next = route.getNext(); - - if (next instanceof Application application) { - - if (application.getConnectorService() != null) { - if (application.getConnectorService().getClientProtocols() != null) { - for (Protocol clientProtocol : application.getConnectorService().getClientProtocols()) { - boolean clientFound = false; - - // Try to find a client connector matching the - // client protocol - Client client; - for (Iterator iter = getHelped().getClients().iterator(); !clientFound - && iter.hasNext();) { - client = iter.next(); - clientFound = client.getProtocols().contains(clientProtocol); - } - - if (!clientFound) { - getLogger().severe("Unable to start the application \"" + application.getName() - + "\". Client connector for protocol " + clientProtocol.getName() - + " is missing."); - result = false; - } - } - } - - if (application.getConnectorService().getServerProtocols() != null) { - for (Protocol serverProtocol : application.getConnectorService().getServerProtocols()) { - boolean serverFound = false; - - // Try to find a server connector matching the - // server protocol - Server server; - for (Iterator iter = getHelped().getServers().iterator(); !serverFound - && iter.hasNext();) { - server = iter.next(); - serverFound = server.getProtocols().contains(serverProtocol); - } - - if (!serverFound) { - getLogger().severe("Unable to start the application \"" + application.getName() - + "\". Server connector for protocol " + serverProtocol.getName() - + " is missing."); - result = false; - } - } - } - } - - if (result && application.isStopped()) { - application.start(); - } - } - } - } - - return result; - } - - /** - * Returns the internal client router. - * - * @return the internal client router. - */ - public ClientRouter getClientRouter() { - return this.clientRouter; - } - - /** - * Returns the internal host router. - * - * @return the internal host router. - */ - public ServerRouter getServerRouter() { - return this.serverRouter; - } - - /** - * Sets the internal server router. - * - * @param serverRouter The internal host router. - */ - public void setServerRouter(ServerRouter serverRouter) { - this.serverRouter = serverRouter; - } - - @Override - public synchronized void start() throws Exception { - // Checking if all applications have proper connectors - boolean success = checkVirtualHost(getHelped().getDefaultHost()); - - if (success) { - for (VirtualHost host : getHelped().getHosts()) { - success = success && checkVirtualHost(host); - } - } - - // Let's actually start the component - if (!success) { - getHelped().stop(); - } else { - Filter filter = null; - - for (Service service : getHelped().getServices()) { - if (service.isEnabled()) { - // Attach the service inbound filters - filter = service - .createInboundFilter((getContext() == null) ? null : getContext().createChildContext()); - - if (filter != null) { - addInboundFilter(filter); - } - - // Attach the service outbound filters - filter = service - .createOutboundFilter((getContext() == null) ? null : getContext().createChildContext()); - - if (filter != null) { - addOutboundFilter(filter); - } - } - } - - // Re-attach the original filter's attached Restlet - setInboundNext(getServerRouter()); - } - } - - @Override - public synchronized void stop() throws Exception { - // Stop the server's router - getServerRouter().stop(); - - // Stop all applications - stopHostApplications(getHelped().getDefaultHost()); - - for (VirtualHost host : getHelped().getHosts()) { - stopHostApplications(host); - } - } - - /** - * Stop all applications attached to a virtual host - * - * @param host - * @throws Exception - */ - private void stopHostApplications(VirtualHost host) throws Exception { - for (Route route : host.getRoutes()) { - if (route.getNext().isStarted()) { - route.getNext().stop(); - } - } - } - - /** - * Set the new server router that will compute the new routes when the first - * request will be received (automatic start). - */ - @Override - public void update() throws Exception { - // Note the old router to be able to stop it at the end - ServerRouter oldRouter = getServerRouter(); - - // Set the new server router that will compute the new routes when the - // first request will be received (automatic start). - setServerRouter(new ServerRouter(getHelped())); - - // Replace the old server router - setInboundNext(getServerRouter()); - - // Stop the old server router - if (oldRouter != null) { - oldRouter.stop(); - } - } - + /** The internal client router. */ + private final ClientRouter clientRouter; + + /** The internal server router. */ + private volatile ServerRouter serverRouter; + + /** + * Constructor. + * + * @param component The helper component. + */ + public ComponentHelper(Component component) { + super(component); + component.setContext(new ComponentContext(this)); + this.clientRouter = new ClientRouter(getHelped()); + this.serverRouter = new ServerRouter(getHelped()); + } + + /** + * Check the applications attached to a virtual host. + * + * @param host The parent virtual host. + * @return True if the check succeeded. + * @throws Exception + */ + private boolean checkVirtualHost(VirtualHost host) throws Exception { + boolean result = true; + + if (host != null) { + for (Route route : host.getRoutes()) { + Restlet next = route.getNext(); + + if (next instanceof Application application) { + + if (application.getConnectorService() != null) { + if (application.getConnectorService().getClientProtocols() != null) { + for (Protocol clientProtocol : + application.getConnectorService().getClientProtocols()) { + boolean clientFound = false; + + // Try to find a client connector matching the + // client protocol + Client client; + for (Iterator iter = getHelped().getClients().iterator(); + !clientFound && iter.hasNext(); ) { + client = iter.next(); + clientFound = client.getProtocols().contains(clientProtocol); + } + + if (!clientFound) { + getLogger() + .severe( + "Unable to start the application \"" + + application.getName() + + "\". Client connector for protocol " + + clientProtocol.getName() + + " is missing."); + result = false; + } + } + } + + if (application.getConnectorService().getServerProtocols() != null) { + for (Protocol serverProtocol : + application.getConnectorService().getServerProtocols()) { + boolean serverFound = false; + + // Try to find a server connector matching the + // server protocol + Server server; + for (Iterator iter = getHelped().getServers().iterator(); + !serverFound && iter.hasNext(); ) { + server = iter.next(); + serverFound = server.getProtocols().contains(serverProtocol); + } + + if (!serverFound) { + getLogger() + .severe( + "Unable to start the application \"" + + application.getName() + + "\". Server connector for protocol " + + serverProtocol.getName() + + " is missing."); + result = false; + } + } + } + } + + if (result && application.isStopped()) { + application.start(); + } + } + } + } + + return result; + } + + /** + * Returns the internal client router. + * + * @return the internal client router. + */ + public ClientRouter getClientRouter() { + return this.clientRouter; + } + + /** + * Returns the internal host router. + * + * @return the internal host router. + */ + public ServerRouter getServerRouter() { + return this.serverRouter; + } + + /** + * Sets the internal server router. + * + * @param serverRouter The internal host router. + */ + public void setServerRouter(ServerRouter serverRouter) { + this.serverRouter = serverRouter; + } + + @Override + public synchronized void start() throws Exception { + // Checking if all applications have proper connectors + boolean success = checkVirtualHost(getHelped().getDefaultHost()); + + if (success) { + for (VirtualHost host : getHelped().getHosts()) { + success = success && checkVirtualHost(host); + } + } + + // Let's actually start the component + if (!success) { + getHelped().stop(); + } else { + Filter filter = null; + + for (Service service : getHelped().getServices()) { + if (service.isEnabled()) { + // Attach the service inbound filters + Context context = + (getContext() == null) ? null : getContext().createChildContext(); + + filter = service.createInboundFilter(context); + if (filter != null) { + addInboundFilter(filter); + } + + // Attach the service outbound filters + context = (getContext() == null) ? null : getContext().createChildContext(); + + filter = service.createOutboundFilter(context); + if (filter != null) { + addOutboundFilter(filter); + } + } + } + + // Re-attach the original filter's attached Restlet + setInboundNext(getServerRouter()); + } + } + + @Override + public synchronized void stop() throws Exception { + // Stop the server's router + getServerRouter().stop(); + + // Stop all applications + stopHostApplications(getHelped().getDefaultHost()); + + for (VirtualHost host : getHelped().getHosts()) { + stopHostApplications(host); + } + } + + /** + * Stop all applications attached to a virtual host + * + * @param host + * @throws Exception + */ + private void stopHostApplications(VirtualHost host) throws Exception { + for (Route route : host.getRoutes()) { + if (route.getNext().isStarted()) { + route.getNext().stop(); + } + } + } + + /** + * Set the new server router that will compute the new routes when the first request is received + * (automatic start). + */ + @Override + public void update() throws Exception { + // Note the old router to be able to stop it at the end + ServerRouter oldRouter = getServerRouter(); + + // Set the new server router that will compute the new routes when the + // first request is received (automatic start). + setServerRouter(new ServerRouter(getHelped())); + + // Replace the old server router + setInboundNext(getServerRouter()); + + // Stop the old server router + if (oldRouter != null) { + oldRouter.stop(); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java index ea7b943387..5e7adbd257 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; import org.restlet.Request; @@ -15,53 +14,52 @@ /** * Component server dispatcher. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state as member variables. - * + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state as member variables. + * * @author Jerome Louvel */ public class ComponentServerDispatcher extends TemplateDispatcher { - /** The component context. */ - private ComponentContext componentContext; + /** The component context. */ + private final ComponentContext componentContext; - /** - * Constructor. - * - * @param componentContext The component context. - */ - public ComponentServerDispatcher(ComponentContext componentContext) { - this.componentContext = componentContext; - } + /** + * Constructor. + * + * @param componentContext The component context. + */ + public ComponentServerDispatcher(ComponentContext componentContext) { + this.componentContext = componentContext; + } - @Override - public int beforeHandle(Request request, Response response) { - int result = super.beforeHandle(request, response); + @Override + public int beforeHandle(Request request, Response response) { + int result = super.beforeHandle(request, response); - // This causes the baseRef of the resource reference to be set - // as if it had actually arrived from a server connector. - request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); + // This causes the baseRef of the resource reference to be set + // as if it had actually arrived from a server connector. + request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); - return result; - } + return result; + } - @Override - protected int doHandle(Request request, Response response) { + @Override + protected int doHandle(Request request, Response response) { // Ask the server router to actually handle the call - getComponentContext().getComponentHelper().getServerRouter().handle(request, response); - - return CONTINUE; - } + getComponentContext().getComponentHelper().getServerRouter().handle(request, response); - /** - * Returns the component context. - * - * @return The component context. - */ - private ComponentContext getComponentContext() { - return componentContext; - } + return CONTINUE; + } + /** + * Returns the component context. + * + * @return The component context. + */ + private ComponentContext getComponentContext() { + return componentContext; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java index 902879d058..732d6fc671 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java @@ -1,188 +1,195 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; +import java.util.logging.Level; +import java.util.regex.Pattern; import org.restlet.Request; import org.restlet.Response; import org.restlet.routing.Route; import org.restlet.routing.Router; import org.restlet.routing.VirtualHost; -import java.util.logging.Level; -import java.util.regex.Pattern; - /** * Route based on a target VirtualHost. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class HostRoute extends Route { - /** - * Constructor. - * - * @param router The parent router. - * @param target The target virtual host. - */ - public HostRoute(Router router, VirtualHost target) { - super(router, target); - } - - /** - * Allows filtering before processing by the next Restlet. Set the base - * reference. - * - * @param request The request to handle. - * @param response The response to update. - * @return The continuation status. - */ - @Override - protected int beforeHandle(Request request, Response response) { - if (request.getHostRef() == null) { - request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); - } else { - request.getResourceRef().setBaseRef(request.getHostRef()); - } - - if (request.isLoggable() && getLogger().isLoggable(Level.FINE)) { - getLogger().fine("Base URI: \"" + request.getResourceRef().getBaseRef() + "\". Remaining part: \"" - + request.getResourceRef().getRemainingPart() + "\""); - } - - return CONTINUE; - } - - /** - * Returns the target virtual host. - * - * @return The target virtual host. - */ - public VirtualHost getVirtualHost() { - return (VirtualHost) getNext(); - } - - /** - * Matches a formatted string against a regex pattern, in a case insensitive - * manner. - * - * @param regex The pattern to use. - * @param formattedString The formatted string to match. - * @return True if the formatted string matched the pattern. - */ - private boolean matches(String regex, String formattedString) { - return Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(formattedString).matches(); - } - - /** - * Returns the score for a given call (between 0 and 1.0). - * - * @param request The request to score. - * @param response The response to score. - * @return The score for a given call (between 0 and 1.0). - */ - @Override - public float score(Request request, Response response) { - float result = 0F; - - // Prepare the value to be matched - String hostDomain = ""; - String hostPort = ""; - String hostScheme = ""; - - if (request.getHostRef() != null) { - hostDomain = request.getHostRef().getHostDomain(); - - if (hostDomain == null) { - hostDomain = ""; - } - - int basePortValue = request.getHostRef().getHostPort(); - - if (basePortValue == -1) { - basePortValue = request.getHostRef().getSchemeProtocol().getDefaultPort(); - } - - hostPort = Integer.toString(basePortValue); - - hostScheme = request.getHostRef().getScheme(); - - if (hostScheme == null) { - hostScheme = ""; - } - } - - if (request.getResourceRef() != null) { - String resourceDomain = request.getResourceRef().getHostDomain(); - - if (resourceDomain == null) { - resourceDomain = ""; - } - - int resourcePortValue = request.getResourceRef().getHostPort(); - - if (resourcePortValue == -1 && request.getResourceRef().getSchemeProtocol() != null) { - resourcePortValue = request.getResourceRef().getSchemeProtocol().getDefaultPort(); - } - - String resourcePort = (resourcePortValue == -1) ? "" : Integer.toString(resourcePortValue); - - String resourceScheme = request.getResourceRef().getScheme(); - - if (resourceScheme == null) { - resourceScheme = ""; - } - - String serverAddress = response.getServerInfo().getAddress(); - - if (serverAddress == null) { - serverAddress = ""; - } - - int serverPortValue = response.getServerInfo().getPort(); - - if (serverPortValue == -1) { - serverPortValue = request.getProtocol().getDefaultPort(); - } - - String serverPort = Integer.toString(response.getServerInfo().getPort()); - - // Check if all the criteria match - if (matches(getVirtualHost().getHostDomain(), hostDomain) - && matches(getVirtualHost().getHostPort(), hostPort) - && matches(getVirtualHost().getHostScheme(), hostScheme) - && matches(getVirtualHost().getResourceDomain(), resourceDomain) - && matches(getVirtualHost().getResourcePort(), resourcePort) - && matches(getVirtualHost().getResourceScheme(), resourceScheme) - && matches(getVirtualHost().getServerAddress(), serverAddress) - && matches(getVirtualHost().getServerPort(), serverPort)) { - result = 1F; - } - } - - // Log the result of the matching - if (getLogger().isLoggable(Level.FINER)) { - getLogger().finer("Call score for the \"" + getVirtualHost().getName() + "\" host: " + result); - } - - return result; - } - - /** - * Sets the next virtual host. - * - * @param next The next virtual host. - */ - public void setNext(VirtualHost next) { - super.setNext(next); - } + /** + * Constructor. + * + * @param router The parent router. + * @param target The target virtual host. + */ + public HostRoute(Router router, VirtualHost target) { + super(router, target); + } + + /** + * Allows filtering before processing by the next Restlet. Set the base reference. + * + * @param request The request to handle. + * @param response The response to update. + * @return The continuation status. + */ + @Override + protected int beforeHandle(Request request, Response response) { + if (request.getHostRef() == null) { + request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); + } else { + request.getResourceRef().setBaseRef(request.getHostRef()); + } + + if (request.isLoggable() && getLogger().isLoggable(Level.FINE)) { + getLogger() + .fine( + "Base URI: \"" + + request.getResourceRef().getBaseRef() + + "\". Remaining part: \"" + + request.getResourceRef().getRemainingPart() + + "\""); + } + + return CONTINUE; + } + + /** + * Returns the target virtual host. + * + * @return The target virtual host. + */ + public VirtualHost getVirtualHost() { + return (VirtualHost) getNext(); + } + + /** + * Matches a formatted string against a regex pattern, in a case-insensitive manner. + * + * @param regex The pattern to use. + * @param formattedString The formatted string to match. + * @return True if the formatted string matched the pattern. + */ + private boolean matches(String regex, String formattedString) { + return Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(formattedString).matches(); + } + + /** + * Returns the score for a given call (between 0 and 1.0). + * + * @param request The request to score. + * @param response The response to score. + * @return The score for a given call (between 0 and 1.0). + */ + @Override + public float score(Request request, Response response) { + float result = 0F; + + // Prepare the value to be matched + String hostDomain = ""; + String hostPort = ""; + String hostScheme = ""; + + if (request.getHostRef() != null) { + hostDomain = request.getHostRef().getHostDomain(); + + if (hostDomain == null) { + hostDomain = ""; + } + + int basePortValue = request.getHostRef().getHostPort(); + + if (basePortValue == -1) { + basePortValue = request.getHostRef().getSchemeProtocol().getDefaultPort(); + } + + hostPort = Integer.toString(basePortValue); + + hostScheme = request.getHostRef().getScheme(); + + if (hostScheme == null) { + hostScheme = ""; + } + } + + if (request.getResourceRef() != null) { + String resourceDomain = request.getResourceRef().getHostDomain(); + + if (resourceDomain == null) { + resourceDomain = ""; + } + + int resourcePortValue = request.getResourceRef().getHostPort(); + + if (resourcePortValue == -1 && request.getResourceRef().getSchemeProtocol() != null) { + resourcePortValue = request.getResourceRef().getSchemeProtocol().getDefaultPort(); + } + + String resourcePort = + (resourcePortValue == -1) ? "" : Integer.toString(resourcePortValue); + + String resourceScheme = request.getResourceRef().getScheme(); + + if (resourceScheme == null) { + resourceScheme = ""; + } + + String serverAddress = response.getServerInfo().getAddress(); + + if (serverAddress == null) { + serverAddress = ""; + } + + int serverPortValue = response.getServerInfo().getPort(); + + if (serverPortValue == -1) { + serverPortValue = request.getProtocol().getDefaultPort(); + } + + String serverPort = Integer.toString(response.getServerInfo().getPort()); + + // Check if all the criteria match + if (matches(getVirtualHost().getHostDomain(), hostDomain) + && matches(getVirtualHost().getHostPort(), hostPort) + && matches(getVirtualHost().getHostScheme(), hostScheme) + && matches(getVirtualHost().getResourceDomain(), resourceDomain) + && matches(getVirtualHost().getResourcePort(), resourcePort) + && matches(getVirtualHost().getResourceScheme(), resourceScheme) + && matches(getVirtualHost().getServerAddress(), serverAddress) + && matches(getVirtualHost().getServerPort(), serverPort)) { + result = 1F; + } + } + + // Log the result of the matching + if (getLogger().isLoggable(Level.FINER)) { + getLogger() + .finer( + "Call score for the \"" + + getVirtualHost().getName() + + "\" host: " + + result); + } + + return result; + } + + /** + * Sets the next virtual host. + * + * @param next The next virtual host. + */ + public void setNext(VirtualHost next) { + super.setNext(next); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java index 1b121cbfea..a75f0a5606 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; import org.restlet.Context; @@ -20,77 +19,77 @@ import org.restlet.routing.TemplateRoute; /** - * Provides the behavior of the internal router of a Component. It overrides the - * default behavior of a classic Router. - * + * Provides the behavior of the internal router of a Component. It overrides the default behavior of + * a classic Router. + * * @author Thierry Boileau */ public class InternalRouter extends Router { - /** - * Constructor. - * - * @param context The current context. - */ - public InternalRouter(Context context) { - super(context); - // Override Router's default modes - setDefaultMatchingMode(Template.MODE_STARTS_WITH); - setRoutingMode(Router.MODE_BEST_MATCH); - } - - @Override - protected TemplateRoute createRoute(String uriPattern, Restlet target, int matchingMode) { - TemplateRoute result = new TemplateRoute(this, uriPattern, target) { - @Override - protected int beforeHandle(Request request, Response response) { - final int result = super.beforeHandle(request, response); - - // Set the request's root reference in order to help the - // retrieval of the relative reference. - request.setRootRef(request.getResourceRef().getBaseRef()); - - return result; - } - }; - - result.getTemplate().setMatchingMode(matchingMode); - result.setMatchingQuery(getDefaultMatchingQuery()); - return result; - } - - @Override - public TemplateRoute attach(Restlet target) { - if (target.getContext() == null) { - target.setContext(getContext().createChildContext()); - } - - return super.attach(target); - } - - @Override - public TemplateRoute attach(String uriPattern, Restlet target) { - if (target.getContext() == null) { - target.setContext(getContext().createChildContext()); - } - - return super.attach(uriPattern, target); - } - - @Override - public TemplateRoute attachDefault(Restlet defaultTarget) { - if (defaultTarget.getContext() == null) { - defaultTarget.setContext(getContext().createChildContext()); - } - - return super.attachDefault(defaultTarget); - } - - @Override - public Finder createFinder(Class targetClass) { - Finder result = super.createFinder(targetClass); - result.setContext(getContext().createChildContext()); - return result; - } - + /** + * Constructor. + * + * @param context The current context. + */ + public InternalRouter(Context context) { + super(context); + // Override Router's default modes + setDefaultMatchingMode(Template.MODE_STARTS_WITH); + setRoutingMode(Router.MODE_BEST_MATCH); + } + + @Override + protected TemplateRoute createRoute(String uriPattern, Restlet target, int matchingMode) { + TemplateRoute result = + new TemplateRoute(this, uriPattern, target) { + @Override + protected int beforeHandle(Request request, Response response) { + final int result = super.beforeHandle(request, response); + + // Set the request's root reference to help the + // retrieval of the relative reference. + request.setRootRef(request.getResourceRef().getBaseRef()); + + return result; + } + }; + + result.getTemplate().setMatchingMode(matchingMode); + result.setMatchingQuery(getDefaultMatchingQuery()); + return result; + } + + @Override + public TemplateRoute attach(Restlet target) { + if (target.getContext() == null) { + target.setContext(getContext().createChildContext()); + } + + return super.attach(target); + } + + @Override + public TemplateRoute attach(String uriPattern, Restlet target) { + if (target.getContext() == null) { + target.setContext(getContext().createChildContext()); + } + + return super.attach(uriPattern, target); + } + + @Override + public TemplateRoute attachDefault(Restlet defaultTarget) { + if (defaultTarget.getContext() == null) { + defaultTarget.setContext(getContext().createChildContext()); + } + + return super.attachDefault(defaultTarget); + } + + @Override + public Finder createFinder(Class targetClass) { + Finder result = super.createFinder(targetClass); + result.setContext(getContext().createChildContext()); + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java index 5d9a1c684b..b5ba9b7faa 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.component; +import java.util.logging.Level; import org.restlet.Component; import org.restlet.Request; import org.restlet.Response; @@ -17,92 +17,99 @@ import org.restlet.routing.Router; import org.restlet.routing.VirtualHost; -import java.util.logging.Level; - /** - * Router that collects calls from all server connectors and dispatches them to - * the appropriate host routers. The host routers then dispatch them to the user - * applications. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Router that collects calls from all server connectors and dispatches them to the appropriate host + * routers. The host routers then dispatch them to the user applications. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class ServerRouter extends Router { - /** The parent component. */ - private volatile Component component; + /** The parent component. */ + private volatile Component component; - /** - * Constructor. - * - * @param component The parent component. - */ - public ServerRouter(Component component) { - super((component == null) ? null : component.getContext().createChildContext()); - this.component = component; - setRoutingMode(MODE_FIRST_MATCH); - } + /** + * Constructor. + * + * @param component The parent component. + */ + public ServerRouter(Component component) { + super((component == null) ? null : component.getContext().createChildContext()); + this.component = component; + setRoutingMode(MODE_FIRST_MATCH); + } - /** - * Returns the parent component. - * - * @return The parent component. - */ - private Component getComponent() { - return this.component; - } + /** + * Returns the parent component. + * + * @return The parent component. + */ + private Component getComponent() { + return this.component; + } - @Override - protected void logRoute(org.restlet.routing.Route route) { - if (getLogger().isLoggable(Level.FINE)) { - if (route instanceof HostRoute) { - VirtualHost vhost = ((HostRoute) route).getVirtualHost(); + @Override + protected void logRoute(org.restlet.routing.Route route) { + if (getLogger().isLoggable(Level.FINE)) { + if (route instanceof HostRoute) { + VirtualHost vhost = ((HostRoute) route).getVirtualHost(); - if (getComponent().getDefaultHost() == vhost) { - getLogger().fine("Default virtual host selected"); - } else { - getLogger().fine("Virtual host selected: \"" + vhost.getHostScheme() + "\", \"" - + vhost.getHostDomain() + "\", \"" + vhost.getHostPort() + "\""); - } - } else { - super.logRoute(route); - } - } - } + if (getComponent().getDefaultHost() == vhost) { + getLogger().fine("Default virtual host selected"); + } else { + getLogger() + .fine( + "Virtual host selected: \"" + + vhost.getHostScheme() + + "\", \"" + + vhost.getHostDomain() + + "\", \"" + + vhost.getHostPort() + + "\""); + } + } else { + super.logRoute(route); + } + } + } - /** Starts the Restlet. */ - @Override - public synchronized void start() throws Exception { - // Attach all virtual hosts - for (VirtualHost host : getComponent().getHosts()) { - getRoutes().add(new HostRoute(this, host)); - } + /** Starts the Restlet. */ + @Override + public synchronized void start() throws Exception { + // Attach all virtual hosts + for (VirtualHost host : getComponent().getHosts()) { + getRoutes().add(new HostRoute(this, host)); + } - // Also attach the default host if it exists - if (getComponent().getDefaultHost() != null) { - getRoutes().add(new HostRoute(this, getComponent().getDefaultHost())); - } + // Also attach the default host if it exists + if (getComponent().getDefaultHost() != null) { + getRoutes().add(new HostRoute(this, getComponent().getDefaultHost())); + } - // If no host matches, display and error page with a precise message - final Restlet noHostMatched = new Restlet(getComponent().getContext().createChildContext()) { - @Override - public void handle(Request request, Response response) { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND, "No virtual host could handle the request"); - } - }; + // If no host matches, display and error page with a precise message + final Restlet noHostMatched = + new Restlet(getComponent().getContext().createChildContext()) { + @Override + public void handle(Request request, Response response) { + response.setStatus( + Status.CLIENT_ERROR_NOT_FOUND, + "No virtual host could handle the request"); + } + }; - setDefaultRoute(new org.restlet.routing.TemplateRoute(this, "", noHostMatched)); + setDefaultRoute(new org.restlet.routing.TemplateRoute(this, "", noHostMatched)); - // Start the router - super.start(); - } + // Start the router + super.start(); + } - @Override - public synchronized void stop() throws Exception { - getRoutes().clear(); - super.stop(); - } + @Override + public synchronized void stop() throws Exception { + getRoutes().clear(); + super.stop(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java index 05562a2374..891befa915 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java @@ -1,20 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import org.restlet.Client; /** - * Client connector helper. Base client helper based on NIO non-blocking - * sockets. Here is the list of parameters that are supported. They should be - * set in the Client's context before it is started: + * Client connector helper. Base client helper based on NIO non-blocking sockets. Here is the list + * of parameters that are supported. They should be set in the Client's context before it is + * started: + * * * * @@ -24,18 +24,17 @@ * * *
list of supported parameters
Description
- * + * * @author Jerome Louvel */ public class ClientHelper extends ConnectorHelper { - /** - * Constructor. - * - * @param client The client to help. - */ - public ClientHelper(Client client) { - super(client); - } - + /** + * Constructor. + * + * @param client The client to help. + */ + public ClientHelper(Client client) { + super(client); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java index c640c61bc8..e8912a701f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java @@ -1,22 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Connector; import org.restlet.Context; import org.restlet.data.Protocol; import org.restlet.engine.RestletHelper; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - /** * Base connector helper. * @@ -24,64 +22,58 @@ */ public abstract class ConnectorHelper extends RestletHelper { - /** - * Returns the connector service associated to a request. - * - * @return The connector service associated to a request. - */ - public static org.restlet.service.ConnectorService getConnectorService() { - org.restlet.service.ConnectorService result = null; - org.restlet.Application application = org.restlet.Application.getCurrent(); - - if (application != null) { - result = application.getConnectorService(); - } else { - result = new org.restlet.service.ConnectorService(); - } + /** + * Returns the connector service associated with a request. + * + * @return The connector service associated with a request. + */ + public static org.restlet.service.ConnectorService getConnectorService() { + org.restlet.service.ConnectorService result = null; + org.restlet.Application application = org.restlet.Application.getCurrent(); - return result; - } + if (application != null) { + result = application.getConnectorService(); + } else { + result = new org.restlet.service.ConnectorService(); + } - /** The protocols simultaneously supported. */ - private final List protocols; + return result; + } - /** - * Constructor. - */ - public ConnectorHelper(T connector) { - super(connector); - this.protocols = new CopyOnWriteArrayList(); - } + /** The protocols simultaneously supported. */ + private final List protocols; - /** - * Returns the helped Restlet context. - * - * @return The helped Restlet context. - */ - @Override - public Context getContext() { - return super.getContext(); - } + /** Constructor. */ + public ConnectorHelper(T connector) { + super(connector); + this.protocols = new CopyOnWriteArrayList(); + } - /** - * Returns the protocols simultaneously supported. - * - * @return The protocols simultaneously supported. - */ - public List getProtocols() { - return this.protocols; - } + /** + * Returns the helped Restlet context. + * + * @return The helped Restlet context. + */ + @Override + public Context getContext() { + return super.getContext(); + } - @Override - public void start() throws Exception { - } + /** + * Returns the protocols simultaneously supported. + * + * @return The protocols simultaneously supported. + */ + public List getProtocols() { + return this.protocols; + } - @Override - public void stop() throws Exception { - } + @Override + public void start() throws Exception {} - @Override - public void update() throws Exception { - } + @Override + public void stop() throws Exception {} + @Override + public void update() throws Exception {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java index b84cda48a9..69d8496ebc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import java.io.IOException; @@ -17,7 +16,6 @@ import java.util.Optional; import java.util.concurrent.Executor; import java.util.logging.Level; - import org.eclipse.jetty.client.AuthenticationStore; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClientTransport; @@ -53,8 +51,9 @@ import org.restlet.engine.util.ReferenceUtils; /** - * HTTP client connector using the Jetty project. Here is the list of parameters that are supported. They should be set - * in the Client's context before it is started: + * HTTP client connector using the Jetty project. Here is the list of parameters that are supported. + * They should be set in the Client's context before it is started: + * * * * @@ -207,6 +206,7 @@ * you configure a secured enough directory. * *
list of supported parameters
+ * * For the default SSL parameters see the Javadocs of the {@link DefaultSslContextFactory} class. * * @author Jerome Louvel @@ -214,27 +214,21 @@ */ public class HttpClientHelper extends org.restlet.engine.adapter.HttpClientHelper { - /** - * The wrapped Jetty HTTP client. - */ + /** The wrapped Jetty HTTP client. */ private volatile HttpClient httpClient; - /** - * The wrapped Jetty authentication store. - */ + /** The wrapped Jetty authentication store. */ private volatile AuthenticationStore authenticationStore; - /** - * The wrapped Jetty cookie store. - */ + /** The wrapped Jetty cookie store. */ private volatile HttpCookieStore cookieStore; /** The wrapper Executor. */ private volatile Executor executor; /** - * Constructor. Properties can still be set before the wrapped Jetty HTTP client is effectively created and - * configured via the {@link #createHttpClient()} method. + * Constructor. Properties can still be set before the wrapped Jetty HTTP client is effectively + * created and configured via the {@link #createHttpClient()} method. * * @param client The client connector to help. */ @@ -243,7 +237,8 @@ public HttpClientHelper(Client client) { getProtocols().add(Protocol.HTTP); getProtocols().add(Protocol.HTTPS); this.authenticationStore = null; - this.cookieStore = isCookieSupported() ? new HttpCookieStore.Default() : new HttpCookieStore.Empty(); + this.cookieStore = + isCookieSupported() ? new HttpCookieStore.Default() : new HttpCookieStore.Empty(); this.executor = null; } @@ -257,8 +252,11 @@ public ClientCall create(Request request) { ClientCall result = null; try { - result = new JettyClientCall(this, request.getMethod().toString(), - ReferenceUtils.update(request.getResourceRef(), request).toString()); + result = + new JettyClientCall( + this, + request.getMethod().toString(), + ReferenceUtils.update(request.getResourceRef(), request).toString()); } catch (IOException e) { getLogger().log(Level.WARNING, "Unable to create the Jetty HTTP/HTTPS client call", e); } @@ -275,18 +273,21 @@ protected HttpClient createHttpClient() { SslContextFactory.Client sslContextFactory = null; try { - sslContextFactory = new RestletSslContextFactoryClient(SslUtils.getSslContextFactory(this)); + sslContextFactory = + new RestletSslContextFactoryClient(SslUtils.getSslContextFactory(this)); } catch (Exception e) { getLogger().log(Level.WARNING, "Unable to create the Jetty SSL context factory", e); } - HttpTransportProtocol httpTransportProtocol = HttpTransportProtocol.fromName(getHttpClientTransportMode()); - final HttpClientTransport httpTransport = switch (httpTransportProtocol) { - case HTTP1_1 -> getHttpTransportForHttp1_1(); - case HTTP2 -> getHttpClientTransportForHttp2(); - case HTTP3 -> getHttpClientTransportForHttp3(sslContextFactory); - case DYNAMIC -> getHttpClientTransportForDynamicMode(sslContextFactory); - }; + HttpTransportProtocol httpTransportProtocol = + HttpTransportProtocol.fromName(getHttpClientTransportMode()); + final HttpClientTransport httpTransport = + switch (httpTransportProtocol) { + case HTTP1_1 -> getHttpTransportForHttp1_1(); + case HTTP2 -> getHttpClientTransportForHttp2(); + case HTTP3 -> getHttpClientTransportForHttp3(sslContextFactory); + case DYNAMIC -> getHttpClientTransportForDynamicMode(sslContextFactory); + }; final HttpClient httpClient = new HttpClient(httpTransport); httpClient.setAddressResolutionTimeout(getAddressResolutionTimeout()); @@ -301,16 +302,21 @@ protected HttpClient createHttpClient() { httpClient.setFollowRedirects(isFollowRedirects()); final String httpComplianceMode = getHttpComplianceMode(); - final HttpCompliance httpCompliance = switch (httpComplianceMode) { - case "RFC7230" -> HttpCompliance.RFC7230; - case "RFC7230_LEGACY" -> HttpCompliance.RFC7230_LEGACY; - case "RFC2616" -> HttpCompliance.RFC2616; - case "RFC2616_LEGACY" -> HttpCompliance.RFC2616_LEGACY; - default -> { - getLogger().log(Level.WARNING, "Unknown HTTP compliance mode: {0}, default to RFC7230", httpComplianceMode); - yield HttpCompliance.RFC7230; - } - }; + final HttpCompliance httpCompliance = + switch (httpComplianceMode) { + case "RFC7230" -> HttpCompliance.RFC7230; + case "RFC7230_LEGACY" -> HttpCompliance.RFC7230_LEGACY; + case "RFC2616" -> HttpCompliance.RFC2616; + case "RFC2616_LEGACY" -> HttpCompliance.RFC2616_LEGACY; + default -> { + getLogger() + .log( + Level.WARNING, + "Unknown HTTP compliance mode: {0}, default to RFC7230", + httpComplianceMode); + yield HttpCompliance.RFC7230; + } + }; httpClient.setHttpCompliance(httpCompliance); httpClient.setHttpCookieStore(getCookieStore()); @@ -352,37 +358,43 @@ private HttpClientTransport getHttpClientTransportForHttp2() { return http2Transport; } - private HttpClientTransport getHttpClientTransportForHttp3(SslContextFactory.Client sslContextFactory) { + private HttpClientTransport getHttpClientTransportForHttp3( + SslContextFactory.Client sslContextFactory) { Path pemWorkDirectory = getHttp3PemWorkDirectoryPath(); - ClientQuicConfiguration quicConfiguration = new ClientQuicConfiguration(sslContextFactory, pemWorkDirectory); + ClientQuicConfiguration quicConfiguration = + new ClientQuicConfiguration(sslContextFactory, pemWorkDirectory); HTTP3Client http3Client = new HTTP3Client(quicConfiguration); http3Client.getQuicConfiguration().setSessionRecvWindow(64 * 1024 * 1024); return new HttpClientTransportOverHTTP3(http3Client); } - private HttpClientTransport getHttpClientTransportForDynamicMode(SslContextFactory.Client sslContextFactory) { + private HttpClientTransport getHttpClientTransportForDynamicMode( + SslContextFactory.Client sslContextFactory) { ClientConnectionFactory.Info http1 = HttpClientConnectionFactory.HTTP11; HTTP2Client http2Client = new HTTP2Client(); - ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client); + ClientConnectionFactoryOverHTTP2.HTTP2 http2 = + new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client); - ClientQuicConfiguration quicConfiguration = new ClientQuicConfiguration(sslContextFactory, - getHttp3PemWorkDirectoryPath()); + ClientQuicConfiguration quicConfiguration = + new ClientQuicConfiguration(sslContextFactory, getHttp3PemWorkDirectoryPath()); HTTP3Client http3Client = new HTTP3Client(quicConfiguration); - ClientConnectionFactoryOverHTTP3.HTTP3 http3 = new ClientConnectionFactoryOverHTTP3.HTTP3(http3Client); + ClientConnectionFactoryOverHTTP3.HTTP3 http3 = + new ClientConnectionFactoryOverHTTP3.HTTP3(http3Client); return new HttpClientTransportDynamic(new ClientConnector(), http1, http2, http3); } /** - * The timeout in milliseconds for the DNS resolution of host addresses. Defaults to 15000. + * The timeout in milliseconds for the DNS resolution of host addresses. Defaults to 15_000. * * @return The address resolution timeout. */ public long getAddressResolutionTimeout() { - return Long.parseLong(getHelpedParameters().getFirstValue("addressResolutionTimeout", "15000")); + return Long.parseLong( + getHelpedParameters().getFirstValue("addressResolutionTimeout", "15000")); } /** @@ -420,7 +432,8 @@ public SocketAddress getBindAddress() { } /** - * The max time in milliseconds a connection can take to connect to destinations. Defaults to 15000. + * The max time in milliseconds a connection can take to connect to destinations. Defaults to + * 15_000. * * @return The connect timeout. */ @@ -483,8 +496,8 @@ public HttpClient getHttpClient() { } /** - * Returns the HTTP compliance mode among the following options: "RFC7230", "RFC2616", "LEGACY", "RFC7230_LEGACY". - * See {@link HttpCompliance}. Default to "RFC7230". + * Returns the HTTP compliance mode among the following options: "RFC7230", "RFC2616", "LEGACY", + * "RFC7230_LEGACY". See {@link HttpCompliance}. Default to "RFC7230". * * @return The HTTP compliance mode. */ @@ -493,18 +506,19 @@ public String getHttpComplianceMode() { } /** - * Returns the HTTP client transport mode among the following options: "HTTP1_1", "HTTP2", "HTTP3", "DYNAMIC. See - * {@link HttpClientTransport}. Default to "HTTP1_1". + * Returns the HTTP client transport mode among the following options: "HTTP1_1", "HTTP2", + * "HTTP3", "DYNAMIC. See {@link HttpClientTransport}. Default to "HTTP1_1". * * @return The HTTP client transport mode. */ public String getHttpClientTransportMode() { - return getHelpedParameters().getFirstValue("httpClientTransportMode", HttpTransportProtocol.HTTP1_1.name()); + return getHelpedParameters() + .getFirstValue("httpClientTransportMode", HttpTransportProtocol.HTTP1_1.name()); } /** * Directory where are extracted the supported certificates. - * + * * @return Directory where are extracted the supported certificates. */ public String getHttp3PemWorkDir() { @@ -516,8 +530,8 @@ private Path getHttp3PemWorkDirectoryPath() { } /** - * The max time in milliseconds a connection can be idle (that is, without traffic of bytes in either direction). - * Defaults to 30000. + * The max time in milliseconds a connection can be idle (that is, without traffic of bytes in + * either direction). Defaults to 30_000. * * @return The idle timeout. */ @@ -527,16 +541,17 @@ public long getIdleTimeout() { /** * Sets the max number of connections to open to each destination. Defaults to 64. - *

- * RFC 2616 suggests that 2 connections should be opened per each destination, but browsers commonly open 6. If this - * client is used for load testing, it is common to have only one destination (the server to load test), and it is - * recommended to set this value to a high value (at least as much as the threads present in the - * {@link #getExecutor() executor}). + * + *

RFC 2616 suggests that 2 connections should be opened per each destination, but browsers + * commonly open 6. If this client is used for load testing, it is common to have only one + * destination (the server to load test), and it is recommended to set this value to a high + * value (at least as much as the threads present in the {@link #getExecutor() executor}). * * @return The maximum connections per destination. */ public int getMaxConnectionsPerDestination() { - return Integer.parseInt(getHelpedParameters().getFirstValue("maxConnectionsPerDestination", "64")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("maxConnectionsPerDestination", "64")); } /** @@ -550,17 +565,19 @@ public int getMaxRedirects() { /** * Sets the max number of requests that may be queued to a destination. Defaults to 1024. - *

- * If this client performs a high rate of requests to a destination, and all the connections managed by that - * destination are busy with other requests, then new requests will be queued up in the destination. This parameter - * controls how many requests can be queued before starting to reject them. If this client is used for load testing, - * it is common to have this parameter set to a high value, although this may impact latency (requests sit in the - * queue for a long time before being sent). + * + *

If this client performs a high rate of requests to a destination, and all the connections + * managed by that destination are busy with other requests, then new requests will be queued up + * in the destination. This parameter controls how many requests can be queued before starting + * to reject them. If this client is used for load testing, it is common to have this parameter + * set to a high value, although this may impact latency (requests sit in the queue for a long + * time before being sent). * * @return The maximum requests queues per destination. */ public int getMaxRequestsQueuedPerDestination() { - return Integer.parseInt(getHelpedParameters().getFirstValue("maxRequestsQueuedPerDestination", "1024")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("maxRequestsQueuedPerDestination", "1024")); } /** @@ -569,7 +586,8 @@ public int getMaxRequestsQueuedPerDestination() { * @return the max size in bytes of the response headers. */ public int getMaxResponseHeadersSize() { - return Integer.parseInt(getHelpedParameters().getFirstValue("maxResponseHeadersSize", "-1")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("maxResponseHeadersSize", "-1")); } /** @@ -578,7 +596,8 @@ public int getMaxResponseHeadersSize() { * @return the host name of the HTTP proxy, if specified. */ public String getProxyHost() { - return getHelpedParameters().getFirstValue("proxyHost", System.getProperty("http.proxyHost")); + return getHelpedParameters() + .getFirstValue("proxyHost", System.getProperty("http.proxyHost")); } /** @@ -588,7 +607,8 @@ public String getProxyHost() { */ public int getProxyPort() { return Integer.parseInt( - getHelpedParameters().getFirstValue("proxyPort", System.getProperty("http.proxyPort", "3128"))); + getHelpedParameters() + .getFirstValue("proxyPort", System.getProperty("http.proxyPort", "3128"))); } /** @@ -601,7 +621,7 @@ public int getRequestBufferSize() { } /** - * The size in bytes of the buffer used to read responses. Defaults to 16384. + * The size in bytes of the buffer used to read responses. Defaults to 16_384. * * @return The response buffer size. */ @@ -610,7 +630,8 @@ public int getResponseBufferSize() { } /** - * The scheduler. Defaults to null. When null, creates a new instance of {@link ScheduledExecutorScheduler}. + * The scheduler. Defaults to null. When null, creates a new instance of {@link + * ScheduledExecutorScheduler}. * * @return The scheduler. */ @@ -628,21 +649,24 @@ public String getUserAgentField() { } /** - * Indicates whether the connect operation is blocking. See {@link HttpClient#isConnectBlocking()}. + * Indicates whether the connect operation is blocking. See {@link + * HttpClient#isConnectBlocking()}. * * @return True if the connect operation is blocking. */ public boolean isConnectBlocking() { - return Boolean.parseBoolean(getHelpedParameters().getFirstValue("connectBlocking", "false")); + return Boolean.parseBoolean( + getHelpedParameters().getFirstValue("connectBlocking", "false")); } /** - * Whether to support cookies, storing and automatically sending them back. Defaults to false. + * Whether to support cookies, storing, and automatically sending them back. Defaults to false. * * @return Whether to support cookies. */ public boolean isCookieSupported() { - return Boolean.parseBoolean(getHelpedParameters().getFirstValue("cookieSupported", "false")); + return Boolean.parseBoolean( + getHelpedParameters().getFirstValue("cookieSupported", "false")); } /** @@ -656,25 +680,29 @@ public boolean isFollowRedirects() { /** * Whether request events must be strictly ordered. Defaults to false. - *

- * Client listeners may send a second request. If the second request is for the same destination, there is an - * inherent race condition for the use of the connection: the first request may still be associated with the - * connection, so the second request cannot use that connection and is forced to open another one. - *

- * From the point of view of connection usage, the connection is reusable just before the "complete" event, so it - * would be possible to reuse that connection from complete listeners; but in this case the second request's events - * will fire before the "complete" events of the first request. - *

- * This setting enforces strict event ordering so that a "begin" event of a second request can never fire before the - * "complete" event of a first request, but at the expense of an increased usage of connections. - *

- * When not enforced, a "begin" event of a second request may happen before the "complete" event of a first request - * and allow for better usage of connections. + * + *

Client listeners may send a second request. If the second request is for the same + * destination, there is an inherent race condition for the use of the connection: the first + * request may still be associated with the connection, so the second request cannot use that + * connection and is forced to open another one. + * + *

From the point of view of connection usage, the connection is reusable just before the + * "complete" event, so it would be possible to reuse that connection from complete listeners; + * but in this case the second request's events will fire before the "complete" events of the + * first request. + * + *

This setting enforces strict event ordering so that a "begin" event of a second request + * can never fire before the "complete" event of a first request, but at the expense of an + * increased usage of connections. + * + *

When not enforced, a "begin" event of a second request may happen before the "complete" + * event of a first request and allow for better usage of connections. * * @return Whether request events must be strictly ordered. */ public boolean isStrictEventOrdering() { - return Boolean.parseBoolean(getHelpedParameters().getFirstValue("strictEventOrdering", "false")); + return Boolean.parseBoolean( + getHelpedParameters().getFirstValue("strictEventOrdering", "false")); } @Override @@ -703,20 +731,24 @@ public void stop() throws Exception { super.stop(); } - /** - * Supported HTTP transport protocols. - */ + /** Supported HTTP transport protocols. */ private enum HttpTransportProtocol { - HTTP1_1, HTTP2, HTTP3, DYNAMIC; + HTTP1_1, + HTTP2, + HTTP3, + DYNAMIC; static HttpTransportProtocol fromName(final String name) { try { return HttpTransportProtocol.valueOf(name); } catch (final IllegalArgumentException iae) { - String supportedHttpTransportProtocols = Arrays.toString(HttpTransportProtocol.values()); + String supportedHttpTransportProtocols = + Arrays.toString(HttpTransportProtocol.values()); - final String errorMessage = String.format("'%s' is not one of the supported values: %s", name, - supportedHttpTransportProtocols); + final String errorMessage = + String.format( + "'%s' is not one of the supported values: %s", + name, supportedHttpTransportProtocols); throw new IllegalArgumentException(errorMessage); } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java index 1d83cdd7ab..149eea4197 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java @@ -1,35 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import org.restlet.data.Method; /** * Protocol helper for the HTTP protocol. - * + * * @author Thierry Boileau */ public class HttpProtocolHelper extends ProtocolHelper { - @Override - public void registerMethods() { - Method.register(Method.ALL); - Method.register(Method.CONNECT); - Method.register(Method.DELETE); - Method.register(Method.GET); - Method.register(Method.HEAD); - Method.register(Method.OPTIONS); - Method.register(Method.PATCH); - Method.register(Method.POST); - Method.register(Method.PUT); - Method.register(Method.TRACE); - } - + @Override + public void registerMethods() { + Method.register(Method.ALL); + Method.register(Method.CONNECT); + Method.register(Method.DELETE); + Method.register(Method.GET); + Method.register(Method.HEAD); + Method.register(Method.OPTIONS); + Method.register(Method.PATCH); + Method.register(Method.POST); + Method.register(Method.PUT); + Method.register(Method.TRACE); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java index ee2a90e82d..267dedd941 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; +import java.util.Arrays; +import java.util.List; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; @@ -17,9 +18,6 @@ import org.restlet.Server; import org.restlet.data.Protocol; -import java.util.Arrays; -import java.util.List; - /** * Jetty HTTP server connector. * @@ -56,14 +54,17 @@ public HttpServerHelper(Server server) { */ @Override protected ConnectionFactory[] createConnectionFactories(final HttpConfiguration configuration) { - HttpTransportProtocol httpTransportProtocol = HttpTransportProtocol.fromName(getHttpTransportProtocol()); + HttpTransportProtocol httpTransportProtocol = + HttpTransportProtocol.fromName(getHttpTransportProtocol()); return switch (httpTransportProtocol) { - case HTTP1_1 -> new ConnectionFactory[] { - new HttpConnectionFactory(configuration) }; - case HTTP2 -> new ConnectionFactory[] { - new HttpConnectionFactory(configuration), // still necessary to support protocol upgrade - new HTTP2CServerConnectionFactory(configuration) }; + case HTTP1_1 -> new ConnectionFactory[] {new HttpConnectionFactory(configuration)}; + case HTTP2 -> + new ConnectionFactory[] { + new HttpConnectionFactory( + configuration), // still necessary to support protocol upgrade + new HTTP2CServerConnectionFactory(configuration) + }; }; } @@ -78,27 +79,29 @@ protected List createConnectors(org.eclipse.jetty.server.Server serve * @return Supported HTTP transport protocol. */ public String getHttpTransportProtocol() { - return getHelpedParameters().getFirstValue("http.transport.protocol", HttpTransportProtocol.HTTP1_1.name()); + return getHelpedParameters() + .getFirstValue("http.transport.protocol", HttpTransportProtocol.HTTP1_1.name()); } - /** - * Supported HTTP transport protocols. - */ + /** Supported HTTP transport protocols. */ private enum HttpTransportProtocol { - HTTP1_1, HTTP2; + HTTP1_1, + HTTP2; static HttpTransportProtocol fromName(final String name) { try { return HttpTransportProtocol.valueOf(name); } catch (final IllegalArgumentException iae) { - String supportedHttpTransportProtocols = Arrays.toString(HttpTransportProtocol.values()); + String supportedHttpTransportProtocols = + Arrays.toString(HttpTransportProtocol.values()); - final String errorMessage = String.format("'%s' is not one of the supported values: %s", name, - supportedHttpTransportProtocols); + final String errorMessage = + String.format( + "'%s' is not one of the supported values: %s", + name, supportedHttpTransportProtocols); throw new IllegalArgumentException(errorMessage); } } } - } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java index feb2bb2403..0e33fb8da2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import java.nio.file.Path; @@ -15,7 +14,6 @@ import java.util.List; import java.util.Optional; import java.util.logging.Level; - import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; import org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory; @@ -34,8 +32,9 @@ import org.restlet.engine.ssl.DefaultSslContextFactory; /** - * Jetty HTTPS server connector. Here is the list of additional parameters that are supported. They should be set in the - * Server's context before it is started: + * Jetty HTTPS server connector. Here is the list of additional parameters that are supported. They + * should be set in the Server's context before it is started: + * * * * @@ -65,9 +64,11 @@ * you configure a secured enough directory. * *
list of supported parameters
+ * * For the default SSL parameters see the Javadocs of the {@link DefaultSslContextFactory} class. * - * @see Configure SSL for Jetty + * @see Configure + * SSL for Jetty * @author Jerome Louvel * @author Tal Liron */ @@ -87,15 +88,16 @@ public HttpsServerHelper(Server server) { protected List createConnectors(org.eclipse.jetty.server.Server server) { final List result = new ArrayList<>(); - final List httpTransportProtocols = getHttpTransportProtocols().stream() - .map(HttpTransportProtocol::fromName).toList(); + final List httpTransportProtocols = + getHttpTransportProtocols().stream().map(HttpTransportProtocol::fromName).toList(); if (httpTransportProtocols.stream().anyMatch(HttpTransportProtocol::isTcpProtocol)) { HttpConfiguration configuration = createHttpConfiguration(); ServerConnector connector = createServerConnector(server, configuration); result.add(connector); } else if (httpTransportProtocols.contains(HttpTransportProtocol.HTTP3)) { - ServerQuicConfiguration configuration = createQuicConfiguration(getQuicServerSslContextFactory()); + ServerQuicConfiguration configuration = + createQuicConfiguration(getQuicServerSslContextFactory()); QuicServerConnector connector = createQuicServerConnector(server, configuration); result.add(connector); } @@ -107,33 +109,45 @@ protected List createConnectors(org.eclipse.jetty.server.Server serve protected ConnectionFactory[] createConnectionFactories(final HttpConfiguration configuration) { final List connectionFactories = new ArrayList<>(); - final List tcpBasedTransportProtocols = getHttpTransportProtocols().stream() - .map(HttpTransportProtocol::fromName).filter(HttpTransportProtocol::isTcpProtocol).toList(); + final List tcpBasedTransportProtocols = + getHttpTransportProtocols().stream() + .map(HttpTransportProtocol::fromName) + .filter(HttpTransportProtocol::isTcpProtocol) + .toList(); for (HttpTransportProtocol tcpBasedTransportProtocol : tcpBasedTransportProtocols) { - final List protocolConnectionFactories = switch (tcpBasedTransportProtocol) { - case HTTP1_1 -> List.of(new HttpConnectionFactory(configuration)); - case HTTP2 -> List.of(new ALPNServerConnectionFactory(), new HTTP2ServerConnectionFactory(configuration)); - default -> { - String supportedHttpTransportProtocols = tcpBasedTransportProtocols.toString(); - final String errorMessage = String.format("'%s' is not one of the supported values: %s", - tcpBasedTransportProtocol, supportedHttpTransportProtocols); - throw new IllegalArgumentException(errorMessage); - } - }; + final List protocolConnectionFactories = + switch (tcpBasedTransportProtocol) { + case HTTP1_1 -> List.of(new HttpConnectionFactory(configuration)); + case HTTP2 -> + List.of( + new ALPNServerConnectionFactory(), + new HTTP2ServerConnectionFactory(configuration)); + default -> { + String supportedHttpTransportProtocols = + tcpBasedTransportProtocols.toString(); + final String errorMessage = + String.format( + "'%s' is not one of the supported values: %s", + tcpBasedTransportProtocol, + supportedHttpTransportProtocols); + throw new IllegalArgumentException(errorMessage); + } + }; connectionFactories.addAll(protocolConnectionFactories); } SslContextFactory.Server sslContextFactory = getServerSslContextFactory(); - return AbstractConnectionFactory.getFactories(sslContextFactory, - connectionFactories.toArray(new ConnectionFactory[0])); + return AbstractConnectionFactory.getFactories( + sslContextFactory, connectionFactories.toArray(new ConnectionFactory[0])); } - private QuicServerConnector createQuicServerConnector(org.eclipse.jetty.server.Server server, - ServerQuicConfiguration configuration) { - QuicServerConnector connector = new QuicServerConnector(server, configuration, - new HTTP3ServerConnectionFactory(configuration)); + private QuicServerConnector createQuicServerConnector( + org.eclipse.jetty.server.Server server, ServerQuicConfiguration configuration) { + QuicServerConnector connector = + new QuicServerConnector( + server, configuration, new HTTP3ServerConnectionFactory(configuration)); final String address = getHelped().getAddress(); if (address != null) { connector.setHost(address); @@ -150,14 +164,19 @@ private QuicServerConnector createQuicServerConnector(org.eclipse.jetty.server.S * @return Supported HTTP transport protocols. */ public List getHttpTransportProtocols() { - String httpTransportProtocolsAsString = getHelpedParameters().getFirstValue("http.transport.protocols", - HttpTransportProtocol.HTTP1_1.name()); - return Arrays.stream(httpTransportProtocolsAsString.split(",")).map(String::trim).distinct().toList(); + String httpTransportProtocolsAsString = + getHelpedParameters() + .getFirstValue( + "http.transport.protocols", HttpTransportProtocol.HTTP1_1.name()); + return Arrays.stream(httpTransportProtocolsAsString.split(",")) + .map(String::trim) + .distinct() + .toList(); } /** * Directory where are extracted the supported certificates. - * + * * @return Directory where are extracted the supported certificates. */ public String getHttp3PemWorkDir() { @@ -170,7 +189,8 @@ private Path getHttp3PemWorkDirectoryPath() { private SslContextFactory.Server getServerSslContextFactory() { try { - return new RestletSslContextFactoryServer(org.restlet.engine.ssl.SslUtils.getSslContextFactory(this)); + return new RestletSslContextFactoryServer( + org.restlet.engine.ssl.SslUtils.getSslContextFactory(this)); } catch (RuntimeException e) { getLogger().log(Level.WARNING, "Unable to create the Jetty SSL context factory", e); throw e; @@ -183,36 +203,63 @@ private SslContextFactory.Server getServerSslContextFactory() { private SslContextFactory.Server getQuicServerSslContextFactory() { SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePassword(getHelpedParameters().getFirstValue("keyStorePassword", true, - System.getProperty("javax.net.ssl.keyStorePassword", ""))); + sslContextFactory.setKeyStorePassword( + getHelpedParameters() + .getFirstValue( + "keyStorePassword", + true, + System.getProperty("javax.net.ssl.keyStorePassword", ""))); sslContextFactory.setKeyStorePath( - getHelpedParameters().getFirstValue("keyStorePath", true, System.getProperty("javax.net.ssl.keyStore"))); + getHelpedParameters() + .getFirstValue( + "keyStorePath", + true, + System.getProperty("javax.net.ssl.keyStore"))); sslContextFactory.setKeyStoreType( - getHelpedParameters().getFirstValue("keyStoreType", true, System.getProperty("javax.net.ssl.keyStoreType"))); + getHelpedParameters() + .getFirstValue( + "keyStoreType", + true, + System.getProperty("javax.net.ssl.keyStoreType"))); sslContextFactory.setProtocol(getHelpedParameters().getFirstValue("protocol", true, "TLS")); - sslContextFactory.setSecureRandomAlgorithm(getHelpedParameters().getFirstValue("secureRandomAlgorithm", true)); - sslContextFactory.setTrustStorePassword(getHelpedParameters().getFirstValue("trustStorePassword", true, - System.getProperty("javax.net.ssl.trustStorePassword"))); + sslContextFactory.setSecureRandomAlgorithm( + getHelpedParameters().getFirstValue("secureRandomAlgorithm", true)); + sslContextFactory.setTrustStorePassword( + getHelpedParameters() + .getFirstValue( + "trustStorePassword", + true, + System.getProperty("javax.net.ssl.trustStorePassword"))); sslContextFactory.setTrustStorePath( - getHelpedParameters().getFirstValue("trustStorePath", true, System.getProperty("javax.net.ssl.trustStore"))); - sslContextFactory.setTrustStoreType(getHelpedParameters().getFirstValue("trustStoreType", true, - System.getProperty("javax.net.ssl.trustStoreType"))); + getHelpedParameters() + .getFirstValue( + "trustStorePath", + true, + System.getProperty("javax.net.ssl.trustStore"))); + sslContextFactory.setTrustStoreType( + getHelpedParameters() + .getFirstValue( + "trustStoreType", + true, + System.getProperty("javax.net.ssl.trustStoreType"))); return sslContextFactory; } - private ServerQuicConfiguration createQuicConfiguration(SslContextFactory.Server sslContextFactory) { + private ServerQuicConfiguration createQuicConfiguration( + SslContextFactory.Server sslContextFactory) { Path pemWorkDirectory = getHttp3PemWorkDirectoryPath(); - ServerQuicConfiguration configuration = new ServerQuicConfiguration(sslContextFactory, pemWorkDirectory); + ServerQuicConfiguration configuration = + new ServerQuicConfiguration(sslContextFactory, pemWorkDirectory); configuration.setOutputBufferSize(getHttpOutputBufferSize()); return configuration; } - /** - * Supported HTTP transport protocols. - */ + /** Supported HTTP transport protocols. */ private enum HttpTransportProtocol { - HTTP1_1(true), HTTP2(true), HTTP3(false); + HTTP1_1(true), + HTTP2(true), + HTTP3(false); private final boolean tcpProtocol; @@ -220,10 +267,13 @@ static HttpTransportProtocol fromName(final String name) { try { return HttpTransportProtocol.valueOf(name); } catch (final IllegalArgumentException iae) { - String supportedHttpTransportProtocols = Arrays.toString(HttpTransportProtocol.values()); + String supportedHttpTransportProtocols = + Arrays.toString(HttpTransportProtocol.values()); - final String errorMessage = String.format("'%s' is not one of the supported values: %s", name, - supportedHttpTransportProtocols); + final String errorMessage = + String.format( + "'%s' is not one of the supported values: %s", + name, supportedHttpTransportProtocols); throw new IllegalArgumentException(errorMessage); } @@ -237,5 +287,4 @@ public boolean isTcpProtocol() { return tcpProtocol; } } - } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java index b26d54d7c6..caa574e330 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import java.net.Socket; @@ -14,7 +13,6 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; - import org.eclipse.jetty.io.ArrayByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.AbstractNetworkConnector; @@ -37,8 +35,9 @@ import org.restlet.engine.adapter.JettyServerCall; /** - * Abstract Jetty web server connector. Here is the list of parameters that are - * supported. They should be set in the Server's context before it is started: + * Abstract Jetty web server connector. Here is the list of parameters that are supported. They + * should be set in the Server's context before it is started: + * * * * @@ -135,7 +134,7 @@ * * * * * @@ -203,21 +202,19 @@ * * *
list of supported parameters
int8*1024HTTP response header size in bytes; larger headers will allow for more - * and/or larger cookies and longer HTTP headers (e.g. for redirection); + * and/or larger cookies and longer HTTP headers (e.g., for redirection); * however, larger headers will also consume more memory
Server shutdown timeout in milliseconds. Defaults to 30000.
- * - * @see Jetty 12 - * documentation + * + * @see Jetty 12 documentation * @author Jerome Louvel * @author Tal Liron */ -public abstract class JettyServerHelper - extends org.restlet.engine.adapter.HttpServerHelper { +public abstract class JettyServerHelper extends org.restlet.engine.adapter.HttpServerHelper { /** The wrapped Jetty server. */ private volatile org.eclipse.jetty.server.Server wrappedServer; /** * Constructor. - * + * * @param server The server to help. */ public JettyServerHelper(Server server) { @@ -226,7 +223,7 @@ public JettyServerHelper(Server server) { /** * Creates a Jetty HTTP configuration. - * + * * @return A Jetty HTTP configuration. */ protected HttpConfiguration createHttpConfiguration() { @@ -245,7 +242,7 @@ protected HttpConfiguration createHttpConfiguration() { /** * Creates new internal Jetty connection factories. - * + * * @param configuration The HTTP configuration. * @return New internal Jetty connection factories. */ @@ -258,30 +255,33 @@ protected abstract ConnectionFactory[] createConnectionFactories( * @param server The Jetty server. * @return The Jetty connectors. */ - protected abstract List createConnectors( - org.eclipse.jetty.server.Server server); + protected abstract List createConnectors(org.eclipse.jetty.server.Server server); /** * Creates a Jetty connector based on a classical TCP type of transport. - * + * * @param server The Jetty server. * @return A Jetty connector. */ protected ServerConnector createServerConnector( - final org.eclipse.jetty.server.Server server, - final HttpConfiguration configuration) { + final org.eclipse.jetty.server.Server server, final HttpConfiguration configuration) { final int acceptors = getConnectorAcceptors(); final int selectors = getConnectorSelectors(); final Executor executor = getConnectorExecutor(); final Scheduler scheduler = getConnectorScheduler(); final ByteBufferPool byteBufferPool = getConnectorByteBufferPool(); - final ConnectionFactory[] connectionFactories = createConnectionFactories( - configuration); + final ConnectionFactory[] connectionFactories = createConnectionFactories(configuration); - final ServerConnector connector = new ServerConnector(server, executor, - scheduler, byteBufferPool, acceptors, selectors, - connectionFactories); + final ServerConnector connector = + new ServerConnector( + server, + executor, + scheduler, + byteBufferPool, + acceptors, + selectors, + connectionFactories); final String address = getHelped().getAddress(); if (address != null) { @@ -297,24 +297,21 @@ protected ServerConnector createServerConnector( /** * Creates a Jetty low-resource monitor. - * + * * @param server A Jetty server. * @return A Jetty low-resource monitor or null. */ - private LowResourceMonitor createLowResourceMonitor( - org.eclipse.jetty.server.Server server) { + private LowResourceMonitor createLowResourceMonitor(org.eclipse.jetty.server.Server server) { final LowResourceMonitor result; final int period = getLowResourceMonitorPeriod(); if (period > 0) { result = new LowResourceMonitor(server); - result.setMonitoredConnectors( - Arrays.asList(server.getConnectors())); + result.setMonitoredConnectors(Arrays.asList(server.getConnectors())); result.setPeriod(period); result.setMonitorThreads(getLowResourceMonitorThreads()); result.setMaxMemory(getLowResourceMonitorMaxMemory()); - result.setLowResourcesIdleTimeout( - getLowResourceMonitorIdleTimeout()); + result.setLowResourcesIdleTimeout(getLowResourceMonitorIdleTimeout()); } else { result = null; } @@ -324,7 +321,7 @@ private LowResourceMonitor createLowResourceMonitor( /** * Creates a Jetty server. - * + * * @return A Jetty server. */ private org.eclipse.jetty.server.Server createServer() { @@ -332,15 +329,14 @@ private org.eclipse.jetty.server.Server createServer() { final ThreadPool threadPool = createThreadPool(); // Server - final org.eclipse.jetty.server.Server jettyServer = new org.eclipse.jetty.server.Server( - threadPool); + final org.eclipse.jetty.server.Server jettyServer = + new org.eclipse.jetty.server.Server(threadPool); int serverMaxConnections = getServerMaxConnections(); if (serverMaxConnections > 0) { - ConnectionLimit connectionLimit = new ConnectionLimit( - serverMaxConnections, jettyServer); - connectionLimit - .setIdleTimeout(getServerMaxConnectionsIdleTimeout()); + ConnectionLimit connectionLimit = + new ConnectionLimit(serverMaxConnections, jettyServer); + connectionLimit.setIdleTimeout(getServerMaxConnectionsIdleTimeout()); jettyServer.addBean(connectionLimit); } @@ -358,8 +354,7 @@ private org.eclipse.jetty.server.Server createServer() { // Low-resource monitor (must be created after connectors have been // added) - LowResourceMonitor lowResourceMonitor = createLowResourceMonitor( - jettyServer); + LowResourceMonitor lowResourceMonitor = createLowResourceMonitor(jettyServer); jettyServer.addBean(lowResourceMonitor); return jettyServer; @@ -374,17 +369,18 @@ private Handler.Abstract createJettyHandler() { final Handler.Abstract result; final JettyServerHelper jettyServerHelper = this; - Handler.Abstract jettyServerHelperWrapperHandler = new Handler.Abstract() { - @Override - public boolean handle(Request request, Response response, - Callback callback) { - JettyServerCall httpCall = new JettyServerCall( - jettyServerHelper.getHelped(), request, response, - callback); - jettyServerHelper.handle(httpCall); - return true; // Indicates that the request is accepted - }; - }; + Handler.Abstract jettyServerHelperWrapperHandler = + new Handler.Abstract() { + @Override + public boolean handle(Request request, Response response, Callback callback) { + JettyServerCall httpCall = + new JettyServerCall( + jettyServerHelper.getHelped(), request, response, callback); + jettyServerHelper.handle(httpCall); + return true; // Indicates that the request is accepted + } + ; + }; if (getShutdownGracefully()) { // StatisticsHandler for graceful shutdown @@ -400,7 +396,7 @@ public boolean handle(Request request, Response response, /** * Creates a Jetty thread pool. - * + * * @return A Jetty thread pool. */ private ThreadPool createThreadPool() { @@ -419,32 +415,30 @@ private ThreadPool createThreadPool() { } /** - * Connector acceptor thread count. Defaults to -1. When -1, Jetty will - * default to 1. - * + * Connector acceptor thread count. Defaults to -1. When -1, Jetty will default to 1. + * * @return Connector acceptor thread count. */ public int getConnectorAcceptors() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("connector.acceptors", "-1")); + return Integer.parseInt(getHelpedParameters().getFirstValue("connector.acceptors", "-1")); } /** * Connector "accept" queue size. Defaults to 0. - *

- * Also known as "accept" backlog. - * + * + *

Also known as "accept" backlog. + * * @return Connector accept queue size. */ public int getConnectorAcceptQueueSize() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("connector.acceptQueueSize", "0")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("connector.acceptQueueSize", "0")); } /** - * Connector byte buffer pool. Defaults to null. When null, will use a new - * {@link ArrayByteBufferPool}. - * + * Connector byte buffer pool. Defaults to null. When null, will use a new {@link + * ArrayByteBufferPool}. + * * @return Connector byte buffer pool or null. */ public ByteBufferPool getConnectorByteBufferPool() { @@ -452,9 +446,8 @@ public ByteBufferPool getConnectorByteBufferPool() { } /** - * Connector executor. Defaults to null. When null, will use the server's - * thread pool. - * + * Connector executor. Defaults to null. When null, will use the server's thread pool. + * * @return Connector executor or null. */ public Executor getConnectorExecutor() { @@ -463,24 +456,23 @@ public Executor getConnectorExecutor() { /** * Connector idle timeout in milliseconds. Defaults to 30000. - *

- * See {@link Socket#setSoTimeout(int)}. - *

- * This value is interpreted as the maximum time between some progress being - * made on the connection. So if a single byte is read or written, then the - * timeout is reset. - * + * + *

See {@link Socket#setSoTimeout(int)}. + * + *

This value is interpreted as the maximum time between some progress being made on the + * connection. So if a single byte is read or written, then the timeout is reset. + * * @return Connector idle timeout. */ public int getConnectorIdleTimeout() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("connector.idleTimeout", "30000")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("connector.idleTimeout", "30000")); } /** - * Connector scheduler. Defaults to null. When null, will use a new - * {@link ScheduledExecutorScheduler}. - * + * Connector scheduler. Defaults to null. When null, will use a new {@link + * ScheduledExecutorScheduler}. + * * @return Connector scheduler or null. */ public Scheduler getConnectorScheduler() { @@ -488,114 +480,107 @@ public Scheduler getConnectorScheduler() { } /** - * Connector selector thread count. Defaults to -1. When less or equal than - * 0, Jetty computes a default value derived from a heuristic over available - * CPUs and thread pool size. - * + * Connector selector thread count. Defaults to -1. When less or equal than 0, Jetty computes a + * default value derived from a heuristic over available CPUs and thread pool size. + * * @return Connector acceptor thread count. */ public int getConnectorSelectors() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("connector.selectors", "-1")); + return Integer.parseInt(getHelpedParameters().getFirstValue("connector.selectors", "-1")); } /** * HTTP header cache size in bytes. Defaults to 512. - * + * * @return HTTP header cache size. */ public int getHttpHeaderCacheSize() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("http.headerCacheSize", "1024")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("http.headerCacheSize", "1024")); } /** * HTTP output buffer size in bytes. Defaults to 32*1024. - *

- * A larger buffer can improve performance by allowing a content producer to - * run without blocking, however, larger buffers consume more memory and may - * induce some latency before a client starts processing the content. - * + * + *

A larger buffer can improve performance by allowing a content producer to run without + * blocking, however, larger buffers consume more memory and may induce some latency before a + * client starts processing the content. + * * @return HTTP output buffer size. */ public int getHttpOutputBufferSize() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("http.outputBufferSize", "32768")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("http.outputBufferSize", "32768")); } /** * HTTP request header size in bytes. Defaults to 8*1024. - *

- * Larger headers will allow for more and/or larger cookies plus larger form - * content encoded in a URL. However, larger headers consume more memory and - * can make a server more vulnerable to denial of service attacks. - * + * + *

Larger headers will allow for more and/or larger cookies plus larger form content encoded + * in a URL. However, larger headers consume more memory and can make a server more vulnerable + * to denial of service attacks. + * * @return HTTP request header size. */ public int getHttpRequestHeaderSize() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("http.requestHeaderSize", "8192")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("http.requestHeaderSize", "8192")); } /** * HTTP response header size in bytes. Defaults to 8*1024. - *

- * Larger headers will allow for more and/or larger cookies and longer HTTP - * headers (e.g., for redirection). However, larger headers will also - * consume more memory. - * + * + *

Larger headers will allow for more and/or larger cookies and longer HTTP headers (e.g., + * for redirection). However, larger headers will also consume more memory. + * * @return HTTP response header size. */ public int getHttpResponseHeaderSize() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("http.responseHeaderSize", "8192")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("http.responseHeaderSize", "8192")); } /** * Low-resource monitor idle timeout in milliseconds. Defaults to 1000. - *

- * Applied to EndPoints when in the low-resources state. - * + * + *

Applied to EndPoints when in the low-resources state. + * * @return Low-resource monitor idle timeout. */ public int getLowResourceMonitorIdleTimeout() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("lowResource.idleTimeout", "1000")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("lowResource.idleTimeout", "1000")); } /** - * Low-resource monitor max memory in bytes. Defaults to 0. When 0, the - * check is disabled. - *

- * Memory used is calculated as (totalMemory-freeMemory). - * + * Low-resource monitor max memory in bytes. Defaults to 0. When 0, the check is disabled. + * + *

Memory used is calculated as (totalMemory-freeMemory). + * * @return Low-resource monitor max memory. */ public long getLowResourceMonitorMaxMemory() { - return Long.parseLong(getHelpedParameters() - .getFirstValue("lowResource.maxMemory", "0")); + return Long.parseLong(getHelpedParameters().getFirstValue("lowResource.maxMemory", "0")); } /** - * Low-resource monitor period in milliseconds. Defaults to 1000. When 0, - * low-resource monitoring is disabled. - * + * Low-resource monitor period in milliseconds. Defaults to 1000. When 0, low-resource + * monitoring is disabled. + * * @return Low-resource monitor period. */ public int getLowResourceMonitorPeriod() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("lowResource.period", "1000")); + return Integer.parseInt(getHelpedParameters().getFirstValue("lowResource.period", "1000")); } /** - * Low-resource monitor, whether to check if we're low on threads. Defaults - * to true. - * + * Low-resource monitor, whether to check if we're low on threads. Defaults to true. + * * @return Low-resource monitor threads. */ public boolean getLowResourceMonitorThreads() { - return Boolean.parseBoolean(getHelpedParameters() - .getFirstValue("lowResource.threads", "true")); + return Boolean.parseBoolean( + getHelpedParameters().getFirstValue("lowResource.threads", "true")); } /** @@ -604,39 +589,35 @@ public boolean getLowResourceMonitorThreads() { * @return Low-resource monitor max connections. */ public int getServerMaxConnections() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("server.maxConnections", "0")); + return Integer.parseInt(getHelpedParameters().getFirstValue("server.maxConnections", "0")); } /** - * The endpoint idle timeout in milliseconds to apply when the connection - * limit is reached. Defaults to 0. When 0, there is no idle timeout. - *

- * The maximum time allowed for the endpoint to close when the connection - * limit is reached. + * The endpoint idle timeout in milliseconds to apply when the connection limit is reached. + * Defaults to 0. When 0, there is no idle timeout. + * + *

The maximum time allowed for the endpoint to close when the connection limit is reached. * - * @return The endpoint idle timeout in milliseconds to apply when the - * connection limit is reached. + * @return The endpoint idle timeout in milliseconds to apply when the connection limit is + * reached. */ public long getServerMaxConnectionsIdleTimeout() { - return Long.parseLong(getHelpedParameters() - .getFirstValue("server.maxConnections.idleTimeout", "0")); + return Long.parseLong( + getHelpedParameters().getFirstValue("server.maxConnections.idleTimeout", "0")); } /** - * When true, upon JVM shutdown, the Jetty server will block incoming - * requests and wait for pending requests to end before shutting down. - * Otherwise, incoming requests are not blocked and all requests are - * aborted. Defaults to true. - * - * @return True if upon JVM shutdown, the Jetty server will block incoming - * requests and wait for pending requests to end before shutting - * down. Otherwise, incoming requests are not blocked and all - * requests are aborted. + * When true, upon JVM shutdown, the Jetty server will block incoming requests and wait for + * pending requests to end before shutting down. Otherwise, incoming requests are not blocked + * and all requests are aborted. Defaults to true. + * + * @return True if upon JVM shutdown, the Jetty server will block incoming requests and wait for + * pending requests to end before shutting down. Otherwise, incoming requests are not + * blocked and all requests are aborted. */ public boolean getShutdownGracefully() { - return Boolean.parseBoolean(getHelpedParameters() - .getFirstValue("shutdown.gracefully", "true")); + return Boolean.parseBoolean( + getHelpedParameters().getFirstValue("shutdown.gracefully", "true")); } /** @@ -645,68 +626,68 @@ public boolean getShutdownGracefully() { * @return Server shutdown timeout. */ public long getShutdownTimeout() { - return Long.parseLong( - getHelpedParameters().getFirstValue("shutdown.timeout", "0")); + return Long.parseLong(getHelpedParameters().getFirstValue("shutdown.timeout", "0")); } /** * Thread pool idle timeout in milliseconds. Defaults to 60000. - *

- * Threads that are idle for longer than this period may be stopped. - * + * + *

Threads that are idle for longer than this period may be stopped. + * * @return Thread pool idle timeout. */ public int getThreadPoolIdleTimeout() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("threadPool.idleTimeout", "60000")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("threadPool.idleTimeout", "60000")); } /** * Thread pool maximum threads. Defaults to 200. - * + * * @return Thread pool maximum threads. */ public int getThreadPoolMaxThreads() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("threadPool.maxThreads", "200")); + return Integer.parseInt( + getHelpedParameters().getFirstValue("threadPool.maxThreads", "200")); } /** * Thread pool minimum threads. Defaults to 8. - * + * * @return Thread pool minimum threads. */ public int getThreadPoolMinThreads() { - return Integer.parseInt(getHelpedParameters() - .getFirstValue("threadPool.minThreads", "8")); + return Integer.parseInt(getHelpedParameters().getFirstValue("threadPool.minThreads", "8")); } /** * Thread pool stop timeout in milliseconds. Defaults to 5000. - *

- * The maximum time allowed for the service to shut down. - * + * + *

The maximum time allowed for the service to shut down. + * * @return Thread pool stop timeout. */ public long getThreadPoolStopTimeout() { - return Long.parseLong(getHelpedParameters() - .getFirstValue("threadPool.stopTimeout", "5000")); + return Long.parseLong( + getHelpedParameters().getFirstValue("threadPool.stopTimeout", "5000")); } /** * Thread pool threads priority. Defaults to {@link Thread#NORM_PRIORITY}. - * + * * @return Thread pool maximum threads. */ public int getThreadPoolThreadsPriority() { - return Integer.parseInt(getHelpedParameters().getFirstValue( - "threadPool.threadsPriority", - String.valueOf(Thread.NORM_PRIORITY))); + return Integer.parseInt( + getHelpedParameters() + .getFirstValue( + "threadPool.threadsPriority", + String.valueOf(Thread.NORM_PRIORITY))); } /** * Returns the wrapped Jetty server. - * + * * @return The wrapped Jetty server. */ protected org.eclipse.jetty.server.Server getWrappedServer() { @@ -718,11 +699,10 @@ protected org.eclipse.jetty.server.Server getWrappedServer() { /** * Sets the wrapped Jetty server. - * + * * @param wrappedServer The wrapped Jetty server. */ - protected void setWrappedServer( - org.eclipse.jetty.server.Server wrappedServer) { + protected void setWrappedServer(org.eclipse.jetty.server.Server wrappedServer) { Objects.requireNonNull(wrappedServer); this.wrappedServer = wrappedServer; } @@ -731,11 +711,14 @@ protected void setWrappedServer( public void start() throws Exception { super.start(); org.eclipse.jetty.server.Server server = getWrappedServer(); - AbstractNetworkConnector connector = (AbstractNetworkConnector) server - .getConnectors()[0]; - - getLogger().info("Starting the Jetty " + getProtocols() - + " server on port " + getHelped().getPort()); + AbstractNetworkConnector connector = (AbstractNetworkConnector) server.getConnectors()[0]; + + getLogger() + .info( + "Starting the Jetty " + + getProtocols() + + " server on port " + + getHelped().getPort()); try { server.start(); // We won't know the local port until after the server starts @@ -746,13 +729,16 @@ public void start() throws Exception { server.stop(); throw e; } - } @Override public void stop() throws Exception { - getLogger().info("Stopping the Jetty " + getProtocols() - + " server on port " + getHelped().getPort()); + getLogger() + .info( + "Stopping the Jetty " + + getProtocols() + + " server on port " + + getHelped().getPort()); if (this.wrappedServer != null) { getWrappedServer().stop(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/Method.java b/org.restlet/src/main/java/org/restlet/engine/connector/Method.java index 20dc2d18c1..d0acb00900 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/Method.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/Method.java @@ -1,24 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; -import org.restlet.resource.*; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.restlet.resource.Delete; +import org.restlet.resource.Get; +import org.restlet.resource.Options; +import org.restlet.resource.Patch; +import org.restlet.resource.Post; +import org.restlet.resource.Put; /** * Meta annotation to declare method annotations. - * + * * @see Get * @see Post * @see Put @@ -31,11 +34,10 @@ @Retention(RetentionPolicy.RUNTIME) public @interface Method { - /** - * Method name identified by the underlying annotation. - * - * @return the method name identified by the underlying annotation. - */ - String value(); - + /** + * Method name identified by the underlying annotation. + * + * @return the method name identified by the underlying annotation. + */ + String value(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java index 070c3f3bd6..c2ddcc2e07 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java @@ -1,35 +1,31 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import org.restlet.engine.Helper; /** * Protocol helper. - * + * * @author Thierry Boileau */ public abstract class ProtocolHelper extends Helper { - /** - * Constructor. - */ - public ProtocolHelper() { - super(); - registerMethods(); - } - - /** - * Register all supported methods. The implementation relies on the - * {@link org.restlet.data.Method#register(org.restlet.data.Method)} method. - */ - public abstract void registerMethods(); + /** Constructor. */ + public ProtocolHelper() { + super(); + registerMethods(); + } + /** + * Register all supported methods. The implementation relies on the {@link + * org.restlet.data.Method#register(org.restlet.data.Method)} method. + */ + public abstract void registerMethods(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java index 83016bd92e..ee9cbcd21e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import org.restlet.Request; @@ -15,64 +14,63 @@ /** * Server connector helper. - * + * * @author Jerome Louvel */ public class ServerHelper extends ConnectorHelper { - /** - * Constructor. - * - * @param server The client to help. - */ - public ServerHelper(Server server) { - super(server); - - // Clear the ephemeral port - getAttributes().put("ephemeralPort", -1); - } + /** + * Constructor. + * + * @param server The client to help. + */ + public ServerHelper(Server server) { + super(server); - /** - * Handles a call by invoking the helped Server's - * {@link Server#handle(Request, Response)} method. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - getHelped().handle(request, response); - } + // Clear the ephemeral port + getAttributes().put("ephemeralPort", -1); + } - /** - * Sets the ephemeral port in the attributes map if necessary. - * - * @param localPort The ephemeral local port. - */ - public void setEphemeralPort(int localPort) { - // If an ephemeral port is used, make sure we update the attribute for - // the API - if (getHelped().getPort() == 0) { - getAttributes().put("ephemeralPort", localPort); - } - } + /** + * Handles a call by invoking the helped Server's {@link Server#handle(Request, Response)} + * method. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public void handle(Request request, Response response) { + super.handle(request, response); + getHelped().handle(request, response); + } - /** - * Sets the ephemeral port in the attributes map if necessary. - * - * @param socket The bound server socket. - */ - public void setEphemeralPort(java.net.ServerSocket socket) { - setEphemeralPort(socket.getLocalPort()); - } + /** + * Sets the ephemeral port in the attributes map if necessary. + * + * @param localPort The ephemeral local port. + */ + public void setEphemeralPort(int localPort) { + // If an ephemeral port is used, make sure we update the attribute for + // the API + if (getHelped().getPort() == 0) { + getAttributes().put("ephemeralPort", localPort); + } + } - @Override - public synchronized void stop() throws Exception { - super.stop(); + /** + * Sets the ephemeral port in the attributes map if necessary. + * + * @param socket The bound server socket. + */ + public void setEphemeralPort(java.net.ServerSocket socket) { + setEphemeralPort(socket.getLocalPort()); + } - // Clear the ephemeral port - getAttributes().put("ephemeralPort", -1); - } + @Override + public synchronized void stop() throws Exception { + super.stop(); + // Clear the ephemeral port + getAttributes().put("ephemeralPort", -1); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java index 2c814d059f..2bdc6ffbcb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.converter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.restlet.data.ClientInfo; import org.restlet.data.MediaType; import org.restlet.data.Preference; @@ -18,196 +20,193 @@ import org.restlet.representation.Variant; import org.restlet.resource.Resource; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - /** * Converter between Representations and regular Java objects. - * + * * @author Jerome Louvel */ public abstract class ConverterHelper extends Helper { - /** - * Adds an object class to the given list. Creates a new list if necessary. - * - * @param objectClasses The object classes list to update or null. - * @param objectClass The object class to add. - * @return The input object classes list or a new one. - */ - protected List> addObjectClass(List> objectClasses, Class objectClass) { - if (objectClasses == null) { - objectClasses = new ArrayList>(); - } - - objectClasses.add(objectClass); - return objectClasses; - } - - /** - * Adds a variant to the given list. Creates a new list if necessary. - * - * @param variants The variants list to update or null. - * @param userVariant The variant to add if not null. - * @return The input variants list or a new one. - */ - protected List addVariant(List variants, VariantInfo userVariant) { - if (userVariant != null) { - if (variants == null) { - variants = new ArrayList<>(); - } - - variants.add(userVariant); - } - - return variants; - } - - /** - * Returns the list of variants that can be converted from a given object class. - * - * @param sourceClass The source class. - * @param targetVariant The expected representation metadata. - * @param variants The variants list to update. - * @return The list of variants that can be converted from a given object class. - * @throws IOException - */ - public List addVariants(Class sourceClass, Variant targetVariant, List variants) - throws IOException { - // List of variants that can be converted from the source class - List helperVariants = getVariants(sourceClass); - - if (helperVariants != null) { - // Loop over the variants list - for (VariantInfo helperVariant : helperVariants) { - if (targetVariant == null) { - variants = addVariant(variants, helperVariant); - } else if (helperVariant.includes(targetVariant)) { - // Detected a more generic variant, but still - // consider - // the conversion is possible to the target variant. - variants = addVariant(variants, new VariantInfo(targetVariant.getMediaType())); - } else if (targetVariant.includes(helperVariant)) { - // Detected a more specific variant, but still - // consider - // the conversion is possible to the target variant. - variants = addVariant(variants, helperVariant); - } - } - } - - return variants; - } - - /** - * Returns the list of object classes that can be converted from a given - * variant. - * - * @param source The source variant. - * @return The list of object class that can be converted. - */ - public abstract List> getObjectClasses(Variant source); - - /** - * Returns the list of variants that can be converted from a given object class. - * The preferred variant should be set in first position. - * - * @param source The source object class. - * @return The list of variants that can be converted. - */ - public abstract List getVariants(Class source) throws IOException; - - /** - * Returns the list of variants that can be converted from a given object class - * by a specific converter helper. - * - * @param sourceClass The source class. - * @param targetVariant The expected representation metadata. - * @return The list of variants that can be converted. - * @throws IOException - */ - public List getVariants(Class sourceClass, Variant targetVariant) throws IOException { - return addVariants(sourceClass, targetVariant, null); - } - - /** - * Scores the affinity of this helper with the source class. - * - * @param source The source object to convert. - * @param target The expected representation metadata. - * @param resource The calling resource. - * @return The affinity score of this helper. - */ - public abstract float score(Object source, Variant target, Resource resource); - - /** - * Scores the affinity of this helper with the source class. - * - * @param The expected class of the Java object. - * @param source The source representation to convert. - * @param target The expected class of the Java object. - * @param resource The calling resource. - * @return The affinity score of this helper. - */ - public abstract float score(Representation source, Class target, Resource resource); - - /** - * Converts a Representation into a regular Java object. - * - * @param The expected class of the Java object. - * @param source The source representation to convert. - * @param target The expected class of the Java object. - * @param resource The calling resource. - * @return The converted Java object. - */ - public abstract T toObject(Representation source, Class target, Resource resource) throws IOException; - - /** - * Converts a regular Java object into a Representation. - * - * @param source The source object to convert. - * @param target The expected representation metadata. - * @param resource The calling resource. - * @return The converted representation. - */ - public abstract Representation toRepresentation(Object source, Variant target, Resource resource) - throws IOException; - - /** - * Updates the preferences of the given {@link ClientInfo} object with - * conversion capabilities for the given entity class. - * - * @param preferences The media type preferences. - * @param entity The entity class to convert. - */ - public void updatePreferences(List> preferences, Class entity) { - // Does nothing by default - } - - /** - * Updates the preferences of the given {@link ClientInfo} object with - * conversion capabilities for the given entity class. - * - * @param preferences The media type preferences. - * @param mediaType The media type to update to add to the preferences. - * @param score The media type score to use as a quality score. - */ - public void updatePreferences(List> preferences, MediaType mediaType, float score) { - boolean found = false; - Preference preference; - - for (int i = 0; !found && (i < preferences.size()); i++) { - preference = preferences.get(i); - - if (preference.getMetadata().equals(mediaType) && (preference.getQuality() < score)) { - preference.setQuality(score); - found = true; - } - } - - if (!found) { - preferences.add(new Preference(mediaType, score)); - } - } + /** + * Adds an object class to the given list. Creates a new list if necessary. + * + * @param objectClasses The object classes list to update or null. + * @param objectClass The object class to add. + * @return The input object classes list or a new one. + */ + protected List> addObjectClass(List> objectClasses, Class objectClass) { + if (objectClasses == null) { + objectClasses = new ArrayList>(); + } + + objectClasses.add(objectClass); + return objectClasses; + } + + /** + * Adds a variant to the given list. Creates a new list if necessary. + * + * @param variants The variants list to update or null. + * @param userVariant The variant to add if not null. + * @return The input variants list or a new one. + */ + protected List addVariant(List variants, VariantInfo userVariant) { + if (userVariant != null) { + if (variants == null) { + variants = new ArrayList<>(); + } + + variants.add(userVariant); + } + + return variants; + } + + /** + * Returns the list of variants that can be converted from a given object class. + * + * @param sourceClass The source class. + * @param targetVariant The expected representation metadata. + * @param variants The variants list to update. + * @return The list of variants that can be converted from a given object class. + * @throws IOException + */ + public List addVariants( + Class sourceClass, Variant targetVariant, List variants) + throws IOException { + // List of variants that can be converted from the source class + List helperVariants = getVariants(sourceClass); + + if (helperVariants != null) { + // Loop over the variants list + for (VariantInfo helperVariant : helperVariants) { + if (targetVariant == null) { + variants = addVariant(variants, helperVariant); + } else if (helperVariant.includes(targetVariant)) { + // Detected a more generic variant, but still consider the conversion is + // possible to the target variant. + variants = addVariant(variants, new VariantInfo(targetVariant.getMediaType())); + } else if (targetVariant.includes(helperVariant)) { + // Detected a more specific variant, but still consider the conversion is + // possible to the target variant. + variants = addVariant(variants, helperVariant); + } + } + } + + return variants; + } + + /** + * Returns the list of object classes that can be converted from a given variant. + * + * @param source The source variant. + * @return The list of object class that can be converted. + */ + public abstract List> getObjectClasses(Variant source); + + /** + * Returns the list of variants that can be converted from a given object class. The preferred + * variant should be set in first position. + * + * @param source The source object class. + * @return The list of variants that can be converted. + */ + public abstract List getVariants(Class source) throws IOException; + + /** + * Returns the list of variants that can be converted from a given object class by a specific + * converter helper. + * + * @param sourceClass The source class. + * @param targetVariant The expected representation metadata. + * @return The list of variants that can be converted. + * @throws IOException + */ + public List getVariants(Class sourceClass, Variant targetVariant) + throws IOException { + return addVariants(sourceClass, targetVariant, null); + } + + /** + * Scores the affinity of this helper with the source class. + * + * @param source The source object to convert. + * @param target The expected representation metadata. + * @param resource The calling resource. + * @return The affinity score of this helper. + */ + public abstract float score(Object source, Variant target, Resource resource); + + /** + * Scores the affinity of this helper with the source class. + * + * @param The expected class of the Java object. + * @param source The source representation to convert. + * @param target The expected class of the Java object. + * @param resource The calling resource. + * @return The affinity score of this helper. + */ + public abstract float score(Representation source, Class target, Resource resource); + + /** + * Converts a Representation into a regular Java object. + * + * @param The expected class of the Java object. + * @param source The source representation to convert. + * @param target The expected class of the Java object. + * @param resource The calling resource. + * @return The converted Java object. + */ + public abstract T toObject(Representation source, Class target, Resource resource) + throws IOException; + + /** + * Converts a regular Java object into a Representation. + * + * @param source The source object to convert. + * @param target The expected representation metadata. + * @param resource The calling resource. + * @return The converted representation. + */ + public abstract Representation toRepresentation( + Object source, Variant target, Resource resource) throws IOException; + + /** + * Updates the preferences of the given {@link ClientInfo} object with conversion capabilities + * for the given entity class. + * + * @param preferences The media type preferences. + * @param entity The entity class to convert. + */ + public void updatePreferences(List> preferences, Class entity) { + // Does nothing by default + } + + /** + * Updates the preferences of the given {@link ClientInfo} object with conversion capabilities + * for the given entity class. + * + * @param preferences The media type preferences. + * @param mediaType The media type to update to add to the preferences. + * @param score The media type score to use as a quality score. + */ + public void updatePreferences( + List> preferences, MediaType mediaType, float score) { + boolean found = false; + Preference preference; + + for (int i = 0; !found && (i < preferences.size()); i++) { + preference = preferences.get(i); + + if (preference.getMetadata().equals(mediaType) && (preference.getQuality() < score)) { + preference.setQuality(score); + found = true; + } + } + + if (!found) { + preferences.add(new Preference<>(mediaType, score)); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java index f32a5ebad2..5058da778d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.converter; +import java.io.IOException; +import java.util.List; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.engine.Engine; import org.restlet.engine.resource.VariantInfo; @@ -16,105 +18,107 @@ import org.restlet.representation.Variant; import org.restlet.resource.Resource; -import java.io.IOException; -import java.util.List; -import java.util.logging.Level; - /** * Utilities for the converter service. - * + * * @author Jerome Louvel */ public class ConverterUtils { - /** - * Returns the best converter helper matching the given parameters. - * - * @param source The object to convert to a representation. - * @param target The target representation variant. - * @param resource The optional parent resource. - * @return The matched converter helper or null. - */ - public static ConverterHelper getBestHelper(Object source, Variant target, Resource resource) { - ConverterHelper result = null; - float bestScore = -1.0F; - float currentScore; - - for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { - if (ch != null) { - try { - currentScore = ch.score(source, target, resource); - - if (currentScore > bestScore) { - bestScore = currentScore; - result = ch; - } - } catch (Exception e) { - Context.getCurrentLogger().log(Level.SEVERE, - "Unable get the score of the " + ch + " converter helper.", e); - } - } - } - - return result; - } - - /** - * Returns the best converter helper matching the given parameters. - * - * @param The target class. - * @param source The source representation variant. - * @param target The target class. - * @param resource The parent resource. - * @return The matched converter helper or null. - */ - public static ConverterHelper getBestHelper(Representation source, Class target, Resource resource) { - ConverterHelper result = null; - float bestScore = -1.0F; - float currentScore; - - for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { - if (ch != null) { - currentScore = ch.score(source, target, resource); - - if (currentScore > bestScore) { - bestScore = currentScore; - result = ch; - } - } - } - - return result; - } - - /** - * Returns the list of variants that can be converted from a given object class. - * - * @param sourceClass The source class. - * @param targetVariant The expected representation metadata. - * @return The list of variants that can be converted. - */ - public static List getVariants(Class sourceClass, Variant targetVariant) { - List result = null; - - for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { - if (ch != null) { - try { - result = ch.addVariants(sourceClass, targetVariant, result); - } catch (IOException e) { - Context.getCurrentLogger().log(Level.FINE, - "Unable get the variants of the " + ch + " converter helper.", e); - } - } - } - - return result; - } - - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private ConverterUtils() { - } + /** + * Returns the best converter helper matching the given parameters. + * + * @param source The object to convert to a representation. + * @param target The target representation variant. + * @param resource The optional parent resource. + * @return The matched converter helper or null. + */ + public static ConverterHelper getBestHelper(Object source, Variant target, Resource resource) { + ConverterHelper result = null; + float bestScore = -1.0F; + float currentScore; + + for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { + if (ch != null) { + try { + currentScore = ch.score(source, target, resource); + + if (currentScore > bestScore) { + bestScore = currentScore; + result = ch; + } + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.SEVERE, + "Unable get the score of the " + ch + " converter helper.", + e); + } + } + } + + return result; + } + + /** + * Returns the best converter helper matching the given parameters. + * + * @param The target class. + * @param source The source representation variant. + * @param target The target class. + * @param resource The parent resource. + * @return The matched converter helper or null. + */ + public static ConverterHelper getBestHelper( + Representation source, Class target, Resource resource) { + ConverterHelper result = null; + float bestScore = -1.0F; + float currentScore; + + for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { + if (ch != null) { + currentScore = ch.score(source, target, resource); + + if (currentScore > bestScore) { + bestScore = currentScore; + result = ch; + } + } + } + + return result; + } + + /** + * Returns the list of variants that can be converted from a given object class. + * + * @param sourceClass The source class. + * @param targetVariant The expected representation metadata. + * @return The list of variants that can be converted. + */ + public static List getVariants(Class sourceClass, Variant targetVariant) { + List result = null; + + for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { + if (ch != null) { + try { + result = ch.addVariants(sourceClass, targetVariant, result); + } catch (IOException e) { + Context.getCurrentLogger() + .log( + Level.FINE, + "Unable get the variants of the " + ch + " converter helper.", + e); + } + } + } + + return result; + } + + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private ConverterUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java index 6af4cbc3bf..24beb240b1 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java @@ -1,291 +1,323 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.converter; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.Serializable; +import java.util.List; import org.restlet.data.Form; import org.restlet.data.MediaType; import org.restlet.data.Preference; import org.restlet.engine.resource.VariantInfo; -import org.restlet.representation.*; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.FileRepresentation; +import org.restlet.representation.InputRepresentation; +import org.restlet.representation.ObjectRepresentation; +import org.restlet.representation.ReaderRepresentation; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.representation.Variant; import org.restlet.resource.Resource; -import java.io.*; -import java.util.List; - /** * Converter for the built-in Representation classes. - * + * * @author Jerome Louvel */ public class DefaultConverter extends ConverterHelper { - /** Neutral variant. */ - private static final VariantInfo VARIANT_ALL = new VariantInfo(MediaType.ALL); - - /** Web form variant. */ - private static final VariantInfo VARIANT_FORM = new VariantInfo(MediaType.APPLICATION_WWW_FORM); + /** Neutral variant. */ + private static final VariantInfo VARIANT_ALL = new VariantInfo(MediaType.ALL); - /** Octet stream variant. */ - private static final VariantInfo VARIANT_OBJECT = new VariantInfo(MediaType.APPLICATION_JAVA_OBJECT); + /** Web form variant. */ + private static final VariantInfo VARIANT_FORM = new VariantInfo(MediaType.APPLICATION_WWW_FORM); - /** Octet stream variant. */ - private static final VariantInfo VARIANT_OBJECT_XML = new VariantInfo(MediaType.APPLICATION_JAVA_OBJECT_XML); + /** Octet stream variant. */ + private static final VariantInfo VARIANT_OBJECT = + new VariantInfo(MediaType.APPLICATION_JAVA_OBJECT); - @Override - public List> getObjectClasses(Variant source) { - List> result = null; - result = addObjectClass(result, String.class); - result = addObjectClass(result, InputStream.class); - result = addObjectClass(result, Reader.class); + /** Octet stream variant. */ + private static final VariantInfo VARIANT_OBJECT_XML = + new VariantInfo(MediaType.APPLICATION_JAVA_OBJECT_XML); - if (source.getMediaType() != null) { - MediaType mediaType = source.getMediaType(); + @Override + public List> getObjectClasses(Variant source) { + List> result = null; + result = addObjectClass(result, String.class); + result = addObjectClass(result, InputStream.class); + result = addObjectClass(result, Reader.class); - if ((ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT.equals(mediaType)) - || (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT_XML.equals(mediaType))) { - result = addObjectClass(result, Object.class); - } else if (MediaType.APPLICATION_WWW_FORM.equals(mediaType)) { - result = addObjectClass(result, Form.class); - } - } + if (source.getMediaType() != null) { + MediaType mediaType = source.getMediaType(); - return result; - } + if ((ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT.equals(mediaType)) + || (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT_XML.equals(mediaType))) { + result = addObjectClass(result, Object.class); + } else if (MediaType.APPLICATION_WWW_FORM.equals(mediaType)) { + result = addObjectClass(result, Form.class); + } + } - @Override - public List getVariants(Class source) { - List result = null; + return result; + } - if (source != null) { - if (String.class.isAssignableFrom(source) || StringRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (File.class.isAssignableFrom(source) || FileRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (InputStream.class.isAssignableFrom(source) - || InputRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (Reader.class.isAssignableFrom(source) || ReaderRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (Representation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (Form.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_FORM); - } else if (Serializable.class.isAssignableFrom(source)) { - if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { - result = addVariant(result, VARIANT_OBJECT); - } - if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { - result = addVariant(result, VARIANT_OBJECT_XML); - } - } - } + @Override + public List getVariants(Class source) { + List result = null; - return result; - } + if (source != null) { + if (String.class.isAssignableFrom(source) + || StringRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (File.class.isAssignableFrom(source) + || FileRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (InputStream.class.isAssignableFrom(source) + || InputRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (Reader.class.isAssignableFrom(source) + || ReaderRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (Representation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (Form.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_FORM); + } else if (Serializable.class.isAssignableFrom(source)) { + if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { + result = addVariant(result, VARIANT_OBJECT); + } + if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { + result = addVariant(result, VARIANT_OBJECT_XML); + } + } + } - @Override - public float score(Object source, Variant target, Resource resource) { - float result = -1.0F; + return result; + } - if (source instanceof String) { - result = 1.0F; - } else if (source instanceof File) { - result = 1.0F; - } else if (source instanceof Form) { - if ((target != null) && MediaType.APPLICATION_WWW_FORM.isCompatible(target.getMediaType())) { - result = 1.0F; - } else { - result = 0.6F; - } - } else if (source instanceof InputStream) { - result = 1.0F; - } else if (source instanceof Reader) { - result = 1.0F; - } else if (source instanceof Representation) { - result = 1.0F; - } else if (source instanceof Serializable) { - if (target != null) { - if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT.equals(target.getMediaType())) { - result = 1.0F; - } else if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT.isCompatible(target.getMediaType())) { - result = 0.6F; - } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT_XML.equals(target.getMediaType())) { - result = 1.0F; - } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT_XML.isCompatible(target.getMediaType())) { - result = 0.6F; - } - } else if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { - result = 0.5F; - } - } + @Override + public float score(Object source, Variant target, Resource resource) { + float result = -1.0F; - return result; - } + if (source instanceof String) { + result = 1.0F; + } else if (source instanceof File) { + result = 1.0F; + } else if (source instanceof Form) { + if ((target != null) + && MediaType.APPLICATION_WWW_FORM.isCompatible(target.getMediaType())) { + result = 1.0F; + } else { + result = 0.6F; + } + } else if (source instanceof InputStream) { + result = 1.0F; + } else if (source instanceof Reader) { + result = 1.0F; + } else if (source instanceof Representation) { + result = 1.0F; + } else if (source instanceof Serializable) { + if (target != null) { + if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT.equals(target.getMediaType())) { + result = 1.0F; + } else if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT.isCompatible(target.getMediaType())) { + result = 0.6F; + } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT_XML.equals(target.getMediaType())) { + result = 1.0F; + } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT_XML.isCompatible( + target.getMediaType())) { + result = 0.6F; + } + } else if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { + result = 0.5F; + } + } - @Override - public float score(Representation source, Class target, Resource resource) { - float result = -1.0F; + return result; + } - if (target != null) { - if (target.isAssignableFrom(source.getClass())) { - result = 1.0F; - } else if (String.class.isAssignableFrom(target)) { - result = 1.0F; - } else if (StringRepresentation.class.isAssignableFrom(target)) { - result = 1.0F; - } else if (EmptyRepresentation.class.isAssignableFrom(target)) { - result = 1.0F; - } else if (File.class.isAssignableFrom(target)) { - if (source instanceof FileRepresentation) { - result = 1.0F; - } - } else if (Form.class.isAssignableFrom(target)) { - if (MediaType.APPLICATION_WWW_FORM.isCompatible(source.getMediaType())) { - result = 1.0F; - } else { - result = 0.5F; - } - } else if (InputStream.class.isAssignableFrom(target)) { - result = 1.0F; - } else if (InputRepresentation.class.isAssignableFrom(target)) { - result = 1.0F; - } else if (Reader.class.isAssignableFrom(target)) { - result = 1.0F; - } else if (ReaderRepresentation.class.isAssignableFrom(target)) { - result = 1.0F; - } else if (Serializable.class.isAssignableFrom(target) || target.isPrimitive()) { - if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT.equals(source.getMediaType())) { - result = 1.0F; - } else if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT.isCompatible(source.getMediaType())) { - result = 0.6F; - } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT_XML.equals(source.getMediaType())) { - result = 1.0F; - } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED - && MediaType.APPLICATION_JAVA_OBJECT_XML.isCompatible(source.getMediaType())) { - result = 0.6F; - } else { - result = 0.5F; - } - } - } else if (source instanceof ObjectRepresentation) { - result = 1.0F; - } + @Override + public float score(Representation source, Class target, Resource resource) { + float result = -1.0F; - return result; - } + if (target != null) { + if (target.isAssignableFrom(source.getClass())) { + result = 1.0F; + } else if (String.class.isAssignableFrom(target)) { + result = 1.0F; + } else if (StringRepresentation.class.isAssignableFrom(target)) { + result = 1.0F; + } else if (EmptyRepresentation.class.isAssignableFrom(target)) { + result = 1.0F; + } else if (File.class.isAssignableFrom(target)) { + if (source instanceof FileRepresentation) { + result = 1.0F; + } + } else if (Form.class.isAssignableFrom(target)) { + if (MediaType.APPLICATION_WWW_FORM.isCompatible(source.getMediaType())) { + result = 1.0F; + } else { + result = 0.5F; + } + } else if (InputStream.class.isAssignableFrom(target)) { + result = 1.0F; + } else if (InputRepresentation.class.isAssignableFrom(target)) { + result = 1.0F; + } else if (Reader.class.isAssignableFrom(target)) { + result = 1.0F; + } else if (ReaderRepresentation.class.isAssignableFrom(target)) { + result = 1.0F; + } else if (Serializable.class.isAssignableFrom(target) || target.isPrimitive()) { + if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT.equals(source.getMediaType())) { + result = 1.0F; + } else if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT.isCompatible(source.getMediaType())) { + result = 0.6F; + } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT_XML.equals(source.getMediaType())) { + result = 1.0F; + } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED + && MediaType.APPLICATION_JAVA_OBJECT_XML.isCompatible( + source.getMediaType())) { + result = 0.6F; + } else { + result = 0.5F; + } + } + } else if (source instanceof ObjectRepresentation) { + result = 1.0F; + } - @SuppressWarnings("unchecked") - @Override - public T toObject(Representation source, Class target, Resource resource) throws IOException { - Object result = null; + return result; + } - if (target != null) { - if (target.isAssignableFrom(source.getClass())) { - result = source; - } else if (String.class.isAssignableFrom(target)) { - result = source.getText(); - } else if (StringRepresentation.class.isAssignableFrom(target)) { - result = new StringRepresentation(source.getText(), source.getMediaType()); - } else if (EmptyRepresentation.class.isAssignableFrom(target)) { - result = null; - } else if (File.class.isAssignableFrom(target)) { - if (source instanceof FileRepresentation) { - result = ((FileRepresentation) source).getFile(); - } else { - result = null; - } - } else if (Form.class.isAssignableFrom(target)) { - result = new Form(source); - } else if (InputStream.class.isAssignableFrom(target)) { - result = source.getStream(); - } else if (InputRepresentation.class.isAssignableFrom(target)) { - result = new InputRepresentation(source.getStream()); - } else if (Reader.class.isAssignableFrom(target)) { - result = source.getReader(); - } else if (ReaderRepresentation.class.isAssignableFrom(target)) { - result = new ReaderRepresentation(source.getReader()); - } else if (Serializable.class.isAssignableFrom(target) || target.isPrimitive()) { - if (source instanceof ObjectRepresentation) { - result = ((ObjectRepresentation) source).getObject(); - } else { - try { - result = new ObjectRepresentation(source).getObject(); - } catch (Exception e) { - IOException ioe = new IOException("Unable to create the Object representation"); - ioe.initCause(e); - throw ioe; - } - } - } - } else if (source instanceof ObjectRepresentation) { - result = ((ObjectRepresentation) source).getObject(); - } + @SuppressWarnings("unchecked") + @Override + public T toObject(Representation source, Class target, Resource resource) + throws IOException { + Object result = null; - return (T) result; - } + if (target != null) { + if (target.isAssignableFrom(source.getClass())) { + result = source; + } else if (String.class.isAssignableFrom(target)) { + result = source.getText(); + } else if (StringRepresentation.class.isAssignableFrom(target)) { + result = new StringRepresentation(source.getText(), source.getMediaType()); + } else if (EmptyRepresentation.class.isAssignableFrom(target)) { + result = null; + } else if (File.class.isAssignableFrom(target)) { + if (source instanceof FileRepresentation) { + result = ((FileRepresentation) source).getFile(); + } else { + result = null; + } + } else if (Form.class.isAssignableFrom(target)) { + result = new Form(source); + } else if (InputStream.class.isAssignableFrom(target)) { + result = source.getStream(); + } else if (InputRepresentation.class.isAssignableFrom(target)) { + result = new InputRepresentation(source.getStream()); + } else if (Reader.class.isAssignableFrom(target)) { + result = source.getReader(); + } else if (ReaderRepresentation.class.isAssignableFrom(target)) { + result = new ReaderRepresentation(source.getReader()); + } else if (Serializable.class.isAssignableFrom(target) || target.isPrimitive()) { + if (source instanceof ObjectRepresentation) { + result = ((ObjectRepresentation) source).getObject(); + } else { + try { + result = new ObjectRepresentation<>(source).getObject(); + } catch (Exception e) { + IOException ioe = + new IOException("Unable to create the Object representation"); + ioe.initCause(e); + throw ioe; + } + } + } + } else if (source instanceof ObjectRepresentation) { + result = ((ObjectRepresentation) source).getObject(); + } - @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { - Representation result = null; + return (T) result; + } - if (source instanceof String) { - result = new StringRepresentation((String) source, - MediaType.getMostSpecific(target.getMediaType(), MediaType.TEXT_PLAIN)); - } else if (source instanceof File) { - result = new FileRepresentation((File) source, - MediaType.getMostSpecific(target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); - } else if (source instanceof Form) { - result = ((Form) source).getWebRepresentation(); - } else if (source instanceof InputStream) { - result = new InputRepresentation((InputStream) source, - MediaType.getMostSpecific(target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); - } else if (source instanceof Reader) { - result = new ReaderRepresentation((Reader) source, - MediaType.getMostSpecific(target.getMediaType(), MediaType.TEXT_PLAIN)); - } else if (source instanceof Representation) { - result = (Representation) source; - } else if (source instanceof Serializable) { - result = new ObjectRepresentation((Serializable) source, - MediaType.getMostSpecific(target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); - } + @Override + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { + Representation result = null; - return result; - } + if (source instanceof String) { + result = + new StringRepresentation( + (String) source, + MediaType.getMostSpecific(target.getMediaType(), MediaType.TEXT_PLAIN)); + } else if (source instanceof File) { + result = + new FileRepresentation( + (File) source, + MediaType.getMostSpecific( + target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); + } else if (source instanceof Form) { + result = ((Form) source).getWebRepresentation(); + } else if (source instanceof InputStream) { + result = + new InputRepresentation( + (InputStream) source, + MediaType.getMostSpecific( + target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); + } else if (source instanceof Reader) { + result = + new ReaderRepresentation( + (Reader) source, + MediaType.getMostSpecific(target.getMediaType(), MediaType.TEXT_PLAIN)); + } else if (source instanceof Representation) { + result = (Representation) source; + } else if (source instanceof Serializable) { + result = + new ObjectRepresentation<>( + (Serializable) source, + MediaType.getMostSpecific( + target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); + } - @Override - public void updatePreferences(List> preferences, Class entity) { - if (Form.class.isAssignableFrom(entity)) { - updatePreferences(preferences, MediaType.APPLICATION_WWW_FORM, 1.0F); - } else if (Serializable.class.isAssignableFrom(entity)) { - if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { - updatePreferences(preferences, MediaType.APPLICATION_JAVA_OBJECT, 1.0F); - } - if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { - updatePreferences(preferences, MediaType.APPLICATION_JAVA_OBJECT_XML, 1.0F); - } - } else if (String.class.isAssignableFrom(entity) || Reader.class.isAssignableFrom(entity)) { - updatePreferences(preferences, MediaType.TEXT_PLAIN, 1.0F); - updatePreferences(preferences, MediaType.TEXT_ALL, 0.5F); - } else if (InputStream.class.isAssignableFrom(entity)) { - updatePreferences(preferences, MediaType.APPLICATION_OCTET_STREAM, 1.0F); - updatePreferences(preferences, MediaType.APPLICATION_ALL, 0.5F); - } - } + return result; + } + @Override + public void updatePreferences(List> preferences, Class entity) { + if (Form.class.isAssignableFrom(entity)) { + updatePreferences(preferences, MediaType.APPLICATION_WWW_FORM, 1.0F); + } else if (Serializable.class.isAssignableFrom(entity)) { + if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { + updatePreferences(preferences, MediaType.APPLICATION_JAVA_OBJECT, 1.0F); + } + if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { + updatePreferences(preferences, MediaType.APPLICATION_JAVA_OBJECT_XML, 1.0F); + } + } else if (String.class.isAssignableFrom(entity) || Reader.class.isAssignableFrom(entity)) { + updatePreferences(preferences, MediaType.TEXT_PLAIN, 1.0F); + updatePreferences(preferences, MediaType.TEXT_ALL, 0.5F); + } else if (InputStream.class.isAssignableFrom(entity)) { + updatePreferences(preferences, MediaType.APPLICATION_OCTET_STREAM, 1.0F); + updatePreferences(preferences, MediaType.APPLICATION_ALL, 0.5F); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java index edd5db09d5..437f2103c7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.converter; +import java.io.IOException; +import java.util.List; import org.restlet.Request; import org.restlet.Response; import org.restlet.data.MediaType; @@ -22,149 +23,150 @@ import org.restlet.resource.Resource; import org.restlet.service.StatusService; -import java.io.IOException; -import java.util.List; - /** * Converter for the {@link StatusInfo} class. - * + * * @author Manuel Boillod */ public class StatusInfoHtmlConverter extends ConverterHelper { - /** Variant with media type application/xhtml+xml. */ - private static final VariantInfo VARIANT_APPLICATION_XHTML = new VariantInfo(MediaType.APPLICATION_XHTML); - - /** Variant with media type text/html. */ - private static final VariantInfo VARIANT_TEXT_HTML = new VariantInfo(MediaType.TEXT_HTML); - - @Override - public List> getObjectClasses(Variant source) { - List> result = null; - - if (isCompatible(source)) { - result = addObjectClass(result, StatusInfo.class); - } - - return result; - } - - /** - * Returns the status information to display in the default representation. By - * default it returns the status's reason phrase. - * - * @param status The status. - * @return The status information. - * @see StatusService#toRepresentation(Status, Request, Response) - */ - protected String getStatusLabel(StatusInfo status) { - return (status.getReasonPhrase() != null) ? status.getReasonPhrase() - : "No information available for this result status"; - } - - @Override - public List getVariants(Class source) throws IOException { - List result = null; - - if (source != null && StatusInfo.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_TEXT_HTML); - result = addVariant(result, VARIANT_APPLICATION_XHTML); - } - - return result; - } - - /** - * Indicates if the given variant is compatible with the media types supported - * by this converter. - * - * @param variant The variant. - * @return True if the given variant is compatible with the media types - * supported by this converter. - */ - protected boolean isCompatible(Variant variant) { - return (variant != null) - && (VARIANT_TEXT_HTML.isCompatible(variant) || VARIANT_APPLICATION_XHTML.isCompatible(variant)); - } - - @Override - public float score(Object source, Variant target, Resource resource) { - float result = -1.0F; - - if (source instanceof StatusInfo && isCompatible(target)) { - result = 1.0F; - } - - return result; - } - - @Override - public float score(Representation source, Class target, Resource resource) { - return -1.0F; - } - - /** - * Returns a representation for the given status.
- * In order to customize the default representation, this method can be - * overridden. - * - * @param status The status info to represent. - * @return The representation of the given status. - */ - protected Representation toHtml(StatusInfo status) { - final StringBuilder sb = new StringBuilder(); - sb.append("\n"); - sb.append("\n"); - sb.append(" Status page\n"); - sb.append("\n"); - sb.append("\n"); - - sb.append("

"); - sb.append(StringUtils.htmlEscape(getStatusLabel(status))); - sb.append("

\n"); - if (status.getDescription() != null) { - sb.append("

"); - sb.append(StringUtils.htmlEscape(status.getDescription())); - sb.append("

\n"); - } - - sb.append("

You can get technical details here.
\n"); - - if (status.getContactEmail() != null) { - sb.append("For further assistance, you can contact the administrator.
\n"); - } - - if (status.getHomeRef() != null) { - sb.append("Please continue your visit at our home page.\n"); - } - - sb.append("

\n"); - sb.append("\n"); - sb.append("\n"); - - return new StringRepresentation(sb.toString(), MediaType.TEXT_HTML); - } - - @Override - public T toObject(Representation source, Class target, Resource resource) throws IOException { - return null; - } - - @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { - Representation result = null; - - if (source != null && StatusInfo.class.isAssignableFrom(source.getClass())) { - StatusInfo si = (StatusInfo) source; - result = toHtml(si); - } - - return result; - } -} \ No newline at end of file + /** Variant with media type application/xhtml+xml. */ + private static final VariantInfo VARIANT_APPLICATION_XHTML = + new VariantInfo(MediaType.APPLICATION_XHTML); + + /** Variant with media type text/html. */ + private static final VariantInfo VARIANT_TEXT_HTML = new VariantInfo(MediaType.TEXT_HTML); + + @Override + public List> getObjectClasses(Variant source) { + List> result = null; + + if (isCompatible(source)) { + result = addObjectClass(result, StatusInfo.class); + } + + return result; + } + + /** + * Returns the status information to display in the default representation. By default, it + * returns the status's reason phrase. + * + * @param status The status. + * @return The status information. + * @see StatusService#toRepresentation(Status, Request, Response) + */ + protected String getStatusLabel(StatusInfo status) { + return (status.getReasonPhrase() != null) + ? status.getReasonPhrase() + : "No information available for this result status"; + } + + @Override + public List getVariants(Class source) throws IOException { + List result = null; + + if (source != null && StatusInfo.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_TEXT_HTML); + result = addVariant(result, VARIANT_APPLICATION_XHTML); + } + + return result; + } + + /** + * Indicates if the given variant is compatible with the media types supported by this + * converter. + * + * @param variant The variant. + * @return True if the given variant is compatible with the media types supported by this + * converter. + */ + protected boolean isCompatible(Variant variant) { + return (variant != null) + && (VARIANT_TEXT_HTML.isCompatible(variant) + || VARIANT_APPLICATION_XHTML.isCompatible(variant)); + } + + @Override + public float score(Object source, Variant target, Resource resource) { + float result = -1.0F; + + if (source instanceof StatusInfo && isCompatible(target)) { + result = 1.0F; + } + + return result; + } + + @Override + public float score(Representation source, Class target, Resource resource) { + return -1.0F; + } + + /** + * Returns a representation for the given status.
+ * To customize the default representation, this method can be overridden. + * + * @param status The status info to represent. + * @return The representation of the given status. + */ + protected Representation toHtml(StatusInfo status) { + final StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append(" Status page\n"); + sb.append("\n"); + sb.append("\n"); + + sb.append("

"); + sb.append(StringUtils.htmlEscape(getStatusLabel(status))); + sb.append("

\n"); + if (status.getDescription() != null) { + sb.append("

"); + sb.append(StringUtils.htmlEscape(status.getDescription())); + sb.append("

\n"); + } + + sb.append("

You can get technical details here.
\n"); + + if (status.getContactEmail() != null) { + sb.append("For further assistance, you can contact the administrator.
\n"); + } + + if (status.getHomeRef() != null) { + sb.append("Please continue your visit at our home page.\n"); + } + + sb.append("

\n"); + sb.append("\n"); + sb.append("\n"); + + return new StringRepresentation(sb.toString(), MediaType.TEXT_HTML); + } + + @Override + public T toObject(Representation source, Class target, Resource resource) + throws IOException { + return null; + } + + @Override + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { + Representation result = null; + + if (source != null && StatusInfo.class.isAssignableFrom(source.getClass())) { + StatusInfo si = (StatusInfo) source; + result = toHtml(si); + } + + return result; + } +} diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java index 0217b83c80..f4da652c2a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java @@ -1,49 +1,46 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.CacheDirective; -import org.restlet.data.Header; - import java.io.IOException; import java.util.Collection; +import org.restlet.data.CacheDirective; +import org.restlet.data.Header; /** * Cache directive header reader. - * + * * @author Jerome Louvel */ public class CacheDirectiveReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param collection The collection to update. - */ - public static void addValues(Header header, Collection collection) { - new CacheDirectiveReader(header.getValue()).addValues(collection); - } - - /** - * Constructor. - * - * @param header The header to read. - */ - public CacheDirectiveReader(String header) { - super(header); - } - - @Override - public CacheDirective readValue() throws IOException { - return readNamedValue(CacheDirective.class); - } - + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param collection The collection to update. + */ + public static void addValues(Header header, Collection collection) { + new CacheDirectiveReader(header.getValue()).addValues(collection); + } + + /** + * Constructor. + * + * @param header The header to read. + */ + public CacheDirectiveReader(String header) { + super(header); + } + + @Override + public CacheDirective readValue() throws IOException { + return readNamedValue(CacheDirective.class); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java index d6378ae256..5b48c5e585 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java @@ -1,39 +1,36 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.CacheDirective; - import java.util.List; +import org.restlet.data.CacheDirective; /** * Cache directive header writer. - * + * * @author Thierry Boileau */ public class CacheDirectiveWriter extends HeaderWriter { - /** - * Writes a list of cache directives with a comma separator. - * - * @param directives The list of cache directives. - * @return The formatted list of cache directives. - */ - public static String write(List directives) { - return new CacheDirectiveWriter().append(directives).toString(); - } - - @Override - public CacheDirectiveWriter append(CacheDirective directive) { - appendExtension(directive); - return this; - } + /** + * Writes a list of cache directives with a comma separator. + * + * @param directives The list of cache directives. + * @return The formatted list of cache directives. + */ + public static String write(List directives) { + return new CacheDirectiveWriter().append(directives).toString(); + } + @Override + public CacheDirectiveWriter append(CacheDirective directive) { + appendExtension(directive); + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java index 2d99dc67b1..fcf8fbd60c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java @@ -1,86 +1,85 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import static org.restlet.engine.header.HeaderUtils.isSpace; + +import java.io.IOException; import org.restlet.data.ChallengeRequest; import org.restlet.data.ChallengeScheme; import org.restlet.data.Parameter; -import java.io.IOException; - -import static org.restlet.engine.header.HeaderUtils.isSpace; - /** * Challenge request header reader. - * + * * @author Thierry Boileau */ public class ChallengeRequestReader extends HeaderReader { - /** - * Constructor. - * - * @param header The header to read. - */ - public ChallengeRequestReader(String header) { - super(header); - } + /** + * Constructor. + * + * @param header The header to read. + */ + public ChallengeRequestReader(String header) { + super(header); + } - @Override - public ChallengeRequest readValue() throws IOException { - ChallengeRequest result = null; + @Override + public ChallengeRequest readValue() throws IOException { + ChallengeRequest result = null; - // The challenge is that this header is a comma separated lst of - // challenges, and that each challenge is also a comma separated list, - // but of parameters. - skipSpaces(); - if (peek() != -1) { - String scheme = readToken(); - result = new ChallengeRequest(new ChallengeScheme("HTTP_" + scheme, scheme)); - skipSpaces(); + // The challenge is that this header is a comma separated lst of + // challenges, and that each challenge is also a comma-separated list, + // but of parameters. + skipSpaces(); + if (peek() != -1) { + String scheme = readToken(); + result = new ChallengeRequest(new ChallengeScheme("HTTP_" + scheme, scheme)); + skipSpaces(); - // Header writer that will reconstruct the raw value of a challenge. - HeaderWriter w = new HeaderWriter() { - @Override - public HeaderWriter append(Parameter value) { - appendExtension(value); - return this; - } - }; + // Header writer that will reconstruct the raw value of a challenge. + HeaderWriter w = + new HeaderWriter() { + @Override + public HeaderWriter append(Parameter value) { + appendExtension(value); + return this; + } + }; - boolean stop = false; - while (peek() != -1 && !stop) { - boolean sepSkipped = skipValueSeparator(); - // Record the start of the segment - mark(); - // Read a token and the next character. - readToken(); - int nextChar = read(); - reset(); - if (isSpace(nextChar)) { - // A new scheme has been discovered. - stop = true; - } else { - // The next segment is considered as a parameter - if (sepSkipped) { - // Add the skipped value separator. - w.appendValueSeparator(); - } - // Append the parameter - w.append(readParameter()); - } - } - result.setRawValue(w.toString()); - w.close(); - } + boolean stop = false; + while (peek() != -1 && !stop) { + boolean sepSkipped = skipValueSeparator(); + // Record the start of the segment + mark(); + // Read a token and the next character. + readToken(); + int nextChar = read(); + reset(); + if (isSpace(nextChar)) { + // A new scheme has been discovered. + stop = true; + } else { + // The next segment is considered as a parameter + if (sepSkipped) { + // Add the skipped value separator. + w.appendValueSeparator(); + } + // Append the parameter + w.append(readParameter()); + } + } + result.setRawValue(w.toString()); + w.close(); + } - return result; - } + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java index c35662b504..73d9c50d6f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; import org.restlet.data.ChallengeRequest; @@ -14,137 +13,134 @@ /** * Authentication challenge header writer. - * + * * @author Jerome Louvel */ public class ChallengeWriter extends HeaderWriter { - /** Indicates if the first challenge parameter is written. */ - private volatile boolean firstChallengeParameter; - - /** - * Constructor. - */ - public ChallengeWriter() { - this.firstChallengeParameter = true; - } - - @Override - public HeaderWriter append(ChallengeRequest value) { - return this; - } - - /** - * Appends a new challenge parameter, prefixed with a comma. The value is - * separated from the name by an '=' character. - * - * @param parameter The parameter. - * @return This writer. - */ - public ChallengeWriter appendChallengeParameter(Parameter parameter) { - return appendChallengeParameter(parameter.getName(), parameter.getValue()); - } - - /** - * Appends a new parameter, prefixed with a comma. - * - * @param name The parameter name. - * @return The current builder. - */ - public ChallengeWriter appendChallengeParameter(String name) { - appendChallengeParameterSeparator(); - appendToken(name); - return this; - } - - /** - * Appends a new parameter, prefixed with a comma. The value is separated from - * the name by an '=' character. - * - * @param name The parameter name. - * @param value The parameter value. - * @return This writer. - */ - public ChallengeWriter appendChallengeParameter(String name, String value) { - appendChallengeParameterSeparator(); - - if (name != null) { - appendToken(name); - } - - if (value != null) { - append('='); - appendToken(value); - } - - return this; - } - - /** - * Appends a comma as a separator if the first parameter has already been - * written. - * - * @return This writer. - */ - public ChallengeWriter appendChallengeParameterSeparator() { - if (isFirstChallengeParameter()) { - setFirstChallengeParameter(false); - } else { - append(", "); - } - - return this; - } - - /** - * Appends a new parameter, prefixed with a comma. The value is separated from - * the name by an '=' character. - * - * @param parameter The parameter. - * @return This writer. - */ - public ChallengeWriter appendQuotedChallengeParameter(Parameter parameter) { - return appendQuotedChallengeParameter(parameter.getName(), parameter.getValue()); - } - - /** - * Appends a new parameter, prefixed with a comma. The value is quoted and - * separated from the name by an '=' character. - * - * @param name The parameter name. - * @param value The parameter value to quote. - * @return This writer. - */ - public ChallengeWriter appendQuotedChallengeParameter(String name, String value) { - appendChallengeParameterSeparator(); - - if (name != null) { - appendToken(name); - } - - if (value != null) { - append('='); - appendQuotedString(value); - } - - return this; - } - - /** - * Indicates if the first comma-separated value is written. - * - * @return True if the first comma-separated value is written. - */ - public boolean isFirstChallengeParameter() { - return firstChallengeParameter; - } - - /** - * Indicates if the first comma-separated value is written. - * - * @param firstValue True if the first comma-separated value is written. - */ - public void setFirstChallengeParameter(boolean firstValue) { - this.firstChallengeParameter = firstValue; - } + /** Indicates if the first challenge parameter is written. */ + private volatile boolean firstChallengeParameter; + + /** Constructor. */ + public ChallengeWriter() { + this.firstChallengeParameter = true; + } + + @Override + public HeaderWriter append(ChallengeRequest value) { + return this; + } + + /** + * Appends a new challenge parameter, prefixed with a comma. The value is separated from the + * name by a '=' character. + * + * @param parameter The parameter. + * @return This writer. + */ + public ChallengeWriter appendChallengeParameter(Parameter parameter) { + return appendChallengeParameter(parameter.getName(), parameter.getValue()); + } + + /** + * Appends a new parameter, prefixed with a comma. + * + * @param name The parameter name. + * @return The current builder. + */ + public ChallengeWriter appendChallengeParameter(String name) { + appendChallengeParameterSeparator(); + appendToken(name); + return this; + } + + /** + * Appends a new parameter, prefixed with a comma. The value is separated from the name by a '=' + * character. + * + * @param name The parameter name. + * @param value The parameter value. + * @return This writer. + */ + public ChallengeWriter appendChallengeParameter(String name, String value) { + appendChallengeParameterSeparator(); + + if (name != null) { + appendToken(name); + } + + if (value != null) { + append('='); + appendToken(value); + } + + return this; + } + + /** + * Appends a comma as a separator if the first parameter has already been written. + * + * @return This writer. + */ + public ChallengeWriter appendChallengeParameterSeparator() { + if (isFirstChallengeParameter()) { + setFirstChallengeParameter(false); + } else { + append(", "); + } + + return this; + } + + /** + * Appends a new parameter, prefixed with a comma. The value is separated from the name by a '=' + * character. + * + * @param parameter The parameter. + * @return This writer. + */ + public ChallengeWriter appendQuotedChallengeParameter(Parameter parameter) { + return appendQuotedChallengeParameter(parameter.getName(), parameter.getValue()); + } + + /** + * Appends a new parameter, prefixed with a comma. The value is quoted and separated from the + * name by a '=' character. + * + * @param name The parameter name. + * @param value The parameter value to quote. + * @return This writer. + */ + public ChallengeWriter appendQuotedChallengeParameter(String name, String value) { + appendChallengeParameterSeparator(); + + if (name != null) { + appendToken(name); + } + + if (value != null) { + append('='); + appendQuotedString(value); + } + + return this; + } + + /** + * Indicates if the first comma-separated value is written. + * + * @return True if the first comma-separated value is written. + */ + public boolean isFirstChallengeParameter() { + return firstChallengeParameter; + } + + /** + * Indicates if the first comma-separated value is written. + * + * @param firstValue True if the first comma-separated value is written. + */ + public void setFirstChallengeParameter(boolean firstValue) { + this.firstChallengeParameter = firstValue; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java b/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java index 8d8737fec5..eee65d69c6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java @@ -1,160 +1,155 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.IOException; import org.restlet.data.CharacterSet; import org.restlet.data.MediaType; import org.restlet.data.Parameter; import org.restlet.representation.Representation; -import java.io.IOException; - /** - * Association of a media type, a character set and modifiers. - * + * Association of a media type, a character set, and modifiers. + * * @author Jerome Louvel */ public class ContentType { - /** - * Parses the given content type header and returns the character set. - * - * @param contentType The content type header to parse. - * @return The character set. - */ - public static CharacterSet readCharacterSet(String contentType) { - return new ContentType(contentType).getCharacterSet(); - } - - /** - * Parses the given content type header and returns the media type. - * - * @param contentType The content type header to parse. - * @return The media type. - */ - public static MediaType readMediaType(String contentType) { - return new ContentType(contentType).getMediaType(); - } - - /** - * Writes the HTTP "Content-Type" header. - * - * @param mediaType The representation media type. - * @param characterSet The representation character set. - * @return The HTTP "Content-Type" header. - */ - public static String writeHeader(MediaType mediaType, CharacterSet characterSet) { - StringBuilder result = new StringBuilder(mediaType.toString()); - - // Specify the character set parameter if required - // TODO I wonder if the given parameter "characterSet" overrides the mediaType's charset'? - /* - if ((mediaType.getParameters().getFirstValue("charset") == null) && (characterSet != null)) { - result = result + "; charset=" + characterSet.getName(); - } - */ - - for (Parameter param : mediaType.getParameters()) { + /** + * Parses the given content type header and returns the character set. + * + * @param contentType The content type header to parse. + * @return The character set. + */ + public static CharacterSet readCharacterSet(String contentType) { + return new ContentType(contentType).getCharacterSet(); + } + + /** + * Parses the given content type header and returns the media type. + * + * @param contentType The content type header to parse. + * @return The media type. + */ + public static MediaType readMediaType(String contentType) { + return new ContentType(contentType).getMediaType(); + } + + /** + * Writes the HTTP "Content-Type" header. + * + * @param mediaType The representation media type. + * @param characterSet The representation character set. + * @return The HTTP "Content-Type" header. + */ + public static String writeHeader(MediaType mediaType, CharacterSet characterSet) { + StringBuilder result = new StringBuilder(mediaType.toString()); + + // Specify the character set parameter if required + // TODO I wonder if the given parameter "characterSet" overrides the mediaType's charset'? + /* + if ((mediaType.getParameters().getFirstValue("charset") == null) && (characterSet != null)) { + result = result + "; charset=" + characterSet.getName(); + } + */ + + for (Parameter param : mediaType.getParameters()) { if (param == null) { - continue; - } + continue; + } if (characterSet != null && param.getName().equals("charset")) { - // TODO I wonder if the given parameter "characterSet" overrides the mediaType's charset'? - result.append("; ").append(param.getName()).append("=").append(characterSet); - } else { - result.append("; ").append(param.getName()).append("=").append(param.getValue()); - } + // TODO I wonder if the given parameter "characterSet" overrides the mediaType's + // charset'? + result.append("; ").append(param.getName()).append("=").append(characterSet); + } else { + result.append("; ").append(param.getName()).append("=").append(param.getValue()); + } + } + + return result.toString(); + } + + /** + * Writes the HTTP "Content-Type" header. + * + * @param representation The related representation. + * @return The HTTP "Content-Type" header. + */ + public static String writeHeader(Representation representation) { + return writeHeader(representation.getMediaType(), representation.getCharacterSet()); + } + + /** The content character set. */ + private volatile CharacterSet characterSet; + + /** The content media type. */ + private volatile MediaType mediaType; + + /** + * Constructor. + * + * @param mediaType The media type. + * @param characterSet The character set. + */ + public ContentType(MediaType mediaType, CharacterSet characterSet) { + this.mediaType = mediaType; + this.characterSet = characterSet; + } + + /** + * Constructor. + * + * @param representation The representation. + */ + public ContentType(Representation representation) { + this(representation.getMediaType(), representation.getCharacterSet()); + } + + /** + * Constructor. + * + * @param headerValue The "Content-type" header to parse. + */ + public ContentType(String headerValue) { + try { + ContentTypeReader ctr = new ContentTypeReader(headerValue); + ContentType ct = ctr.readValue(); + + if (ct != null) { + this.mediaType = ct.getMediaType(); + this.characterSet = ct.getCharacterSet(); + } + } catch (IOException ioe) { + throw new IllegalArgumentException("The Content Type could not be read.", ioe); } - - return result.toString(); - } - - /** - * Writes the HTTP "Content-Type" header. - * - * @param representation The related representation. - * @return The HTTP "Content-Type" header. - */ - public static String writeHeader(Representation representation) { - return writeHeader(representation.getMediaType(), representation.getCharacterSet()); - } - - /** - * The content character set. - */ - private volatile CharacterSet characterSet; - - /** - * The content media type. - */ - private volatile MediaType mediaType; - - /** - * Constructor. - * - * @param mediaType The media type. - * @param characterSet The character set. - */ - public ContentType(MediaType mediaType, CharacterSet characterSet) { - this.mediaType = mediaType; - this.characterSet = characterSet; - } - - /** - * Constructor. - * - * @param representation The representation. - */ - public ContentType(Representation representation) { - this(representation.getMediaType(), representation.getCharacterSet()); - } - - /** - * Constructor. - * - * @param headerValue The "Content-type" header to parse. - */ - public ContentType(String headerValue) { - try { - ContentTypeReader ctr = new ContentTypeReader(headerValue); - ContentType ct = ctr.readValue(); - - if (ct != null) { - this.mediaType = ct.getMediaType(); - this.characterSet = ct.getCharacterSet(); - } - } catch (IOException ioe) { - throw new IllegalArgumentException("The Content Type could not be read.", ioe); - } - } - - /** - * Returns the character set. - * - * @return The character set. - */ - public CharacterSet getCharacterSet() { - return this.characterSet; - } - - /** - * Returns the media type. - * - * @return The media type. - */ - public MediaType getMediaType() { - return this.mediaType; - } - - @Override - public String toString() { - return writeHeader(getMediaType(), getCharacterSet()); - } + } + + /** + * Returns the character set. + * + * @return The character set. + */ + public CharacterSet getCharacterSet() { + return this.characterSet; + } + + /** + * Returns the media type. + * + * @return The media type. + */ + public MediaType getMediaType() { + return this.mediaType; + } + + @Override + public String toString() { + return writeHeader(getMediaType(), getCharacterSet()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java index 35ce49f3d5..b3c55883a6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java @@ -1,203 +1,211 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.IOException; import org.restlet.data.CharacterSet; import org.restlet.data.MediaType; import org.restlet.data.Parameter; import org.restlet.util.Series; -import java.io.IOException; - /** * Content type header reader. - * + * * @author Jerome Louvel */ public class ContentTypeReader extends HeaderReader { - /** - * Constructor. - * - * @param header The header to read. - */ - public ContentTypeReader(String header) { - super(header); - } - - /** - * Creates a content type. - * - * @param mediaType The media type name. - * @param parameters The parameters parsed. - * @return The content type. - */ - private ContentType createContentType(StringBuilder mediaType, Series parameters) { - // Attempt to extract the character set - CharacterSet characterSet = null; - - if (parameters != null) { - String charSet = parameters.getFirstValue("charset"); - - if (charSet != null) { - parameters.removeAll("charset"); - characterSet = new CharacterSet(charSet); - } - - return new ContentType(new MediaType(mediaType.toString(), parameters), characterSet); - } - - return new ContentType(new MediaType(mediaType.toString()), null); - } - - @Override - public ContentType readValue() throws IOException { - ContentType result = null; - - boolean readingMediaType = true; - boolean readingParamName = false; - boolean readingParamValue = false; - - StringBuilder mediaTypeBuffer = new StringBuilder(); - StringBuilder paramNameBuffer = null; - StringBuilder paramValueBuffer = null; - - Series parameters = null; - String nextValue = readRawValue(); - int nextIndex = 0; - - if (nextValue != null) { - int nextChar = nextValue.charAt(nextIndex++); - - while (result == null) { - if (readingMediaType) { - if (nextChar == -1) { - if (mediaTypeBuffer.length() > 0) { - // End of metadata section - // No parameters detected - result = createContentType(mediaTypeBuffer, null); - paramNameBuffer = new StringBuilder(); - } else { - // Ignore empty metadata name - } - } else if (nextChar == ';') { - if (mediaTypeBuffer.length() > 0) { - // End of mediaType section - // Parameters detected - readingMediaType = false; - readingParamName = true; - paramNameBuffer = new StringBuilder(); - parameters = new Series(Parameter.class); - } else { - throw new IOException("Empty mediaType name detected."); - } - } else if (HeaderUtils.isSpace(nextChar)) { - // Ignore spaces - } else if (HeaderUtils.isText(nextChar)) { - mediaTypeBuffer.append((char) nextChar); - } else { - throw new IOException( - "The " + (char) nextChar + " character isn't allowed in a media type name."); - } - } else if (readingParamName) { - if (nextChar == '=') { - if (paramNameBuffer.length() > 0) { - // End of parameter name section - readingParamName = false; - readingParamValue = true; - paramValueBuffer = new StringBuilder(); - } else { - throw new IOException("Empty parameter name detected."); - } - } else if (nextChar == -1) { - if (paramNameBuffer.length() > 0) { - // End of parameters section - parameters.add(Parameter.create(paramNameBuffer, null)); - result = createContentType(mediaTypeBuffer, parameters); - } else if (paramNameBuffer.length() == 0) { - result = createContentType(mediaTypeBuffer, parameters); - } else { - throw new IOException("Empty parameter name detected."); - } - } else if (nextChar == ';') { - // End of parameter - parameters.add(Parameter.create(paramNameBuffer, null)); - paramNameBuffer = new StringBuilder(); - readingParamName = true; - readingParamValue = false; - } else if (HeaderUtils.isSpace(nextChar) && (paramNameBuffer.length() == 0)) { - // Ignore white spaces - } else if (HeaderUtils.isTokenChar(nextChar)) { - paramNameBuffer.append((char) nextChar); - } else { - throw new IOException("The \"" + (char) nextChar - + "\" character isn't allowed in a media type parameter name."); - } - } else if (readingParamValue) { - if (nextChar == -1) { - if (paramValueBuffer.length() > 0) { - // End of parameters section - parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); - result = createContentType(mediaTypeBuffer, parameters); - } else { - throw new IOException("Empty parameter value detected"); - } - } else if (nextChar == ';') { - // End of parameter - parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); - paramNameBuffer = new StringBuilder(); - readingParamName = true; - readingParamValue = false; - } else if ((nextChar == '"') && (paramValueBuffer.length() == 0)) { - // Parse the quoted string - boolean done = false; - boolean quotedPair = false; - - while ((!done) && (nextChar != -1)) { - nextChar = (nextIndex < nextValue.length()) ? nextValue.charAt(nextIndex++) : -1; - - if (quotedPair) { - // End of quoted pair (escape sequence) - if (HeaderUtils.isText(nextChar)) { - paramValueBuffer.append((char) nextChar); - quotedPair = false; - } else { - throw new IOException("Invalid character \"" + (char) nextChar - + "\" detected in quoted string. Please check your value"); - } - } else if (HeaderUtils.isDoubleQuote(nextChar)) { - // End of quoted string - done = true; - } else if (nextChar == '\\') { - // Begin of quoted pair (escape sequence) - quotedPair = true; - } else if (HeaderUtils.isText(nextChar)) { - paramValueBuffer.append((char) nextChar); - } else { - throw new IOException("Invalid character \"" + (char) nextChar - + "\" detected in quoted string. Please check your value"); - } - } - } else if (HeaderUtils.isTokenChar(nextChar)) { - paramValueBuffer.append((char) nextChar); - } else { - throw new IOException("The \"" + (char) nextChar - + "\" character isn't allowed in a media type parameter value."); - } - } - - nextChar = (nextIndex < nextValue.length()) ? nextValue.charAt(nextIndex++) : -1; - } - } - - return result; - } - + /** + * Constructor. + * + * @param header The header to read. + */ + public ContentTypeReader(String header) { + super(header); + } + + /** + * Creates a content type. + * + * @param mediaType The media type name. + * @param parameters The parameters parsed. + * @return The content type. + */ + private ContentType createContentType(StringBuilder mediaType, Series parameters) { + // Attempt to extract the character set + CharacterSet characterSet = null; + + if (parameters != null) { + String charSet = parameters.getFirstValue("charset"); + + if (charSet != null) { + parameters.removeAll("charset"); + characterSet = new CharacterSet(charSet); + } + + return new ContentType(new MediaType(mediaType.toString(), parameters), characterSet); + } + + return new ContentType(new MediaType(mediaType.toString()), null); + } + + @Override + public ContentType readValue() throws IOException { + ContentType result = null; + + boolean readingMediaType = true; + boolean readingParamName = false; + boolean readingParamValue = false; + + StringBuilder mediaTypeBuffer = new StringBuilder(); + StringBuilder paramNameBuffer = null; + StringBuilder paramValueBuffer = null; + + Series parameters = null; + String nextValue = readRawValue(); + int nextIndex = 0; + + if (nextValue != null) { + int nextChar = nextValue.charAt(nextIndex++); + + while (result == null) { + if (readingMediaType) { + if (nextChar == -1) { + if (mediaTypeBuffer.isEmpty()) { + // Ignore empty metadata name + } else { + // End of metadata section + // No parameters detected + result = createContentType(mediaTypeBuffer, null); + paramNameBuffer = new StringBuilder(); + } + } else if (nextChar == ';') { + if (mediaTypeBuffer.isEmpty()) { + throw new IOException("Empty mediaType name detected."); + } else { + // End of mediaType section + // Parameters detected + readingMediaType = false; + readingParamName = true; + paramNameBuffer = new StringBuilder(); + parameters = new Series<>(Parameter.class); + } + } else if (HeaderUtils.isSpace(nextChar)) { + // Ignore spaces + } else if (HeaderUtils.isText(nextChar)) { + mediaTypeBuffer.append((char) nextChar); + } else { + throw new IOException( + "The " + + (char) nextChar + + " character isn't allowed in a media type name."); + } + } else if (readingParamName) { + if (nextChar == '=') { + if (paramNameBuffer.isEmpty()) { + throw new IOException("Empty parameter name detected."); + } else { + // End of a parameter name section + readingParamName = false; + readingParamValue = true; + paramValueBuffer = new StringBuilder(); + } + } else if (nextChar == -1) { + if (paramNameBuffer.isEmpty()) { + result = createContentType(mediaTypeBuffer, parameters); + } else { + // End of the parameters section + parameters.add(Parameter.create(paramNameBuffer, null)); + result = createContentType(mediaTypeBuffer, parameters); + } + } else if (nextChar == ';') { + // End of parameter + parameters.add(Parameter.create(paramNameBuffer, null)); + paramNameBuffer = new StringBuilder(); + readingParamName = true; + readingParamValue = false; + } else if (HeaderUtils.isSpace(nextChar) && paramNameBuffer.isEmpty()) { + // Ignore white spaces + } else if (HeaderUtils.isTokenChar(nextChar)) { + paramNameBuffer.append((char) nextChar); + } else { + throw new IOException( + "The \"" + + (char) nextChar + + "\" character isn't allowed in a media type parameter name."); + } + } else if (readingParamValue) { + if (nextChar == -1) { + if (paramValueBuffer.isEmpty()) { + throw new IOException("Empty parameter value detected"); + } else { + // End of the parameters section + parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); + result = createContentType(mediaTypeBuffer, parameters); + } + } else if (nextChar == ';') { + // End of parameter + parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); + paramNameBuffer = new StringBuilder(); + readingParamName = true; + readingParamValue = false; + } else if ((nextChar == '"') && paramValueBuffer.isEmpty()) { + // Parse the quoted string + boolean done = false; + boolean quotedPair = false; + + while ((!done) && (nextChar != -1)) { + nextChar = + (nextIndex < nextValue.length()) + ? nextValue.charAt(nextIndex++) + : -1; + + if (quotedPair) { + // End of a quoted pair (escape sequence) + if (HeaderUtils.isText(nextChar)) { + paramValueBuffer.append((char) nextChar); + quotedPair = false; + } else { + throw new IOException( + "Invalid character \"" + + (char) nextChar + + "\" detected in quoted string. Please check your value"); + } + } else if (HeaderUtils.isDoubleQuote(nextChar)) { + // End of quoted string + done = true; + } else if (nextChar == '\\') { + // Begin of a quoted pair (escape sequence) + quotedPair = true; + } else if (HeaderUtils.isText(nextChar)) { + paramValueBuffer.append((char) nextChar); + } else { + throw new IOException( + "Invalid character \"" + + (char) nextChar + + "\" detected in quoted string. Please check your value"); + } + } + } else if (HeaderUtils.isTokenChar(nextChar)) { + paramValueBuffer.append((char) nextChar); + } else { + throw new IOException( + "The \"" + + (char) nextChar + + "\" character isn't allowed in a media type parameter value."); + } + } + + nextChar = (nextIndex < nextValue.length()) ? nextValue.charAt(nextIndex++) : -1; + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java index 02ef717ef5..2c9f10e5b9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java @@ -1,176 +1,172 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.IOException; import org.restlet.data.Cookie; import org.restlet.data.Parameter; -import java.io.IOException; - /** * Cookie header reader. - * + * * @author Jerome Louvel */ public class CookieReader extends HeaderReader { - private static final String NAME_DOMAIN = "$Domain"; - - private static final String NAME_PATH = "$Path"; - - private static final String NAME_VERSION = "$Version"; - - /** - * Parses the given String to a Cookie - * - * @param cookie - * @return the Cookie parsed from the String - * @throws IllegalArgumentException Thrown if the String can not be parsed as - * Cookie. - */ - public static Cookie read(String cookie) throws IllegalArgumentException { - CookieReader cr = new CookieReader(cookie); - - try { - return cr.readValue(); - } catch (IOException e) { - throw new IllegalArgumentException("Could not read the cookie", e); - } - } - - /** The global cookie specification version. */ - private volatile int globalVersion; - - /** - * Constructor. - * - * @param header The header to read. - */ - public CookieReader(String header) { - super(header); - this.globalVersion = -1; - } - - /** - * Reads the next pair as a parameter. - * - * @param readAttribute True, if the intention is to read only cookie attribute. - * @return The next pair as a parameter. - * @throws IOException - */ - private Parameter readPair(boolean readAttribute) throws IOException { - Parameter result = null; - - boolean readingName = true; - StringBuilder nameBuffer = new StringBuilder(); - StringBuilder valueBuffer = new StringBuilder(); - int nextChar = 0; - - while ((result == null) && (nextChar != -1)) { - nextChar = read(); - - if (readingName) { - if ((HeaderUtils.isSpace(nextChar)) && (nameBuffer.isEmpty())) { - // Skip spaces - } else if ((nextChar == -1) || (nextChar == ';') || (nextChar == ',')) { - if (!nameBuffer.isEmpty()) { - // End of pair with no value - result = Parameter.create(nameBuffer, null); - } else if (nextChar == -1) { - // Do nothing return null preference - } else { - throw new IOException("Empty cookie name detected. Please check your cookies"); - } - } else if (nextChar == '=') { - readingName = false; - } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { - if (readAttribute && nextChar != '$' && (nameBuffer.isEmpty())) { - unread(); - nextChar = -1; - } else { - nameBuffer.append((char) nextChar); - } - } else { - throw new IOException( - "Separator and control characters are not allowed within a token. Please check your cookie header"); - } - } else { - // reading value - if ((HeaderUtils.isSpace(nextChar)) && (valueBuffer.isEmpty())) { - // Skip spaces - } else if ((nextChar == -1) || (nextChar == ';')) { - // End of pair - result = Parameter.create(nameBuffer, valueBuffer); - } else if ((nextChar == '"') && (valueBuffer.isEmpty())) { - // Step back - unread(); - valueBuffer.append(readQuotedString()); - } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { - valueBuffer.append((char) nextChar); - } else { - throw new IOException( - "Separator and control characters are not allowed within a token. Please check your cookie header"); - } - } - } - - return result; - } - - @Override - public Cookie readValue() throws IOException { - Cookie result = null; - Parameter pair = readPair(false); - - if (pair != null && this.globalVersion == -1) { - // Cookies version not yet detected - if (NAME_VERSION.equalsIgnoreCase(pair.getName())) { - if (pair.getValue() != null) { - this.globalVersion = Integer.parseInt(pair.getValue()); - } else { - throw new IOException("Empty cookies version attribute detected. Please check your cookie header"); - } - } else { - // Set the default version for old Netscape cookies - this.globalVersion = 0; - } - } - - while ((pair != null) && (pair.getName().isEmpty() || pair.getName().charAt(0) == '$')) { - // Unexpected special attribute - // Silently ignore it as it may have been introduced by new - // specifications - pair = readPair(false); - } - - if (pair != null) { - // Set the cookie name and value - result = new Cookie(this.globalVersion, pair.getName(), pair.getValue()); - pair = readPair(true); - } - - while ((pair != null) && (pair.getName().isEmpty() || pair.getName().charAt(0) == '$')) { - if (pair.getName().equalsIgnoreCase(NAME_PATH)) { - result.setPath(pair.getValue()); - } else if (pair.getName().equalsIgnoreCase(NAME_DOMAIN)) { - result.setDomain(pair.getValue()); - } else { - // Unexpected special attribute - // Silently ignore it as it may have been introduced by new - // specifications - } - - pair = readPair(true); - } - - return result; - } - + private static final String NAME_DOMAIN = "$Domain"; + + private static final String NAME_PATH = "$Path"; + + private static final String NAME_VERSION = "$Version"; + + /** + * Parses the given String to a Cookie + * + * @param cookie The cookie a string to parse. + * @return the Cookie parsed from the String + * @throws IllegalArgumentException Thrown if the String cannot be parsed as Cookie. + */ + public static Cookie read(String cookie) throws IllegalArgumentException { + CookieReader cr = new CookieReader(cookie); + + try { + return cr.readValue(); + } catch (IOException e) { + throw new IllegalArgumentException("Could not read the cookie", e); + } + } + + /** The global cookie specification version. */ + private volatile int globalVersion; + + /** + * Constructor. + * + * @param header The header to read. + */ + public CookieReader(String header) { + super(header); + this.globalVersion = -1; + } + + /** + * Reads the next pair as a parameter. + * + * @param readAttribute True, if the intention is to only read the cookie attribute. + * @return The next pair as a parameter. + * @throws IOException + */ + private Parameter readPair(boolean readAttribute) throws IOException { + Parameter result = null; + + boolean readingName = true; + StringBuilder nameBuffer = new StringBuilder(); + StringBuilder valueBuffer = new StringBuilder(); + int nextChar = 0; + + while ((result == null) && (nextChar != -1)) { + nextChar = read(); + + if (readingName) { + if ((HeaderUtils.isSpace(nextChar)) && (nameBuffer.isEmpty())) { + // Skip spaces + } else if ((nextChar == -1) || (nextChar == ';') || (nextChar == ',')) { + if (!nameBuffer.isEmpty()) { + // End of a pair with no value + result = Parameter.create(nameBuffer, null); + } else if (nextChar == -1) { + // Do nothing and return a null preference + } else { + throw new IOException( + "Empty cookie name detected. Please check your cookies"); + } + } else if (nextChar == '=') { + readingName = false; + } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { + if (readAttribute && nextChar != '$' && (nameBuffer.isEmpty())) { + unread(); + nextChar = -1; + } else { + nameBuffer.append((char) nextChar); + } + } else { + throw new IOException( + "Separator and control characters are not allowed within a token. Please check your cookie header"); + } + } else { + // reading value + if (HeaderUtils.isSpace(nextChar) && valueBuffer.isEmpty()) { + // Skip spaces + } else if ((nextChar == -1) || (nextChar == ';')) { + // End of a pair + result = Parameter.create(nameBuffer, valueBuffer); + } else if ((nextChar == '"') && (valueBuffer.isEmpty())) { + // Step back + unread(); + valueBuffer.append(readQuotedString()); + } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { + valueBuffer.append((char) nextChar); + } else { + throw new IOException( + "Separator and control characters are not allowed within a token. Please check your cookie header"); + } + } + } + + return result; + } + + @Override + public Cookie readValue() throws IOException { + Cookie result = null; + Parameter pair = readPair(false); + + if (pair != null && this.globalVersion == -1) { + // Cookies version not yet detected + if (NAME_VERSION.equalsIgnoreCase(pair.getName())) { + if (pair.getValue() != null) { + this.globalVersion = Integer.parseInt(pair.getValue()); + } else { + throw new IOException( + "Empty cookies version attribute detected. Please check your cookie header"); + } + } else { + // Set the default version for old Netscape cookies + this.globalVersion = 0; + } + } + + while ((pair != null) && (pair.getName().isEmpty() || pair.getName().charAt(0) == '$')) { + // Unexpected special attribute + // Silently ignore it as it may have been introduced by new specifications + pair = readPair(false); + } + + if (pair != null) { + // Set the cookie name and value + result = new Cookie(this.globalVersion, pair.getName(), pair.getValue()); + pair = readPair(true); + } + + while ((pair != null) && (pair.getName().isEmpty() || pair.getName().charAt(0) == '$')) { + if (pair.getName().equalsIgnoreCase(NAME_PATH)) { + result.setPath(pair.getValue()); + } else if (pair.getName().equalsIgnoreCase(NAME_DOMAIN)) { + result.setDomain(pair.getValue()); + } else { + // Unexpected special attribute + // Silently ignore it as it may have been introduced by new specifications + } + + pair = readPair(true); + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java index ba363166e2..f8797a83df 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java @@ -1,231 +1,236 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import static org.restlet.engine.util.DateUtils.FORMAT_ASC_TIME; +import static org.restlet.engine.util.DateUtils.FORMAT_RFC_1036; +import static org.restlet.engine.util.DateUtils.FORMAT_RFC_1123; + +import java.io.IOException; +import java.util.Date; import org.restlet.Context; import org.restlet.data.CookieSetting; import org.restlet.data.Parameter; import org.restlet.engine.util.DateUtils; import org.restlet.engine.util.StringUtils; -import java.io.IOException; -import java.util.Date; - -import static org.restlet.engine.util.DateUtils.*; - /** * Cookie setting header reader. - * + * * @author Jerome Louvel */ public class CookieSettingReader extends HeaderReader { - private static final String NAME_SET_ACCESS_RESTRICTED = "httpOnly"; - - private static final String NAME_SET_COMMENT = "comment"; - - private static final String NAME_SET_COMMENT_URL = "commentURL"; - - private static final String NAME_SET_DISCARD = "discard"; - - private static final String NAME_SET_DOMAIN = "domain"; - - private static final String NAME_SET_EXPIRES = "expires"; - - private static final String NAME_SET_MAX_AGE = "max-age"; - - private static final String NAME_SET_PATH = "path"; - - private static final String NAME_SET_PORT = "port"; - - private static final String NAME_SET_SECURE = "secure"; - - private static final String NAME_SET_VERSION = "version"; - - /** - * Parses the given String to a CookieSetting - * - * @param cookieSetting - * @return the CookieSetting parsed from the String - * @throws IllegalArgumentException Thrown if the String cannot be parsed as - * CookieSetting. - */ - public static CookieSetting read(String cookieSetting) throws IllegalArgumentException { - CookieSettingReader cr = new CookieSettingReader(cookieSetting); - - try { - return cr.readValue(); - } catch (IOException e) { - throw new IllegalArgumentException("Could not read the cookie setting", e); - } - } - - /** The cached pair. Used by the readPair() method. */ - private volatile Parameter cachedPair; - - /** The global cookie specification version. */ - private volatile int globalVersion; - - /** - * Constructor. - * - * @param header The header to read. - */ - public CookieSettingReader(String header) { - super(header); - this.cachedPair = null; - this.globalVersion = -1; - } - - /** - * Reads the next pair as a parameter. - * - * @return The next pair as a parameter. - * @throws IOException - */ - private Parameter readPair() throws IOException { - if (this.cachedPair != null) { - Parameter pair = this.cachedPair; - this.cachedPair = null; - return pair; - } - - Parameter result = null; - - boolean readingName = true; - StringBuilder nameBuffer = new StringBuilder(); - StringBuilder valueBuffer = new StringBuilder(); - int nextChar = 0; - - while ((result == null) && (nextChar != -1)) { - nextChar = read(); - - if (readingName) { - if ((HeaderUtils.isSpace(nextChar)) && (nameBuffer.isEmpty())) { - // Skip spaces - } else if ((nextChar == -1) || (nextChar == ';') || (nextChar == ',')) { - if (!nameBuffer.isEmpty()) { - // End of pair with no value - result = Parameter.create(nameBuffer, null); - } else if (nextChar == -1) { - // Do nothing return null preference - } else { - throw new IOException("Empty cookie name detected. Please check your cookies"); - } - } else if (nextChar == '=') { - readingName = false; - } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { - nameBuffer.append((char) nextChar); - } else { - throw new IOException( - "Separator and control characters are not allowed within a token. Please check your cookie header"); - } - } else { - // reading value - if ((HeaderUtils.isSpace(nextChar)) && (valueBuffer.isEmpty())) { - // Skip spaces - } else if ((nextChar == -1) || (nextChar == ';')) { - // End of pair - result = Parameter.create(nameBuffer, valueBuffer); - } else if ((nextChar == '"') && (valueBuffer.isEmpty())) { - // Step back - unread(); - valueBuffer.append(readQuotedString()); - } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { - valueBuffer.append((char) nextChar); - } else { - throw new IOException( - "Separator and control characters are not allowed within a token. Please check your cookie header"); - } - } - } - - return result; - } - - @Override - public CookieSetting readValue() throws IOException { - Parameter pair = null; - - // Unexpected special attributes - // Silently ignore it as it may have been introduced by new specifications - do { - if ((pair = readPair()) == null) { - return null; - } - } while (pair.getName().charAt(0) == '$'); - - // Set the cookie name and value - CookieSetting result = new CookieSetting(pair.getName(), pair.getValue()); - - while ((pair = readPair()) != null) { - if (pair.getName().equalsIgnoreCase(NAME_SET_PATH)) { - result.setPath(pair.getValue()); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_DOMAIN)) { - result.setDomain(pair.getValue()); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_COMMENT)) { - result.setComment(pair.getValue()); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_COMMENT_URL)) { - // No yet supported - } else if (pair.getName().equalsIgnoreCase(NAME_SET_DISCARD)) { - result.setMaxAge(-1); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_EXPIRES)) { - Date expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1036); - - if (expires == null) { - expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1123); - } - - if (expires == null) { - expires = DateUtils.parse(pair.getValue(), FORMAT_ASC_TIME); - } - - if (expires != null) { - final Date current = new Date(System.currentTimeMillis()); - if (DateUtils.after(current, expires)) { - result.setMaxAge((int) ((expires.getTime() - current.getTime()) / 1000)); - } else { - result.setMaxAge(0); - } - } else { - // Ignore the expires header - Context.getCurrentLogger().warning( - "Ignoring cookie setting expiration date. Unable to parse the date: " + pair.getValue()); - } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_MAX_AGE)) { - try { - result.setMaxAge(Integer.parseInt(pair.getValue())); - } catch (NumberFormatException numberFormatException) { - result.setMaxAge(Integer.MAX_VALUE); - Context.getCurrentLogger().warning("Unable to parse the cookie setting max-age value \"" - + pair.getValue() + "\", used Integer.MAX_VALUE instead: " + Integer.MAX_VALUE); - } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_PORT)) { - // No yet supported - } else if (pair.getName().equalsIgnoreCase(NAME_SET_SECURE)) { - if (StringUtils.isNullOrEmpty(pair.getValue())) { - result.setSecure(true); - } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_ACCESS_RESTRICTED)) { - if (StringUtils.isNullOrEmpty(pair.getValue())) { - result.setAccessRestricted(true); - } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_VERSION)) { - result.setVersion(Integer.parseInt(pair.getValue())); - } else { - // Unexpected special attribute - // Silently ignore it as it may have been introduced by new specifications - } - } - - return result; - } - + private static final String NAME_SET_ACCESS_RESTRICTED = "httpOnly"; + + private static final String NAME_SET_COMMENT = "comment"; + + private static final String NAME_SET_COMMENT_URL = "commentURL"; + + private static final String NAME_SET_DISCARD = "discard"; + + private static final String NAME_SET_DOMAIN = "domain"; + + private static final String NAME_SET_EXPIRES = "expires"; + + private static final String NAME_SET_MAX_AGE = "max-age"; + + private static final String NAME_SET_PATH = "path"; + + private static final String NAME_SET_PORT = "port"; + + private static final String NAME_SET_SECURE = "secure"; + + private static final String NAME_SET_VERSION = "version"; + + /** + * Parses the given String to a CookieSetting + * + * @param cookieSetting + * @return the CookieSetting parsed from the String + * @throws IllegalArgumentException Thrown if the String cannot be parsed as CookieSetting. + */ + public static CookieSetting read(String cookieSetting) throws IllegalArgumentException { + CookieSettingReader cr = new CookieSettingReader(cookieSetting); + + try { + return cr.readValue(); + } catch (IOException e) { + throw new IllegalArgumentException("Could not read the cookie setting", e); + } + } + + /** The cached pair. Used by the readPair() method. */ + private volatile Parameter cachedPair; + + /** The global cookie specification version. */ + private volatile int globalVersion; + + /** + * Constructor. + * + * @param header The header to read. + */ + public CookieSettingReader(String header) { + super(header); + this.cachedPair = null; + this.globalVersion = -1; + } + + /** + * Reads the next pair as a parameter. + * + * @return The next pair as a parameter. + * @throws IOException + */ + private Parameter readPair() throws IOException { + if (this.cachedPair != null) { + Parameter pair = this.cachedPair; + this.cachedPair = null; + return pair; + } + + Parameter result = null; + + boolean readingName = true; + StringBuilder nameBuffer = new StringBuilder(); + StringBuilder valueBuffer = new StringBuilder(); + int nextChar = 0; + + while ((result == null) && (nextChar != -1)) { + nextChar = read(); + + if (readingName) { + if ((HeaderUtils.isSpace(nextChar)) && (nameBuffer.isEmpty())) { + // Skip spaces + } else if ((nextChar == -1) || (nextChar == ';') || (nextChar == ',')) { + if (!nameBuffer.isEmpty()) { + // End of a pair with no value + result = Parameter.create(nameBuffer, null); + } else if (nextChar == -1) { + // Do nothing and return a null preference + } else { + throw new IOException( + "Empty cookie name detected. Please check your cookies"); + } + } else if (nextChar == '=') { + readingName = false; + } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { + nameBuffer.append((char) nextChar); + } else { + throw new IOException( + "Separator and control characters are not allowed within a token. Please check your cookie header"); + } + } else { + // reading value + if ((HeaderUtils.isSpace(nextChar)) && (valueBuffer.isEmpty())) { + // Skip spaces + } else if ((nextChar == -1) || (nextChar == ';')) { + // End of pair + result = Parameter.create(nameBuffer, valueBuffer); + } else if ((nextChar == '"') && (valueBuffer.isEmpty())) { + // Step back + unread(); + valueBuffer.append(readQuotedString()); + } else if (HeaderUtils.isTokenChar(nextChar) || (this.globalVersion < 1)) { + valueBuffer.append((char) nextChar); + } else { + throw new IOException( + "Separator and control characters are not allowed within a token. Please check your cookie header"); + } + } + } + + return result; + } + + @Override + public CookieSetting readValue() throws IOException { + Parameter pair = null; + + // Unexpected special attributes + // Silently ignore it as it may have been introduced by new specifications + do { + if ((pair = readPair()) == null) { + return null; + } + } while (pair.getName().charAt(0) == '$'); + + // Set the cookie name and value + CookieSetting result = new CookieSetting(pair.getName(), pair.getValue()); + + while ((pair = readPair()) != null) { + if (pair.getName().equalsIgnoreCase(NAME_SET_PATH)) { + result.setPath(pair.getValue()); + } else if (pair.getName().equalsIgnoreCase(NAME_SET_DOMAIN)) { + result.setDomain(pair.getValue()); + } else if (pair.getName().equalsIgnoreCase(NAME_SET_COMMENT)) { + result.setComment(pair.getValue()); + } else if (pair.getName().equalsIgnoreCase(NAME_SET_COMMENT_URL)) { + // No yet supported + } else if (pair.getName().equalsIgnoreCase(NAME_SET_DISCARD)) { + result.setMaxAge(-1); + } else if (pair.getName().equalsIgnoreCase(NAME_SET_EXPIRES)) { + Date expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1036); + + if (expires == null) { + expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1123); + } + + if (expires == null) { + expires = DateUtils.parse(pair.getValue(), FORMAT_ASC_TIME); + } + + if (expires != null) { + final Date current = new Date(System.currentTimeMillis()); + if (DateUtils.after(current, expires)) { + result.setMaxAge((int) ((expires.getTime() - current.getTime()) / 1000)); + } else { + result.setMaxAge(0); + } + } else { + // Ignore the expires header + Context.getCurrentLogger() + .warning( + "Ignoring cookie setting expiration date. Unable to parse the date: " + + pair.getValue()); + } + } else if (pair.getName().equalsIgnoreCase(NAME_SET_MAX_AGE)) { + try { + result.setMaxAge(Integer.parseInt(pair.getValue())); + } catch (NumberFormatException numberFormatException) { + result.setMaxAge(Integer.MAX_VALUE); + Context.getCurrentLogger() + .warning( + "Unable to parse the cookie setting max-age value \"" + + pair.getValue() + + "\", used Integer.MAX_VALUE instead: " + + Integer.MAX_VALUE); + } + } else if (pair.getName().equalsIgnoreCase(NAME_SET_PORT)) { + // No yet supported + } else if (pair.getName().equalsIgnoreCase(NAME_SET_SECURE)) { + if (StringUtils.isNullOrEmpty(pair.getValue())) { + result.setSecure(true); + } + } else if (pair.getName().equalsIgnoreCase(NAME_SET_ACCESS_RESTRICTED)) { + if (StringUtils.isNullOrEmpty(pair.getValue())) { + result.setAccessRestricted(true); + } + } else if (pair.getName().equalsIgnoreCase(NAME_SET_VERSION)) { + result.setVersion(Integer.parseInt(pair.getValue())); + } else { + // Unexpected special attribute + // Silently ignore it as it may have been introduced by new specifications + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java index 0b11bb34ac..19c87f19ab 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java @@ -1,154 +1,151 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.CookieSetting; -import org.restlet.engine.util.DateUtils; - import java.util.Date; import java.util.List; +import org.restlet.data.CookieSetting; +import org.restlet.engine.util.DateUtils; /** * Cookie setting header writer. - * + * * @author Jerome Louvel */ public class CookieSettingWriter extends HeaderWriter { - /** - * Writes a cookie setting. - * - * @param cookieSetting The cookie setting to format. - * @return The formatted cookie setting. - */ - public static String write(CookieSetting cookieSetting) { - return new CookieSettingWriter().append(cookieSetting).toString(); - } - - /** - * Writes a list of cookie settings. - * - * @param cookieSettings The cookie settings to write. - * @return The formatted cookie setting. - */ - public static String write(List cookieSettings) { - return new CookieSettingWriter().append(cookieSettings).toString(); - } - - @Override - public CookieSettingWriter append(CookieSetting cookieSetting) throws IllegalArgumentException { - String name = cookieSetting.getName(); - String value = cookieSetting.getValue(); - int version = cookieSetting.getVersion(); - - if ((name == null) || (name.isEmpty())) { - throw new IllegalArgumentException("Can't write cookie. Invalid name detected"); - } - - append(name).append('='); - - // Append the value - if ((value != null) && (!value.isEmpty())) { - appendValue(value, version); - } - - // Append the version - if (version > 0) { - append("; Version="); - appendValue(Integer.toString(version), version); - } - - // Append the path - String path = cookieSetting.getPath(); - - if ((path != null) && (!path.isEmpty())) { - append("; Path="); - - if (version == 0) { - append(path); - } else { - appendQuotedString(path); - } - } - - // Append the expiration date - int maxAge = cookieSetting.getMaxAge(); - - if (maxAge >= 0) { - if (version == 0) { - long currentTime = System.currentTimeMillis(); - long maxTime = (maxAge * 1000L); - long expiresTime = currentTime + maxTime; - Date expires = new Date(expiresTime); - - append("; Expires="); - appendValue(DateUtils.format(expires, DateUtils.FORMAT_RFC_1123.get(0)), version); - } else { - append("; Max-Age="); - appendValue(Integer.toString(cookieSetting.getMaxAge()), version); - } - } else if ((maxAge == -1) && (version > 0)) { - // Discard the cookie at the end of the user's session (RFC - // 2965) - append("; Discard"); - } else { - // NetScape cookies automatically expire at the end of the - // user's session - } - - // Append the domain - String domain = cookieSetting.getDomain(); - - if ((domain != null) && (!domain.isEmpty())) { - append("; Domain="); - appendValue(domain.toLowerCase(), version); - } - - // Append the secure flag - if (cookieSetting.isSecure()) { - append("; Secure"); - } - - // Append the secure flag - if (cookieSetting.isAccessRestricted()) { - append("; HttpOnly"); - } - - // Append the comment - if (version > 0) { - String comment = cookieSetting.getComment(); - - if ((comment != null) && (!comment.isEmpty())) { - append("; Comment="); - appendValue(comment, version); - } - } - - return this; - } - - /** - * Appends a source string as an HTTP comment. - * - * @param value The source string to format. - * @param version The cookie version. - * @return This writer. - */ - public CookieSettingWriter appendValue(String value, int version) { - if (version == 0) { - append(value); - } else { - appendQuotedString(value); - } - - return this; - } - + /** + * Writes a cookie setting. + * + * @param cookieSetting The cookie setting to format. + * @return The formatted cookie setting. + */ + public static String write(CookieSetting cookieSetting) { + return new CookieSettingWriter().append(cookieSetting).toString(); + } + + /** + * Writes a list of cookie settings. + * + * @param cookieSettings The cookie settings to write. + * @return The formatted cookie setting. + */ + public static String write(List cookieSettings) { + return new CookieSettingWriter().append(cookieSettings).toString(); + } + + @Override + public CookieSettingWriter append(CookieSetting cookieSetting) throws IllegalArgumentException { + String name = cookieSetting.getName(); + String value = cookieSetting.getValue(); + int version = cookieSetting.getVersion(); + + if ((name == null) || (name.isEmpty())) { + throw new IllegalArgumentException("Can't write cookie. Invalid name detected"); + } + + append(name).append('='); + + // Append the value + if ((value != null) && (!value.isEmpty())) { + appendValue(value, version); + } + + // Append the version + if (version > 0) { + append("; Version="); + appendValue(Integer.toString(version), version); + } + + // Append the path + String path = cookieSetting.getPath(); + + if ((path != null) && (!path.isEmpty())) { + append("; Path="); + + if (version == 0) { + append(path); + } else { + appendQuotedString(path); + } + } + + // Append the expiration date + int maxAge = cookieSetting.getMaxAge(); + + if (maxAge >= 0) { + if (version == 0) { + long currentTime = System.currentTimeMillis(); + long maxTime = (maxAge * 1000L); + long expiresTime = currentTime + maxTime; + Date expires = new Date(expiresTime); + + append("; Expires="); + appendValue(DateUtils.format(expires, DateUtils.FORMAT_RFC_1123.get(0)), version); + } else { + append("; Max-Age="); + appendValue(Integer.toString(cookieSetting.getMaxAge()), version); + } + } else if ((maxAge == -1) && (version > 0)) { + // Discard the cookie at the end of the user's session (RFC + // 2965) + append("; Discard"); + } else { + // NetScape cookies automatically expire at the end of the + // user's session + } + + // Append the domain + String domain = cookieSetting.getDomain(); + + if ((domain != null) && (!domain.isEmpty())) { + append("; Domain="); + appendValue(domain.toLowerCase(), version); + } + + // Append the secure flag + if (cookieSetting.isSecure()) { + append("; Secure"); + } + + // Append the secure flag + if (cookieSetting.isAccessRestricted()) { + append("; HttpOnly"); + } + + // Append the comment + if (version > 0) { + String comment = cookieSetting.getComment(); + + if ((comment != null) && (!comment.isEmpty())) { + append("; Comment="); + appendValue(comment, version); + } + } + + return this; + } + + /** + * Appends a source string as an HTTP comment. + * + * @param value The source string to format. + * @param version The cookie version. + * @return This writer. + */ + public CookieSettingWriter appendValue(String value, int version) { + if (version == 0) { + append(value); + } else { + appendQuotedString(value); + } + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java index e812e42ba7..6ad145d210 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java @@ -1,148 +1,144 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Cookie; - -import java.util.Iterator; import java.util.List; import java.util.Map; +import org.restlet.data.Cookie; /** * Cookie header writer. - * + * * @author Jerome Louvel */ public class CookieWriter extends HeaderWriter { - /** - * Gets the cookies whose name is a key in the given map. If a matching cookie - * is found, its value is put in the map. - * - * @param source The source list of cookies. - * @param destination The cookies map controlling the reading. - */ - public static void getCookies(List source, Map destination) { - Cookie cookie; - - for (final Iterator iter = source.iterator(); iter.hasNext();) { - cookie = iter.next(); - - if (destination.containsKey(cookie.getName())) { - destination.put(cookie.getName(), cookie); - } - } - } - - /** - * Writes a cookie. - * - * @param cookie The cookie to format. - * @return The formatted cookie. - * @throws IllegalArgumentException If the Cookie contains illegal values. - */ - public static String write(Cookie cookie) throws IllegalArgumentException { - return new CookieWriter().append(cookie).toString(); - } - - /** - * Writes a cookie. - * - * @param cookies The cookies to format. - * @return The formatted cookie. - */ - public static String write(List cookies) { - return new CookieWriter().append(cookies).toString(); - } - - @Override - public CookieWriter append(Cookie cookie) throws IllegalArgumentException { - String name = cookie.getName(); - String value = cookie.getValue(); - int version = cookie.getVersion(); - - if ((name == null) || (name.isEmpty())) { - throw new IllegalArgumentException("Can't write cookie. Invalid name detected"); - } - - appendValue(name, 0).append('='); - - // Append the value - if ((value != null) && (!value.isEmpty())) { - appendValue(value, version); - } - - if (version > 0) { - // Append the path - String path = cookie.getPath(); - - if ((path != null) && (!path.isEmpty())) { - append("; $Path="); - appendQuotedString(path); - } - - // Append the domain - String domain = cookie.getDomain(); - - if ((domain != null) && (!domain.isEmpty())) { - append("; $Domain="); - appendQuotedString(domain); - } - } - - return this; - } - - /** - * Appends a list of cookies as an HTTP header. - * - * @param cookies The list of cookies to format. - * @return This writer. - */ - public CookieWriter append(List cookies) { - if ((cookies != null) && !cookies.isEmpty()) { - Cookie cookie; - - for (int i = 0; i < cookies.size(); i++) { - cookie = cookies.get(i); - - if (i == 0) { - if (cookie.getVersion() > 0) { - append("$Version=\"").append(cookie.getVersion()).append("\"; "); - } - } else { - append("; "); - } - - append(cookie); - } - } - - return this; - } - - /** - * Appends a source string as an HTTP comment. - * - * @param value The source string to format. - * @param version The cookie version. - * @return This writer. - */ - public CookieWriter appendValue(String value, int version) { - if (version == 0) { - append(value); - } else { - appendQuotedString(value); - } - - return this; - } - + /** + * Gets the cookies whose name is a key in the given map. If a matching cookie is found, its + * value is put on the map. + * + * @param source The source list of cookies. + * @param destination The cookies map controlling the reading. + */ + public static void getCookies(List source, Map destination) { + Cookie cookie; + + for (final Cookie value : source) { + cookie = value; + + if (destination.containsKey(cookie.getName())) { + destination.put(cookie.getName(), cookie); + } + } + } + + /** + * Writes a cookie. + * + * @param cookie The cookie to format. + * @return The formatted cookie. + * @throws IllegalArgumentException If the Cookie contains illegal values. + */ + public static String write(Cookie cookie) throws IllegalArgumentException { + return new CookieWriter().append(cookie).toString(); + } + + /** + * Writes a cookie. + * + * @param cookies The cookies to format. + * @return The formatted cookie. + */ + public static String write(List cookies) { + return new CookieWriter().append(cookies).toString(); + } + + @Override + public CookieWriter append(Cookie cookie) throws IllegalArgumentException { + String name = cookie.getName(); + String value = cookie.getValue(); + int version = cookie.getVersion(); + + if ((name == null) || (name.isEmpty())) { + throw new IllegalArgumentException("Can't write cookie. Invalid name detected"); + } + + appendValue(name, 0).append('='); + + // Append the value + if ((value != null) && (!value.isEmpty())) { + appendValue(value, version); + } + + if (version > 0) { + // Append the path + String path = cookie.getPath(); + + if ((path != null) && (!path.isEmpty())) { + append("; $Path="); + appendQuotedString(path); + } + + // Append the domain + String domain = cookie.getDomain(); + + if ((domain != null) && (!domain.isEmpty())) { + append("; $Domain="); + appendQuotedString(domain); + } + } + + return this; + } + + /** + * Appends a list of cookies as an HTTP header. + * + * @param cookies The list of cookies to format. + * @return This writer. + */ + public CookieWriter append(List cookies) { + if ((cookies != null) && !cookies.isEmpty()) { + Cookie cookie; + + for (int i = 0; i < cookies.size(); i++) { + cookie = cookies.get(i); + + if (i == 0) { + if (cookie.getVersion() > 0) { + append("$Version=\"").append(cookie.getVersion()).append("\"; "); + } + } else { + append("; "); + } + + append(cookie); + } + } + + return this; + } + + /** + * Appends a source string as an HTTP comment. + * + * @param value The source string to format. + * @param version The cookie version. + * @return This writer. + */ + public CookieWriter appendValue(String value, int version) { + if (version == 0) { + append(value); + } else { + appendQuotedString(value); + } + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java index f6467c4527..fd265ffb6b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java @@ -1,48 +1,45 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.engine.util.DateUtils; - import java.util.Date; +import org.restlet.engine.util.DateUtils; /** * Date header writer. - * + * * @author Jerome Louvel */ public class DateWriter { - /** - * Writes a date header. - * - * @param date The date to write. - * @return The formatted date. - */ - public static String write(Date date) { - return write(date, false); - } - - /** - * Writes a date header. - * - * @param date The date to write. - * @param cookie Indicates if the date should be in the cookie format. - * @return The formatted date. - */ - public static String write(Date date, boolean cookie) { - if (cookie) { - return DateUtils.format(date, DateUtils.FORMAT_RFC_1036.get(0)); - } - - return DateUtils.format(date); - } - + /** + * Writes a date header. + * + * @param date The date to write. + * @return The formatted date. + */ + public static String write(Date date) { + return write(date, false); + } + + /** + * Writes a date header. + * + * @param date The date to write. + * @param cookie Indicates if the date should be in the cookie format. + * @return The formatted date. + */ + public static String write(Date date, boolean cookie) { + if (cookie) { + return DateUtils.format(date, DateUtils.FORMAT_RFC_1036.get(0)); + } + + return DateUtils.format(date); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java b/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java index fb5e64f57d..5fb19c8427 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java @@ -1,71 +1,68 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Dimension; -import org.restlet.data.Header; - import java.io.IOException; import java.util.Collection; +import org.restlet.data.Dimension; +import org.restlet.data.Header; /** * Dimension header reader. - * + * * @author Jerome Louvel */ public class DimensionReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param collection The collection to update. - */ - public static void addValues(Header header, Collection collection) { - new DimensionReader(header.getValue()).addValues(collection); - } - - /** - * Constructor. - * - * @param header The header to read. - */ - public DimensionReader(String header) { - super(header); - } + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param collection The collection to update. + */ + public static void addValues(Header header, Collection collection) { + new DimensionReader(header.getValue()).addValues(collection); + } - @Override - public Dimension readValue() throws IOException { - Dimension result = null; - String value = readRawValue(); + /** + * Constructor. + * + * @param header The header to read. + */ + public DimensionReader(String header) { + super(header); + } - if (value != null) { - if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT)) { - result = Dimension.MEDIA_TYPE; - } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT_CHARSET)) { - result = Dimension.CHARACTER_SET; - } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT_ENCODING)) { - result = Dimension.ENCODING; - } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT_LANGUAGE)) { - result = Dimension.LANGUAGE; - } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_AUTHORIZATION)) { - result = Dimension.AUTHORIZATION; - } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_USER_AGENT)) { - result = Dimension.CLIENT_AGENT; - } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ORIGIN)) { - result = Dimension.ORIGIN; - } else if (value.equals("*")) { - result = Dimension.UNSPECIFIED; - } - } + @Override + public Dimension readValue() throws IOException { + Dimension result = null; + String value = readRawValue(); - return result; - } + if (value != null) { + if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT)) { + result = Dimension.MEDIA_TYPE; + } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT_CHARSET)) { + result = Dimension.CHARACTER_SET; + } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT_ENCODING)) { + result = Dimension.ENCODING; + } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ACCEPT_LANGUAGE)) { + result = Dimension.LANGUAGE; + } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_AUTHORIZATION)) { + result = Dimension.AUTHORIZATION; + } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_USER_AGENT)) { + result = Dimension.CLIENT_AGENT; + } else if (value.equalsIgnoreCase(HeaderConstants.HEADER_ORIGIN)) { + result = Dimension.ORIGIN; + } else if (value.equals("*")) { + result = Dimension.UNSPECIFIED; + } + } + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java index 80c7d47f54..08ecd3bb39 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java @@ -1,85 +1,82 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Dimension; - import java.util.Collection; +import org.restlet.data.Dimension; /** * Dimension header writer. - * + * * @author Thierry Boileau */ public class DimensionWriter extends HeaderWriter { - /** - * Creates a vary header from the given dimensions. - * - * @param dimensions The dimensions to copy to the response. - * @return Returns the Vary header or null, if dimensions is null or empty. - */ - public static String write(Collection dimensions) { - return new DimensionWriter().append(dimensions).toString(); - } - - /** - * Appends a collection of dimensions as a header. - * - * @param dimensions The dimensions to format. - * @return This writer. - */ - public DimensionWriter append(Collection dimensions) { - if ((dimensions != null) && !dimensions.isEmpty()) { - if (dimensions.contains(Dimension.CLIENT_ADDRESS) || dimensions.contains(Dimension.TIME) - || dimensions.contains(Dimension.UNSPECIFIED)) { - // From an HTTP point of view the representations can - // vary in unspecified ways - append("*"); - } else { - boolean first = true; + /** + * Creates a "vary" header from the given dimensions. + * + * @param dimensions The dimensions to copy to the response. + * @return Returns the "Vary" header or null if dimensions are null or empty. + */ + public static String write(Collection dimensions) { + return new DimensionWriter().append(dimensions).toString(); + } - for (Dimension dimension : dimensions) { - if (first) { - first = false; - } else { - append(", "); - } + /** + * Appends a collection of dimensions as a header. + * + * @param dimensions The dimensions to format. + * @return This writer. + */ + public DimensionWriter append(Collection dimensions) { + if ((dimensions != null) && !dimensions.isEmpty()) { + if (dimensions.contains(Dimension.CLIENT_ADDRESS) + || dimensions.contains(Dimension.TIME) + || dimensions.contains(Dimension.UNSPECIFIED)) { + // From an HTTP point of view, the representations can vary in unspecified ways + append("*"); + } else { + boolean first = true; - append(dimension); - } - } - } + for (Dimension dimension : dimensions) { + if (first) { + first = false; + } else { + append(", "); + } - return this; - } + append(dimension); + } + } + } - @Override - public HeaderWriter append(Dimension dimension) { - if (dimension == Dimension.CHARACTER_SET) { - append(HeaderConstants.HEADER_ACCEPT_CHARSET); - } else if (dimension == Dimension.CLIENT_AGENT) { - append(HeaderConstants.HEADER_USER_AGENT); - } else if (dimension == Dimension.ENCODING) { - append(HeaderConstants.HEADER_ACCEPT_ENCODING); - } else if (dimension == Dimension.LANGUAGE) { - append(HeaderConstants.HEADER_ACCEPT_LANGUAGE); - } else if (dimension == Dimension.MEDIA_TYPE) { - append(HeaderConstants.HEADER_ACCEPT); - } else if (dimension == Dimension.AUTHORIZATION) { - append(HeaderConstants.HEADER_AUTHORIZATION); - } else if (dimension == Dimension.ORIGIN) { - append(HeaderConstants.HEADER_ORIGIN); - } + return this; + } - return this; - } + @Override + public HeaderWriter append(Dimension dimension) { + if (dimension == Dimension.CHARACTER_SET) { + append(HeaderConstants.HEADER_ACCEPT_CHARSET); + } else if (dimension == Dimension.CLIENT_AGENT) { + append(HeaderConstants.HEADER_USER_AGENT); + } else if (dimension == Dimension.ENCODING) { + append(HeaderConstants.HEADER_ACCEPT_ENCODING); + } else if (dimension == Dimension.LANGUAGE) { + append(HeaderConstants.HEADER_ACCEPT_LANGUAGE); + } else if (dimension == Dimension.MEDIA_TYPE) { + append(HeaderConstants.HEADER_ACCEPT); + } else if (dimension == Dimension.AUTHORIZATION) { + append(HeaderConstants.HEADER_AUTHORIZATION); + } else if (dimension == Dimension.ORIGIN) { + append(HeaderConstants.HEADER_ORIGIN); + } + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java b/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java index e23228807a..68cd4dbbbf 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java @@ -1,60 +1,57 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.IOException; import org.restlet.data.Disposition; import org.restlet.data.Parameter; -import java.io.IOException; - /** * Disposition header reader. - * + * * @author Thierry Boileau */ public class DispositionReader extends HeaderReader { - /** - * Constructor. - * - * @param header The header to read. - */ - public DispositionReader(String header) { - super(header); - } - - @Override - public Disposition readValue() throws IOException { - Disposition result = null; - String type = readToken(); - - if (!type.isEmpty()) { - result = new Disposition(); - result.setType(type); - - if (skipParameterSeparator()) { - Parameter param = readParameter(); - - while (param != null) { - result.getParameters().add(param); - - if (skipParameterSeparator()) { - param = readParameter(); - } else { - param = null; - } - } - } - } - - return result; - } - + /** + * Constructor. + * + * @param header The header to read. + */ + public DispositionReader(String header) { + super(header); + } + + @Override + public Disposition readValue() throws IOException { + Disposition result = null; + String type = readToken(); + + if (!type.isEmpty()) { + result = new Disposition(); + result.setType(type); + + if (skipParameterSeparator()) { + Parameter param = readParameter(); + + while (param != null) { + result.getParameters().add(param); + + if (skipParameterSeparator()) { + param = readParameter(); + } else { + param = null; + } + } + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java index b5d19a1e0d..4082811ca2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; import org.restlet.data.Disposition; @@ -14,42 +13,41 @@ /** * Disposition header writer. - * + * * @author Thierry Boileau */ public class DispositionWriter extends HeaderWriter { - /** - * Formats a disposition. - * - * @param disposition The disposition to format. - * @return The formatted disposition. - */ - public static String write(Disposition disposition) { - return new DispositionWriter().append(disposition).toString(); - } - - @Override - public DispositionWriter append(Disposition disposition) { - if (Disposition.TYPE_NONE.equals(disposition.getType()) || disposition.getType() == null) { - return this; - } - - append(disposition.getType()); - - for (Parameter parameter : disposition.getParameters()) { - append("; "); - append(parameter.getName()); - append("="); - - if (HeaderUtils.isToken(parameter.getValue())) { - append(parameter.getValue()); - } else { - appendQuotedString(parameter.getValue()); - } - } - - return this; - } - + /** + * Formats a disposition. + * + * @param disposition The disposition to format. + * @return The formatted disposition. + */ + public static String write(Disposition disposition) { + return new DispositionWriter().append(disposition).toString(); + } + + @Override + public DispositionWriter append(Disposition disposition) { + if (Disposition.TYPE_NONE.equals(disposition.getType()) || disposition.getType() == null) { + return this; + } + + append(disposition.getType()); + + for (Parameter parameter : disposition.getParameters()) { + append("; "); + append(parameter.getName()); + append("="); + + if (HeaderUtils.isToken(parameter.getValue())) { + append(parameter.getValue()); + } else { + appendQuotedString(parameter.getValue()); + } + } + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java b/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java index 1afbc0e976..afd0430a2e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java @@ -1,43 +1,40 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Encoding; - import java.io.IOException; import java.util.Collection; +import org.restlet.data.Encoding; /** * Encoding header reader. - * + * * @author Jerome Louvel */ public class EncodingReader extends HeaderReader { - /** - * Constructor. - * - * @param header The header to read. - */ - public EncodingReader(String header) { - super(header); - } - - @Override - protected boolean canAdd(Encoding value, Collection values) { - return value != null && !Encoding.IDENTITY.equals(value); - } - - @Override - public Encoding readValue() throws IOException { - return Encoding.valueOf(readToken()); - } - + /** + * Constructor. + * + * @param header The header to read. + */ + public EncodingReader(String header) { + super(header); + } + + @Override + protected boolean canAdd(Encoding value, Collection values) { + return value != null && !Encoding.IDENTITY.equals(value); + } + + @Override + public Encoding readValue() throws IOException { + return Encoding.valueOf(readToken()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java index 38c37c4d71..778307dc9f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java @@ -1,37 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Encoding; - import java.util.List; +import org.restlet.data.Encoding; /** * Encoding header writer. - * + * * @author Jerome Louvel */ public class EncodingWriter extends MetadataWriter { - /** - * Writes a list of encodings. - * - * @param encodings The encodings to write. - * @return This writer. - */ - public static String write(List encodings) { - return new EncodingWriter().append(encodings).toString(); - } + /** + * Writes a list of encodings. + * + * @param encodings The encodings to write. + * @return This writer. + */ + public static String write(List encodings) { + return new EncodingWriter().append(encodings).toString(); + } - @Override - protected boolean canWrite(Encoding encoding) { - return !encoding.equals(Encoding.IDENTITY); - } + @Override + protected boolean canWrite(Encoding encoding) { + return !encoding.equals(Encoding.IDENTITY); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java index 8e69fc05e4..6cd522deaf 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java @@ -1,55 +1,52 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.IOException; import org.restlet.data.ClientInfo; import org.restlet.data.Expectation; -import java.io.IOException; - /** * Expectation header reader. - * + * * @author Jerome Louvel */ public class ExpectationReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param clientInfo The client info to update. - */ - public static void addValues(String header, ClientInfo clientInfo) { - if (header != null) { - new ExpectationReader(header).addValues(clientInfo.getExpectations()); - } - } - - /** - * Constructor. - * - * @param header The header to read. - */ - public ExpectationReader(String header) { - super(header); - } - - @Override - public Expectation readValue() throws IOException { - Expectation result = readNamedValue(Expectation.class); - - while (skipParameterSeparator()) { - result.getParameters().add(readParameter()); - } - - return result; - } - + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param clientInfo The client info to update. + */ + public static void addValues(String header, ClientInfo clientInfo) { + if (header != null) { + new ExpectationReader(header).addValues(clientInfo.getExpectations()); + } + } + + /** + * Constructor. + * + * @param header The header to read. + */ + public ExpectationReader(String header) { + super(header); + } + + @Override + public Expectation readValue() throws IOException { + Expectation result = readNamedValue(Expectation.class); + + while (skipParameterSeparator()) { + result.getParameters().add(readParameter()); + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java index 1b200bb3d0..a7018f1bb6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java @@ -1,50 +1,47 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.util.List; import org.restlet.data.Expectation; import org.restlet.data.Parameter; -import java.util.List; - /** * Expectation header writer. - * + * * @author Jerome Louvel */ public class ExpectationWriter extends HeaderWriter { - /** - * Writes a list of expectations with a comma separator. - * - * @param expectations The list of expectations. - * @return The formatted list of expectations. - */ - public static String write(List expectations) { - return new ExpectationWriter().append(expectations).toString(); - } - - @Override - public ExpectationWriter append(Expectation expectation) { - if ((expectation.getName() != null) && (!expectation.getName().isEmpty())) { - appendExtension(expectation); - - if (!expectation.getParameters().isEmpty()) { - for (Parameter param : expectation.getParameters()) { - appendParameterSeparator(); - appendExtension(param); - } - } - } - - return this; - } - + /** + * Writes a list of expectations with a comma separator. + * + * @param expectations The list of expectations. + * @return The formatted list of expectations. + */ + public static String write(List expectations) { + return new ExpectationWriter().append(expectations).toString(); + } + + @Override + public ExpectationWriter append(Expectation expectation) { + if ((expectation.getName() != null) && (!expectation.getName().isEmpty())) { + appendExtension(expectation); + + if (!expectation.getParameters().isEmpty()) { + for (Parameter param : expectation.getParameters()) { + appendParameterSeparator(); + appendExtension(param); + } + } + } + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java index e784a80851..7ba777ecfd 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java @@ -1,202 +1,205 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; /** * Constants related to the HTTP protocol. - * + * * @author Jerome Louvel */ public final class HeaderConstants { - // -------------------- - // --- Expectations --- - // -------------------- + // -------------------- + // --- Expectations --- + // -------------------- - public static final String EXPECT_CONTINUE = "100-continue"; + public static final String EXPECT_CONTINUE = "100-continue"; - // ------------------------ - // --- Cache directives --- - // ------------------------ + // ------------------------ + // --- Cache directives --- + // ------------------------ - public static final String CACHE_NO_CACHE = "no-cache"; + public static final String CACHE_NO_CACHE = "no-cache"; - public static final String CACHE_NO_STORE = "no-store"; + public static final String CACHE_NO_STORE = "no-store"; - public static final String CACHE_MAX_AGE = "max-age"; + public static final String CACHE_MAX_AGE = "max-age"; - public static final String CACHE_MAX_STALE = "max-stale"; + public static final String CACHE_MAX_STALE = "max-stale"; - public static final String CACHE_MIN_FRESH = "min-fresh"; + public static final String CACHE_MIN_FRESH = "min-fresh"; - public static final String CACHE_NO_TRANSFORM = "no-transform"; + public static final String CACHE_NO_TRANSFORM = "no-transform"; - public static final String CACHE_ONLY_IF_CACHED = "only-if-cached"; + public static final String CACHE_ONLY_IF_CACHED = "only-if-cached"; - public static final String CACHE_PUBLIC = "public"; + public static final String CACHE_PUBLIC = "public"; - public static final String CACHE_PRIVATE = "private"; + public static final String CACHE_PRIVATE = "private"; - public static final String CACHE_MUST_REVALIDATE = "must-revalidate"; + public static final String CACHE_MUST_REVALIDATE = "must-revalidate"; - public static final String CACHE_PROXY_MUST_REVALIDATE = "proxy-revalidate"; + public static final String CACHE_PROXY_MUST_REVALIDATE = "proxy-revalidate"; - public static final String CACHE_SHARED_MAX_AGE = "s-maxage"; + public static final String CACHE_SHARED_MAX_AGE = "s-maxage"; - // --------------------- - // --- Header names --- - // --------------------- + // --------------------- + // --- Header names --- + // --------------------- - public static final String HEADER_ACCEPT = "Accept"; + public static final String HEADER_ACCEPT = "Accept"; - public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset"; + public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset"; - public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; + public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; - public static final String HEADER_ACCEPT_LANGUAGE = "Accept-Language"; + public static final String HEADER_ACCEPT_LANGUAGE = "Accept-Language"; - public static final String HEADER_ACCEPT_PATCH = "Accept-Patch"; + public static final String HEADER_ACCEPT_PATCH = "Accept-Patch"; - public static final String HEADER_ACCEPT_RANGES = "Accept-Ranges"; + public static final String HEADER_ACCEPT_RANGES = "Accept-Ranges"; - public static final String HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + public static final String HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS = + "Access-Control-Allow-Credentials"; - public static final String HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + public static final String HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; - public static final String HEADER_ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + public static final String HEADER_ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; - public static final String HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + public static final String HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; - public static final String HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; + public static final String HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = + "Access-Control-Expose-Headers"; - public static final String HEADER_ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; + public static final String HEADER_ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; - public static final String HEADER_ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; + public static final String HEADER_ACCESS_CONTROL_REQUEST_HEADERS = + "Access-Control-Request-Headers"; - public static final String HEADER_ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; + public static final String HEADER_ACCESS_CONTROL_REQUEST_METHOD = + "Access-Control-Request-Method"; - public static final String HEADER_AGE = "Age"; + public static final String HEADER_AGE = "Age"; - public static final String HEADER_ALLOW = "Allow"; + public static final String HEADER_ALLOW = "Allow"; - public static final String HEADER_AUTHENTICATION_INFO = "Authentication-Info"; + public static final String HEADER_AUTHENTICATION_INFO = "Authentication-Info"; - public static final String HEADER_AUTHORIZATION = "Authorization"; + public static final String HEADER_AUTHORIZATION = "Authorization"; - public static final String HEADER_CACHE_CONTROL = "Cache-Control"; + public static final String HEADER_CACHE_CONTROL = "Cache-Control"; - public static final String HEADER_CONNECTION = "Connection"; + public static final String HEADER_CONNECTION = "Connection"; - public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; + public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; - public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; + public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; - public static final String HEADER_CONTENT_LANGUAGE = "Content-Language"; + public static final String HEADER_CONTENT_LANGUAGE = "Content-Language"; - public static final String HEADER_CONTENT_LENGTH = "Content-Length"; + public static final String HEADER_CONTENT_LENGTH = "Content-Length"; - public static final String HEADER_CONTENT_LOCATION = "Content-Location"; + public static final String HEADER_CONTENT_LOCATION = "Content-Location"; - public static final String HEADER_CONTENT_MD5 = "Content-MD5"; + public static final String HEADER_CONTENT_MD5 = "Content-MD5"; - public static final String HEADER_CONTENT_RANGE = "Content-Range"; + public static final String HEADER_CONTENT_RANGE = "Content-Range"; - public static final String HEADER_CONTENT_TYPE = "Content-Type"; + public static final String HEADER_CONTENT_TYPE = "Content-Type"; - public static final String HEADER_COOKIE = "Cookie"; + public static final String HEADER_COOKIE = "Cookie"; - public static final String HEADER_DATE = "Date"; + public static final String HEADER_DATE = "Date"; - public static final String HEADER_ETAG = "ETag"; + public static final String HEADER_ETAG = "ETag"; - public static final String HEADER_EXPECT = "Expect"; + public static final String HEADER_EXPECT = "Expect"; - public static final String HEADER_EXPIRES = "Expires"; + public static final String HEADER_EXPIRES = "Expires"; - public static final String HEADER_FROM = "From"; + public static final String HEADER_FROM = "From"; - public static final String HEADER_HOST = "Host"; + public static final String HEADER_HOST = "Host"; - public static final String HEADER_IF_MATCH = "If-Match"; + public static final String HEADER_IF_MATCH = "If-Match"; - public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since"; + public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since"; - public static final String HEADER_IF_NONE_MATCH = "If-None-Match"; + public static final String HEADER_IF_NONE_MATCH = "If-None-Match"; - public static final String HEADER_IF_RANGE = "If-Range"; + public static final String HEADER_IF_RANGE = "If-Range"; - public static final String HEADER_IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + public static final String HEADER_IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; - public static final String HEADER_LAST_MODIFIED = "Last-Modified"; + public static final String HEADER_LAST_MODIFIED = "Last-Modified"; - public static final String HEADER_LOCATION = "Location"; + public static final String HEADER_LOCATION = "Location"; - public static final String HEADER_MAX_FORWARDS = "Max-Forwards"; + public static final String HEADER_MAX_FORWARDS = "Max-Forwards"; - public static final String HEADER_ORIGIN = "Origin"; + public static final String HEADER_ORIGIN = "Origin"; - public static final String HEADER_PRAGMA = "Pragma"; + public static final String HEADER_PRAGMA = "Pragma"; - public static final String HEADER_PROXY_AUTHENTICATE = "Proxy-Authenticate"; + public static final String HEADER_PROXY_AUTHENTICATE = "Proxy-Authenticate"; - public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization"; + public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization"; - public static final String HEADER_RANGE = "Range"; + public static final String HEADER_RANGE = "Range"; - public static final String HEADER_REFERRER = "Referer"; + public static final String HEADER_REFERRER = "Referer"; - public static final String HEADER_RETRY_AFTER = "Retry-After"; + public static final String HEADER_RETRY_AFTER = "Retry-After"; - public static final String HEADER_SERVER = "Server"; + public static final String HEADER_SERVER = "Server"; - public static final String HEADER_SET_COOKIE = "Set-Cookie"; + public static final String HEADER_SET_COOKIE = "Set-Cookie"; - public static final String HEADER_SET_COOKIE2 = "Set-Cookie2"; + public static final String HEADER_SET_COOKIE2 = "Set-Cookie2"; - public static final String HEADER_SLUG = "Slug"; + public static final String HEADER_SLUG = "Slug"; - public static final String HEADER_TRAILER = "Trailer"; + public static final String HEADER_TRAILER = "Trailer"; - public static final String HEADER_TRANSFER_ENCODING = "Transfer-Encoding"; + public static final String HEADER_TRANSFER_ENCODING = "Transfer-Encoding"; - public static final String HEADER_TRANSFER_EXTENSION = "TE"; + public static final String HEADER_TRANSFER_EXTENSION = "TE"; - public static final String HEADER_UPGRADE = "Upgrade"; + public static final String HEADER_UPGRADE = "Upgrade"; - public static final String HEADER_USER_AGENT = "User-Agent"; + public static final String HEADER_USER_AGENT = "User-Agent"; - public static final String HEADER_VARY = "Vary"; + public static final String HEADER_VARY = "Vary"; - public static final String HEADER_VIA = "Via"; + public static final String HEADER_VIA = "Via"; - public static final String HEADER_WARNING = "Warning"; + public static final String HEADER_WARNING = "Warning"; - public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; + public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; - public static final String HEADER_X_FORWARDED_FOR = "X-Forwarded-For"; + public static final String HEADER_X_FORWARDED_FOR = "X-Forwarded-For"; - public static final String HEADER_X_FORWARDED_PORT = "X-Forwarded-Port"; + public static final String HEADER_X_FORWARDED_PORT = "X-Forwarded-Port"; - public static final String HEADER_X_FORWARDED_PROTO = "X-Forwarded-Proto"; + public static final String HEADER_X_FORWARDED_PROTO = "X-Forwarded-Proto"; - public static final String HEADER_X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override"; + public static final String HEADER_X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override"; - // ------------------------- - // --- Attribute names --- - // ------------------------- + // ------------------------- + // --- Attribute names --- + // ------------------------- - public static final String ATTRIBUTE_HEADERS = "org.restlet.http.headers"; + public static final String ATTRIBUTE_HEADERS = "org.restlet.http.headers"; - public static final String ATTRIBUTE_VERSION = "org.restlet.http.version"; + public static final String ATTRIBUTE_VERSION = "org.restlet.http.version"; - public static final String ATTRIBUTE_HTTPS_KEY_SIZE = "org.restlet.https.keySize"; + public static final String ATTRIBUTE_HTTPS_KEY_SIZE = "org.restlet.https.keySize"; - public static final String ATTRIBUTE_HTTPS_SSL_SESSION_ID = "org.restlet.https.sslSessionId"; + public static final String ATTRIBUTE_HTTPS_SSL_SESSION_ID = "org.restlet.https.sslSessionId"; } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java index c5b2794fae..219bb23d07 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java @@ -1,20 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.Context; -import org.restlet.data.Encoding; -import org.restlet.data.Header; -import org.restlet.data.Parameter; -import org.restlet.engine.util.DateUtils; -import org.restlet.util.NamedValue; +import static org.restlet.engine.header.HeaderUtils.isCarriageReturn; +import static org.restlet.engine.header.HeaderUtils.isComma; +import static org.restlet.engine.header.HeaderUtils.isCommentText; +import static org.restlet.engine.header.HeaderUtils.isDoubleQuote; +import static org.restlet.engine.header.HeaderUtils.isLineFeed; +import static org.restlet.engine.header.HeaderUtils.isLinearWhiteSpace; +import static org.restlet.engine.header.HeaderUtils.isQuoteCharacter; +import static org.restlet.engine.header.HeaderUtils.isQuotedText; +import static org.restlet.engine.header.HeaderUtils.isSemiColon; +import static org.restlet.engine.header.HeaderUtils.isSpace; +import static org.restlet.engine.header.HeaderUtils.isTokenChar; import java.io.IOException; import java.io.InputStream; @@ -23,417 +27,425 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; - -import static org.restlet.engine.header.HeaderUtils.*; +import org.restlet.Context; +import org.restlet.data.Encoding; +import org.restlet.data.Header; +import org.restlet.data.Parameter; +import org.restlet.engine.util.DateUtils; +import org.restlet.util.NamedValue; /** * HTTP-style header reader. - * - * @param The header value target type. There can be multiple values for a - * single header. + * + * @param The header value target type. There can be multiple values for a single header. * @author Jerome Louvel */ public class HeaderReader { - /** - * Creates a new named value with a null value. - * - * @param name The name. - * @param resultClass The named value class to return. - * @return The new named value. - */ - private static > NV createNamedValue(Class resultClass, String name) { - return createNamedValue(resultClass, name, null); - } - - /** - * Creates a new named value. - * - * @param name The name. - * @param value The value or null. - * @param resultClass The named value class to return. - * @return The new named value. - */ - private static > NV createNamedValue(Class resultClass, String name, - String value) { - try { - return resultClass.getConstructor(String.class, String.class).newInstance(name, value); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to create named value", e); - return null; - } - } - - /** - * Parses a date string. - * - * @param date The date string to parse. - * @param cookie Indicates if the date is in the cookie format. - * @return The parsed date. - */ - public static Date readDate(String date, boolean cookie) { - if (cookie) { - return DateUtils.parse(date, DateUtils.FORMAT_RFC_1036); - } - - return DateUtils.parse(date, DateUtils.FORMAT_RFC_1123); - } - - /** - * Read a header. Return null if the last header was already read. - * - * @param header The header line to parse. - * @return The header read or null. - * @throws IOException - */ - public static Header readHeader(CharSequence header) throws IOException { - Header result = null; - - if (!header.isEmpty()) { - // Detect the end of headers - int start = 0; - int index = 0; - int next = header.charAt(index++); - - if (isCarriageReturn(next)) { - next = header.charAt(index++); - - if (!isLineFeed(next)) { - throw new IOException("Invalid end of headers. Line feed missing after the carriage return."); - } - } else { - result = new Header(); - - // Parse the header name - while ((index < header.length()) && (next != ':')) { - next = header.charAt(index++); - } - - if (next != ':') { - // Colon character is mandatory - throw new IOException("Unable to parse the header name. End of line reached too early."); - } - - result.setName(header.subSequence(start, index - 1).toString()); - - if (index == header.length()) { - // No more content to read. - return result; - } - next = header.charAt(index++); - - while ((index < header.length()) && isSpace(next)) { - // Skip any separator space between colon and header value - next = header.charAt(index++); - } - if (index < header.length()) { - start = index - 1; - - // Parse the header value - result.setValue(header.subSequence(start, header.length()).toString()); - } - - } - } - - return result; - } - - /** - * Read a header. Return null if the last header was already read. - * - * @param is The message input stream. - * @param sb The string builder to reuse. - * @return The header read or null. - * @throws IOException - */ - public static Header readHeader(InputStream is, StringBuilder sb) throws IOException { - Header result = null; - - // Detect the end of headers - int next = is.read(); - - if (isCarriageReturn(next)) { - next = is.read(); - - if (!isLineFeed(next)) { - throw new IOException("Invalid end of headers. Line feed missing after the carriage return."); - } - } else { - result = new Header(); - - // Parse the header name - while ((next != -1) && (next != ':')) { - sb.append((char) next); - next = is.read(); - } - - if (next == -1) { - throw new IOException("Unable to parse the header name. End of stream reached too early."); - } - - result.setName(sb.toString()); - sb.delete(0, sb.length()); - next = is.read(); - - while (isSpace(next)) { - // Skip any separator space between colon and header value - next = is.read(); - } - - // Parse the header value - while ((next != -1) && (!isCarriageReturn(next))) { - sb.append((char) next); - next = is.read(); - } - - if (next == -1) { - throw new IOException("Unable to parse the header value. End of stream reached too early."); - } - - next = is.read(); - - if (isLineFeed(next)) { - result.setValue(sb.toString()); - sb.delete(0, sb.length()); - } else { - throw new IOException( - "Unable to parse the HTTP header value. The carriage return must be followed by a line feed."); - } - } - - return result; - } - - /** The header to read. */ - private final String header; - - /** The current read index (or -1 if not reading anymore). */ - private volatile int index; - - /** The current mark. */ - private volatile int mark; - - /** - * Constructor. - * - * @param header The header to read. - */ - public HeaderReader(String header) { - this.header = header; - this.index = ((header == null) || (header.isEmpty())) ? -1 : 0; - this.mark = index; - } - - /** - * Adds values to the given list. - * - * @param values The list of values to update. - */ - public void addValues(Collection values) { - try { - // Skip leading spaces - skipSpaces(); - boolean cont = true; - do { - int i = index; - - // Read the first value - V nextValue = readValue(); - if (canAdd(nextValue, values)) { - // Add the value to the list - values.add(nextValue); - } - - // Attempt to skip the value separator - skipValueSeparator(); - if (index == -1) { - cont = false; - } else if (i == index) { - // Infinite loop - throw new IOException("The reading of one header initiates an infinite loop"); - } - } while (cont); - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.INFO, "Unable to read a header", ioe); - } - } - - /** - * Indicates if the value can be added the the list. Useful to prevent the - * addition of {@link Encoding#IDENTITY} constants for example. By default it - * returns true for non null values. - * - * @param value The value to add. - * - * @param values The target collection. - * @return True if the value can be added. - */ - protected boolean canAdd(V value, Collection values) { - return value != null && !values.contains(value); - } - - /** - * Creates a new parameter with a null value. Can be overridden. - * - * @param name The parameter name. - * @return The new parameter. - */ - protected final Parameter createParameter(String name) { - return createParameter(name, null); - } - - /** - * Creates a new parameter. Can be overridden. - * - * @param name The parameter name. - * @param value The parameter value or null. - * @return The new parameter. - */ - protected Parameter createParameter(String name, String value) { - return new Parameter(name, value); - } - - /** - * Marks the current position in this reader. A subsequent call to the - * reset method repositions this reader at the last marked - * position. - */ - public void mark() { - mark = index; - } - - /** - * Reads the next character without moving the reader index. - * - * @return The next character. - */ - public int peek() { - int result = -1; - - if (this.index != -1) { - result = this.header.charAt(this.index); - } - - return result; - } - - /** - * Reads the next character. - * - * @return The next character. - */ - public int read() { - int result = -1; - - if (this.index >= 0) { - result = this.header.charAt(this.index++); - - if (this.index >= this.header.length()) { - this.index = -1; - } - } - - return result; - } - - /** - * Reads a parameter value which is either a token or a quoted string. - * - * @return A parameter value. - * @throws IOException - */ - public String readActualNamedValue() throws IOException { - String result = null; - - // Discard any leading space - skipSpaces(); - - // Detect if quoted string or token available - int nextChar = peek(); - - if (isDoubleQuote(nextChar)) { - result = readQuotedString(); - } else if (isTokenChar(nextChar)) { - result = readToken(); - } - - return result; - } - - /** - * Reads the next comment. The first character must be a parenthesis. - * - * @return The next comment. - * @throws IOException - */ - public String readComment() throws IOException { - String result = null; - int next = read(); - - // First character must be a parenthesis - if (next == '(') { - StringBuilder buffer = new StringBuilder(); - - while (result == null) { - next = read(); - - if (isCommentText(next)) { - buffer.append((char) next); - } else if (isQuoteCharacter(next)) { - // Start of a quoted pair (escape sequence) - buffer.append((char) read()); - } else if (next == '(') { - // Nested comment - buffer.append('(').append(readComment()).append(')'); - } else if (next == ')') { - // End of comment - result = buffer.toString(); - } else if (next == -1) { - throw new IOException("Unexpected end of comment. Please check your value"); - } else { - throw new IOException( - "Invalid character \"" + next + "\" detected in comment. Please check your value"); - } - } - } else { - throw new IOException("A comment must start with a parenthesis"); - } - - return result; - } - - /** - * Reads the next digits. - * - * @return The next digits. - */ - public String readDigits() { - StringBuilder sb = new StringBuilder(); - int next = read(); - - while (isTokenChar(next)) { - sb.append((char) next); - next = read(); - } - - // Unread the last character (separator or end marker) - unread(); - - return sb.toString(); - } - - /** - * Reads the next pair as a parameter. - * - * @param resultClass The named value class to return. - * @return The next pair as a parameter. - * @throws IOException - */ - public > NV readNamedValue(Class resultClass) throws IOException { - NV result = null; - String name = readToken(); - int nextChar = read(); + /** + * Creates a new NamedValue with a null value. + * + * @param name The name. + * @param resultClass The NamedValue class to return. + * @return The new NamedValue. + */ + private static > NV createNamedValue( + Class resultClass, String name) { + return createNamedValue(resultClass, name, null); + } + + /** + * Creates a new named value. + * + * @param name The name. + * @param value The value or null. + * @param resultClass The named value class to return. + * @return The new named value. + */ + private static > NV createNamedValue( + Class resultClass, String name, String value) { + try { + return resultClass.getConstructor(String.class, String.class).newInstance(name, value); + } catch (Exception e) { + Context.getCurrentLogger().log(Level.WARNING, "Unable to create named value", e); + return null; + } + } + + /** + * Parses a date string. + * + * @param date The date string to parse. + * @param cookie Indicates if the date is in the cookie format. + * @return The parsed date. + */ + public static Date readDate(String date, boolean cookie) { + if (cookie) { + return DateUtils.parse(date, DateUtils.FORMAT_RFC_1036); + } + + return DateUtils.parse(date, DateUtils.FORMAT_RFC_1123); + } + + /** + * Read a header. Return null if the last header was already read. + * + * @param header The header line to parse. + * @return The header read or null. + * @throws IOException + */ + public static Header readHeader(CharSequence header) throws IOException { + Header result = null; + + if (!header.isEmpty()) { + // Detect the end of headers + int start = 0; + int index = 0; + int next = header.charAt(index++); + + if (isCarriageReturn(next)) { + next = header.charAt(index++); + + if (!isLineFeed(next)) { + throw new IOException( + "Invalid end of headers. Line feed missing after the carriage return."); + } + } else { + result = new Header(); + + // Parse the header name + while ((index < header.length()) && (next != ':')) { + next = header.charAt(index++); + } + + if (next != ':') { + // Colon character is mandatory + throw new IOException( + "Unable to parse the header name. End of line reached too early."); + } + + result.setName(header.subSequence(start, index - 1).toString()); + + if (index == header.length()) { + // No more content to read. + return result; + } + next = header.charAt(index++); + + while ((index < header.length()) && isSpace(next)) { + // Skip any separator space between colon and header value + next = header.charAt(index++); + } + if (index < header.length()) { + start = index - 1; + + // Parse the header value + result.setValue(header.subSequence(start, header.length()).toString()); + } + } + } + + return result; + } + + /** + * Read a header. Return null if the last header was already read. + * + * @param is The message input stream. + * @param sb The string builder to reuse. + * @return The header read or null. + * @throws IOException + */ + public static Header readHeader(InputStream is, StringBuilder sb) throws IOException { + Header result = null; + + // Detect the end of headers + int next = is.read(); + + if (isCarriageReturn(next)) { + next = is.read(); + + if (!isLineFeed(next)) { + throw new IOException( + "Invalid end of headers. Line feed missing after the carriage return."); + } + } else { + result = new Header(); + + // Parse the header name + while ((next != -1) && (next != ':')) { + sb.append((char) next); + next = is.read(); + } + + if (next == -1) { + throw new IOException( + "Unable to parse the header name. End of stream reached too early."); + } + + result.setName(sb.toString()); + sb.delete(0, sb.length()); + next = is.read(); + + while (isSpace(next)) { + // Skip any separator space between colon and header value + next = is.read(); + } + + // Parse the header value + while ((next != -1) && (!isCarriageReturn(next))) { + sb.append((char) next); + next = is.read(); + } + + if (next == -1) { + throw new IOException( + "Unable to parse the header value. End of stream reached too early."); + } + + next = is.read(); + + if (isLineFeed(next)) { + result.setValue(sb.toString()); + sb.delete(0, sb.length()); + } else { + throw new IOException( + "Unable to parse the HTTP header value. The carriage return must be followed by a line feed."); + } + } + + return result; + } + + /** The header to read. */ + private final String header; + + /** The current read index (or -1 if not reading anymore). */ + private volatile int index; + + /** The current mark. */ + private volatile int mark; + + /** + * Constructor. + * + * @param header The header to read. + */ + public HeaderReader(String header) { + this.header = header; + this.index = ((header == null) || (header.isEmpty())) ? -1 : 0; + this.mark = index; + } + + /** + * Adds values to the given list. + * + * @param values The list of values to update. + */ + public void addValues(Collection values) { + try { + // Skip leading spaces + skipSpaces(); + boolean cont = true; + do { + int i = index; + + // Read the first value + V nextValue = readValue(); + if (canAdd(nextValue, values)) { + // Add the value to the list + values.add(nextValue); + } + + // Attempt to skip the value separator + skipValueSeparator(); + if (index == -1) { + cont = false; + } else if (i == index) { + // Infinite loop + throw new IOException("The reading of one header initiates an infinite loop"); + } + } while (cont); + } catch (IOException ioe) { + Context.getCurrentLogger().log(Level.INFO, "Unable to read a header", ioe); + } + } + + /** + * Indicates if the value can be added to the list. Useful to prevent the addition of {@link + * Encoding#IDENTITY} constants for example. By default, it returns true for non-null values. + * + * @param value The value to add. + * @param values The target collection. + * @return True if the value can be added. + */ + protected boolean canAdd(V value, Collection values) { + return value != null && !values.contains(value); + } + + /** + * Creates a new parameter with a null value. Can be overridden. + * + * @param name The parameter name. + * @return The new parameter. + */ + protected final Parameter createParameter(String name) { + return createParameter(name, null); + } + + /** + * Creates a new parameter. Can be overridden. + * + * @param name The parameter name. + * @param value The parameter value or null. + * @return The new parameter. + */ + protected Parameter createParameter(String name, String value) { + return new Parameter(name, value); + } + + /** + * Marks the current position in this reader. A later call to the reset method + * repositions this reader at the last marked position. + */ + public void mark() { + mark = index; + } + + /** + * Reads the next character without moving the reader index. + * + * @return The next character. + */ + public int peek() { + int result = -1; + + if (this.index != -1) { + result = this.header.charAt(this.index); + } + + return result; + } + + /** + * Reads the next character. + * + * @return The next character. + */ + public int read() { + int result = -1; + + if (this.index >= 0) { + result = this.header.charAt(this.index++); + + if (this.index >= this.header.length()) { + this.index = -1; + } + } + + return result; + } + + /** + * Reads a parameter value, which is either a token or a quoted string. + * + * @return A parameter value. + * @throws IOException + */ + public String readActualNamedValue() throws IOException { + String result = null; + + // Discard any leading space + skipSpaces(); + + // Detect if quoted string or token available + int nextChar = peek(); + + if (isDoubleQuote(nextChar)) { + result = readQuotedString(); + } else if (isTokenChar(nextChar)) { + result = readToken(); + } + + return result; + } + + /** + * Reads the next comment. The first character must be a parenthesis. + * + * @return The next comment. + * @throws IOException + */ + public String readComment() throws IOException { + String result = null; + int next = read(); + + // The first character must be a parenthesis + if (next == '(') { + StringBuilder buffer = new StringBuilder(); + + while (result == null) { + next = read(); + + if (isCommentText(next)) { + buffer.append((char) next); + } else if (isQuoteCharacter(next)) { + // Start of a quoted pair (escape sequence) + buffer.append((char) read()); + } else if (next == '(') { + // Nested comment + buffer.append('(').append(readComment()).append(')'); + } else if (next == ')') { + // End of comment + result = buffer.toString(); + } else if (next == -1) { + throw new IOException("Unexpected end of comment. Please check your value"); + } else { + throw new IOException( + "Invalid character \"" + + next + + "\" detected in comment. Please check your value"); + } + } + } else { + throw new IOException("A comment must start with a parenthesis"); + } + + return result; + } + + /** + * Reads the next digits. + * + * @return The next digits. + */ + public String readDigits() { + StringBuilder sb = new StringBuilder(); + int next = read(); + + while (isTokenChar(next)) { + sb.append((char) next); + next = read(); + } + + // Unread the last character (separator or end marker) + unread(); + + return sb.toString(); + } + + /** + * Reads the next pair as a parameter. + * + * @param resultClass The named value class to return. + * @return The next pair as a parameter. + * @throws IOException + */ + public > NV readNamedValue(Class resultClass) + throws IOException { + NV result = null; + String name = readToken(); + int nextChar = read(); if (name.isEmpty()) { throw new IOException("Parameter or extension has no name. Please check your value"); @@ -449,251 +461,247 @@ public > NV readNamedValue(Class resultClass) } return result; - } - - /** - * Reads the next pair as a parameter. - * - * @return The next pair as a parameter. - * @throws IOException - */ - public Parameter readParameter() throws IOException { - return readNamedValue(Parameter.class); - } - - /** - * Reads the next quoted string. The first character must be a double quote. - * - * @return The next quoted string. - * @throws IOException - */ - public String readQuotedString() throws IOException { - String result = null; - int next = read(); - - // First character must be a double quote - if (isDoubleQuote(next)) { - StringBuilder buffer = new StringBuilder(); - - while (result == null) { - next = read(); - - if (isQuotedText(next)) { - buffer.append((char) next); - } else if (isQuoteCharacter(next)) { - // Start of a quoted pair (escape sequence) - buffer.append((char) read()); - } else if (isDoubleQuote(next)) { - // End of quoted string - result = buffer.toString(); - } else if (next == -1) { - throw new IOException("Unexpected end of quoted string. Please check your value"); - } else { - throw new IOException( - "Invalid character \"" + next + "\" detected in quoted string. Please check your value"); - } - } - } else { - throw new IOException("A quoted string must start with a double quote"); - } - - return result; - } - - /** - * Read the next text until a space separator is reached. - * - * @return The next text. - */ - public String readRawText() { - // Read value until end or space - StringBuilder sb = null; - int next = read(); - - while ((next != -1) && !isSpace(next) && !isComma(next)) { - if (sb == null) { - sb = new StringBuilder(); - } - - sb.append((char) next); - next = read(); - } - - // Unread the separator - if (isSpace(next) || isComma(next)) { - unread(); - } - - return (sb == null) ? null : sb.toString(); - } - - /** - * Read the next header value of a multi-value header. It skips leading and - * trailing spaces. - * - * @see HTTP - * parsing rule - * - * @return The next header value or null. - */ - public String readRawValue() { - // Skip leading spaces - skipSpaces(); - - // Read value until end or comma - StringBuilder sb = null; - int next = read(); - - while ((next != -1) && !isComma(next)) { - if (sb == null) { - sb = new StringBuilder(); - } - - sb.append((char) next); - next = read(); - } - - // Remove trailing spaces - if (sb != null) { - for (int i = sb.length() - 1; (i >= 0) && isLinearWhiteSpace(sb.charAt(i)); i--) { - sb.deleteCharAt(i); - } - } - - // Unread the separator - if (isComma(next)) { - unread(); - } - - return (sb == null) ? null : sb.toString(); - } - - /** - * Reads the next token. - * - * @return The next token. - */ - public String readToken() { - StringBuilder sb = new StringBuilder(); - int next = read(); - - while (isTokenChar(next)) { - sb.append((char) next); - next = read(); - } - - // Unread the last character (separator or end marker) - unread(); - - return sb.toString(); - } - - /** - * Read the next value. There can be multiple values for a single header. - * Returns null by default. - * - * @return The next value. - */ - public V readValue() throws IOException { - return null; - } - - /** - * Returns a new list with all values added. - * - * @return A new list with all values added. - */ - public List readValues() { - List result = new CopyOnWriteArrayList(); - addValues(result); - return result; - } - - /** - * Repositions this stream to the position at the time the mark - * method was last called on this input stream. - */ - public void reset() { - index = mark; - } - - /** - * Skips the next parameter separator (semi-colon) including leading and - * trailing spaces. - * - * @return True if a separator was effectively skipped. - */ - public boolean skipParameterSeparator() { - boolean result = false; - - // Skip leading spaces - skipSpaces(); - - // Check if next character is a parameter separator - if (isSemiColon(read())) { - result = true; - - // Skip trailing spaces - skipSpaces(); - } else { - // Probably reached the end of the header - unread(); - } - - return result; - } - - /** - * Skips the next spaces. - * - * @return True if spaces were skipped. - */ - public boolean skipSpaces() { - boolean result = false; - int next = peek(); - - while (isLinearWhiteSpace(next) && (next != -1)) { - result = result || isLinearWhiteSpace(next); - read(); - next = peek(); - } - - return result; - } - - /** - * Skips the next value separator (comma) including leading and trailing spaces. - * - * @return True if a separator was effectively skipped. - */ - public boolean skipValueSeparator() { - boolean result = false; - - // Skip leading spaces - skipSpaces(); - - // Check if next character is a value separator - if (isComma(read())) { - result = true; - - // Skip trailing spaces - skipSpaces(); - } else { - // Probably reached the end of the header - unread(); - } - - return result; - } - - /** - * Unreads the last character. - */ - public void unread() { - if (this.index > 0) { - this.index--; - } - } + } + + /** + * Reads the next pair as a parameter. + * + * @return The next pair as a parameter. + * @throws IOException + */ + public Parameter readParameter() throws IOException { + return readNamedValue(Parameter.class); + } + + /** + * Reads the next quoted string. The first character must be a double quote. + * + * @return The next quoted string. + * @throws IOException + */ + public String readQuotedString() throws IOException { + String result = null; + int next = read(); + + // The first character must be a double quote + if (isDoubleQuote(next)) { + StringBuilder buffer = new StringBuilder(); + + while (result == null) { + next = read(); + + if (isQuotedText(next)) { + buffer.append((char) next); + } else if (isQuoteCharacter(next)) { + // Start of a quoted pair (escape sequence) + buffer.append((char) read()); + } else if (isDoubleQuote(next)) { + // End of quoted string + result = buffer.toString(); + } else if (next == -1) { + throw new IOException( + "Unexpected end of quoted string. Please check your value"); + } else { + throw new IOException( + "Invalid character \"" + + next + + "\" detected in quoted string. Please check your value"); + } + } + } else { + throw new IOException("A quoted string must start with a double quote"); + } + + return result; + } + + /** + * Read the next text until a space separator is reached. + * + * @return The next text. + */ + public String readRawText() { + // Read value until end or space + StringBuilder sb = null; + int next = read(); + + while ((next != -1) && !isSpace(next) && !isComma(next)) { + if (sb == null) { + sb = new StringBuilder(); + } + + sb.append((char) next); + next = read(); + } + + // Unread the separator + if (isSpace(next) || isComma(next)) { + unread(); + } + + return (sb == null) ? null : sb.toString(); + } + + /** + * Read the next header value of a multi-value header. It skips leading and trailing spaces. + * + * @see HTTP parsing + * rule + * @return The next header value or null. + */ + public String readRawValue() { + // Skip leading spaces + skipSpaces(); + + // Read value until end or comma + StringBuilder sb = null; + int next = read(); + + while ((next != -1) && !isComma(next)) { + if (sb == null) { + sb = new StringBuilder(); + } + + sb.append((char) next); + next = read(); + } + // Remove trailing spaces + if (sb != null) { + for (int i = sb.length() - 1; (i >= 0) && isLinearWhiteSpace(sb.charAt(i)); i--) { + sb.deleteCharAt(i); + } + } + + // Unread the separator + if (isComma(next)) { + unread(); + } + + return (sb == null) ? null : sb.toString(); + } + + /** + * Reads the next token. + * + * @return The next token. + */ + public String readToken() { + StringBuilder sb = new StringBuilder(); + int next = read(); + + while (isTokenChar(next)) { + sb.append((char) next); + next = read(); + } + + // Unread the last character (separator or end marker) + unread(); + + return sb.toString(); + } + + /** + * Read the next value. There can be multiple values for a single header. Returns null by + * default. + * + * @return The next value. + */ + public V readValue() throws IOException { + return null; + } + + /** + * Returns a new list with all values added. + * + * @return A new list with all values added. + */ + public List readValues() { + List result = new CopyOnWriteArrayList(); + addValues(result); + return result; + } + + /** + * Repositions this stream to the position at the time the mark method was last + * called on this input stream. + */ + public void reset() { + index = mark; + } + + /** + * Skips the next parameter separator (semicolon) including leading and trailing spaces. + * + * @return True if a separator was effectively skipped. + */ + public boolean skipParameterSeparator() { + boolean result = false; + + // Skip leading spaces + skipSpaces(); + + // Check if next character is a parameter separator + if (isSemiColon(read())) { + result = true; + + // Skip trailing spaces + skipSpaces(); + } else { + // Probably reached the end of the header + unread(); + } + + return result; + } + + /** + * Skips the next spaces. + * + * @return True if spaces were skipped. + */ + public boolean skipSpaces() { + boolean result = false; + int next = peek(); + + while (isLinearWhiteSpace(next) && (next != -1)) { + result = result || isLinearWhiteSpace(next); + read(); + next = peek(); + } + + return result; + } + + /** + * Skips the next value separator (comma) including leading and trailing spaces. + * + * @return True if a separator was effectively skipped. + */ + public boolean skipValueSeparator() { + boolean result = false; + + // Skip leading spaces + skipSpaces(); + + // Check if the next character is a value separator + if (isComma(read())) { + result = true; + + // Skip trailing spaces + skipSpaces(); + } else { + // Probably reached the end of the header + unread(); + } + + return result; + } + + /** Unread the last character. */ + public void unread() { + if (this.index > 0) { + this.index--; + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java index 83b73fd68c..1a3564708e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java @@ -1,19 +1,108 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import static java.lang.Boolean.parseBoolean; +import static java.util.logging.Level.WARNING; +import static org.restlet.data.Digest.ALGORITHM_MD5; +import static org.restlet.data.Disposition.TYPE_NONE; +import static org.restlet.data.Method.OPTIONS; +import static org.restlet.data.Range.RANGE_BYTES_UNIT; +import static org.restlet.data.Range.isBytesRange; +import static org.restlet.data.Status.CLIENT_ERROR_METHOD_NOT_ALLOWED; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCEPT; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCEPT_CHARSET; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCEPT_ENCODING; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCEPT_LANGUAGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCEPT_PATCH; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCEPT_RANGES; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_HEADERS; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_METHODS; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_ORIGIN; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_EXPOSE_HEADERS; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_MAX_AGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_REQUEST_HEADERS; +import static org.restlet.engine.header.HeaderConstants.HEADER_ACCESS_CONTROL_REQUEST_METHOD; +import static org.restlet.engine.header.HeaderConstants.HEADER_AGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_ALLOW; +import static org.restlet.engine.header.HeaderConstants.HEADER_AUTHENTICATION_INFO; +import static org.restlet.engine.header.HeaderConstants.HEADER_AUTHORIZATION; +import static org.restlet.engine.header.HeaderConstants.HEADER_CACHE_CONTROL; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONNECTION; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_DISPOSITION; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_ENCODING; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_LANGUAGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_LENGTH; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_LOCATION; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_MD5; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_RANGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_CONTENT_TYPE; +import static org.restlet.engine.header.HeaderConstants.HEADER_COOKIE; +import static org.restlet.engine.header.HeaderConstants.HEADER_DATE; +import static org.restlet.engine.header.HeaderConstants.HEADER_ETAG; +import static org.restlet.engine.header.HeaderConstants.HEADER_EXPECT; +import static org.restlet.engine.header.HeaderConstants.HEADER_EXPIRES; +import static org.restlet.engine.header.HeaderConstants.HEADER_FROM; +import static org.restlet.engine.header.HeaderConstants.HEADER_HOST; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_MATCH; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_MODIFIED_SINCE; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_NONE_MATCH; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_RANGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_IF_UNMODIFIED_SINCE; +import static org.restlet.engine.header.HeaderConstants.HEADER_LAST_MODIFIED; +import static org.restlet.engine.header.HeaderConstants.HEADER_LOCATION; +import static org.restlet.engine.header.HeaderConstants.HEADER_MAX_FORWARDS; +import static org.restlet.engine.header.HeaderConstants.HEADER_PRAGMA; +import static org.restlet.engine.header.HeaderConstants.HEADER_PROXY_AUTHENTICATE; +import static org.restlet.engine.header.HeaderConstants.HEADER_PROXY_AUTHORIZATION; +import static org.restlet.engine.header.HeaderConstants.HEADER_RANGE; +import static org.restlet.engine.header.HeaderConstants.HEADER_REFERRER; +import static org.restlet.engine.header.HeaderConstants.HEADER_RETRY_AFTER; +import static org.restlet.engine.header.HeaderConstants.HEADER_SERVER; +import static org.restlet.engine.header.HeaderConstants.HEADER_SET_COOKIE; +import static org.restlet.engine.header.HeaderConstants.HEADER_SET_COOKIE2; +import static org.restlet.engine.header.HeaderConstants.HEADER_TRAILER; +import static org.restlet.engine.header.HeaderConstants.HEADER_TRANSFER_ENCODING; +import static org.restlet.engine.header.HeaderConstants.HEADER_TRANSFER_EXTENSION; +import static org.restlet.engine.header.HeaderConstants.HEADER_UPGRADE; +import static org.restlet.engine.header.HeaderConstants.HEADER_USER_AGENT; +import static org.restlet.engine.header.HeaderConstants.HEADER_VARY; +import static org.restlet.engine.header.HeaderConstants.HEADER_VIA; +import static org.restlet.engine.header.HeaderConstants.HEADER_WARNING; +import static org.restlet.engine.header.HeaderConstants.HEADER_WWW_AUTHENTICATE; +import static org.restlet.engine.util.StringUtils.isNullOrEmpty; +import static org.restlet.representation.Representation.UNKNOWN_SIZE; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Message; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; +import org.restlet.data.AuthenticationInfo; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ClientInfo; +import org.restlet.data.Conditions; +import org.restlet.data.CookieSetting; +import org.restlet.data.Header; +import org.restlet.data.MediaType; +import org.restlet.data.Range; +import org.restlet.data.Reference; +import org.restlet.data.Tag; import org.restlet.engine.Engine; import org.restlet.engine.util.CaseInsensitiveHashSet; import org.restlet.engine.util.DateUtils; @@ -22,1023 +111,1170 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -import java.io.IOException; -import java.io.OutputStream; -import java.util.*; -import java.util.logging.Level; - -import static java.lang.Boolean.parseBoolean; -import static java.util.logging.Level.WARNING; -import static org.restlet.data.Digest.ALGORITHM_MD5; -import static org.restlet.data.Disposition.TYPE_NONE; -import static org.restlet.data.Method.OPTIONS; -import static org.restlet.data.Range.RANGE_BYTES_UNIT; -import static org.restlet.data.Range.isBytesRange; -import static org.restlet.data.Status.CLIENT_ERROR_METHOD_NOT_ALLOWED; -import static org.restlet.engine.header.HeaderConstants.*; -import static org.restlet.engine.util.StringUtils.isNullOrEmpty; -import static org.restlet.representation.Representation.UNKNOWN_SIZE; - /** * HTTP-style header utilities. - * + * * @author Jerome Louvel */ public class HeaderUtils { - /** - * Standard set of headers which cannot be modified. - */ - private static final Set STANDARD_HEADERS = Collections - .unmodifiableSet(new CaseInsensitiveHashSet(Arrays.asList(HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, - HEADER_ACCESS_CONTROL_ALLOW_HEADERS, HEADER_ACCESS_CONTROL_ALLOW_METHODS, - HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, - HEADER_ACCESS_CONTROL_MAX_AGE, HEADER_ACCESS_CONTROL_REQUEST_HEADERS, - HEADER_ACCESS_CONTROL_REQUEST_METHOD, HEADER_ACCEPT, HEADER_ACCEPT_CHARSET, HEADER_ACCEPT_ENCODING, - HEADER_ACCEPT_LANGUAGE, HEADER_ACCEPT_PATCH, HEADER_ACCEPT_RANGES, HEADER_AGE, HEADER_ALLOW, - HEADER_AUTHENTICATION_INFO, HEADER_AUTHORIZATION, HEADER_CACHE_CONTROL, HEADER_CONNECTION, - HEADER_CONTENT_DISPOSITION, HEADER_CONTENT_ENCODING, HEADER_CONTENT_LANGUAGE, HEADER_CONTENT_LENGTH, - HEADER_CONTENT_LOCATION, HEADER_CONTENT_MD5, HEADER_CONTENT_RANGE, HEADER_CONTENT_TYPE, - HEADER_COOKIE, HEADER_DATE, HEADER_ETAG, HEADER_EXPECT, HEADER_EXPIRES, HEADER_FROM, HEADER_HOST, - HEADER_IF_MATCH, HEADER_IF_MODIFIED_SINCE, HEADER_IF_NONE_MATCH, HEADER_IF_RANGE, - HEADER_IF_UNMODIFIED_SINCE, HEADER_LAST_MODIFIED, HEADER_LOCATION, HEADER_MAX_FORWARDS, - HEADER_PROXY_AUTHENTICATE, HEADER_PROXY_AUTHORIZATION, HEADER_RANGE, HEADER_REFERRER, - HEADER_RETRY_AFTER, HEADER_SERVER, HEADER_SET_COOKIE, HEADER_SET_COOKIE2, HEADER_USER_AGENT, - HEADER_VARY, HEADER_VIA, HEADER_WARNING, HEADER_WWW_AUTHENTICATE))); - - /** - * Set of unsupported headers that will be covered in future versions. - */ - private static final Set UNSUPPORTED_STANDARD_HEADERS = Collections - .unmodifiableSet(new CaseInsensitiveHashSet(Arrays.asList(HEADER_PRAGMA, HEADER_TRAILER, - HEADER_TRANSFER_ENCODING, HEADER_TRANSFER_EXTENSION, HEADER_UPGRADE))); - - /** - * Adds the entity headers based on the {@link Representation} to the - * {@link Series}. - * - * @param entity The source entity {@link Representation}. - * @param headers The target headers {@link Series}. - */ - public static void addEntityHeaders(Representation entity, Series

headers) { - if (entity == null || !entity.isAvailable()) { - addHeader(HEADER_CONTENT_LENGTH, "0", headers); - } else if (entity.getAvailableSize() != UNKNOWN_SIZE) { - addHeader(HEADER_CONTENT_LENGTH, Long.toString(entity.getAvailableSize()), headers); - } - - if (entity != null) { - addHeader(HEADER_CONTENT_ENCODING, EncodingWriter.write(entity.getEncodings()), headers); - addHeader(HEADER_CONTENT_LANGUAGE, LanguageWriter.write(entity.getLanguages()), headers); - - if (entity.getLocationRef() != null) { - addHeader(HEADER_CONTENT_LOCATION, entity.getLocationRef().getTargetRef().toString(), headers); - } - - if (entity.getDigest() != null && ALGORITHM_MD5.equals(entity.getDigest().getAlgorithm())) { - addHeader(HEADER_CONTENT_MD5, - new String(java.util.Base64.getEncoder().encode(entity.getDigest().getValue())), headers); - } - - if (entity.getRange() != null) { - Range range = entity.getRange(); - if (isBytesRange(range)) { - addHeader(HEADER_CONTENT_RANGE, RangeWriter.write(range, entity.getSize()), headers); - } else { - addHeader(HEADER_CONTENT_RANGE, RangeWriter.write(range, range.getInstanceSize()), headers); - } - } - - if (entity.getMediaType() != null) { - addHeader(HEADER_CONTENT_TYPE, ContentType.writeHeader(entity), headers); - } - - if (entity.getExpirationDate() != null) { - addHeader(HEADER_EXPIRES, DateWriter.write(entity.getExpirationDate()), headers); - } - - if (entity.getModificationDate() != null) { - addHeader(HEADER_LAST_MODIFIED, DateWriter.write(entity.getModificationDate()), headers); - } - - if (entity.getTag() != null) { - addHeader(HEADER_ETAG, TagWriter.write(entity.getTag()), headers); - } - - if (entity.getDisposition() != null && !TYPE_NONE.equals(entity.getDisposition().getType())) { - addHeader(HEADER_CONTENT_DISPOSITION, DispositionWriter.write(entity.getDisposition()), headers); - } - } - } - - /** - * Adds extension headers if they are non-standard headers. - * - * @param existingHeaders The headers to update. - * @param additionalHeaders The headers to add. - */ - public static void addExtensionHeaders(Series
existingHeaders, Series
additionalHeaders) { - if (additionalHeaders != null) { - for (Header param : additionalHeaders) { - if (STANDARD_HEADERS.contains(param.getName())) { - // Standard headers that can't be overridden - Context.getCurrentLogger().warning("Addition of the standard header \"" + param.getName() - + "\" is not allowed. Please use the equivalent property in the Restlet API."); - } else if (UNSUPPORTED_STANDARD_HEADERS.contains(param.getName())) { - Context.getCurrentLogger().warning("Addition of the standard header \"" + param.getName() - + "\" is discouraged as a future version of the Restlet API will directly support it."); - existingHeaders.add(param); - } else { - existingHeaders.add(param); - } - } - } - } - - /** - * Adds the general headers from the {@link Message} to the {@link Series}. - * - * @param message The source {@link Message}. - * @param headers The target headers {@link Series}. - */ - public static void addGeneralHeaders(Message message, Series
headers) { - addHeader(HEADER_CACHE_CONTROL, CacheDirectiveWriter.write(message.getCacheDirectives()), headers); - - if (message.getDate() == null) { - message.setDate(new Date()); - } - - addHeader(HEADER_DATE, DateWriter.write(message.getDate()), headers); - - addHeader(HEADER_VIA, RecipientInfoWriter.write(message.getRecipientsInfo()), headers); - - addHeader(HEADER_WARNING, WarningWriter.write(message.getWarnings()), headers); - } - - /** - * Adds a header to the given list. Checks for exceptions and logs them. - * - * @param headerName The header name. - * @param headerValue The header value. - * @param headers The headers list. - */ - public static void addHeader(String headerName, String headerValue, Series
headers) { - if (headerName != null && !isNullOrEmpty(headerValue)) { - try { - headers.add(headerName, headerValue); - } catch (Throwable t) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to format the " + headerName + " header", t); - } - } - } - - /** - * Adds the entity headers based on the {@link Representation} to the - * {@link Series} when a 304 (Not Modified) status is returned. - * - * @param entity The source entity {@link Representation}. - * @param headers The target headers {@link Series}. - */ - public static void addNotModifiedEntityHeaders(Representation entity, Series
headers) { - if (entity != null) { - if (entity.getTag() != null) { - addHeader(HEADER_ETAG, TagWriter.write(entity.getTag()), headers); - } - - if (entity.getLocationRef() != null) { - addHeader(HEADER_CONTENT_LOCATION, entity.getLocationRef().getTargetRef().toString(), headers); - } - } - } - - /** - * Adds the headers based on the {@link Request} to the given {@link Series} . - * - * @param request The {@link Request} to copy the headers from. - * @param headers The {@link Series} to copy the headers to. - */ - public static void addRequestHeaders(Request request, Series
headers) { - ClientInfo clientInfo = request.getClientInfo(); - - if (!clientInfo.getAcceptedMediaTypes().isEmpty()) { - addHeader(HEADER_ACCEPT, PreferenceWriter.write(clientInfo.getAcceptedMediaTypes()), headers); - } else { - addHeader(HEADER_ACCEPT, MediaType.ALL.getName(), headers); - } - - if (!clientInfo.getAcceptedCharacterSets().isEmpty()) { - addHeader(HEADER_ACCEPT_CHARSET, PreferenceWriter.write(clientInfo.getAcceptedCharacterSets()), headers); - } - - if (!clientInfo.getAcceptedEncodings().isEmpty()) { - addHeader(HEADER_ACCEPT_ENCODING, PreferenceWriter.write(clientInfo.getAcceptedEncodings()), headers); - } - - if (!clientInfo.getAcceptedLanguages().isEmpty()) { - addHeader(HEADER_ACCEPT_LANGUAGE, PreferenceWriter.write(clientInfo.getAcceptedLanguages()), headers); - } - - if (!clientInfo.getAcceptedPatches().isEmpty()) { - addHeader(HEADER_ACCEPT_PATCH, PreferenceWriter.write(clientInfo.getAcceptedPatches()), headers); - } - - if (!clientInfo.getExpectations().isEmpty()) { - addHeader(HEADER_EXPECT, ExpectationWriter.write(clientInfo.getExpectations()), headers); - } - - if (clientInfo.getFrom() != null) { - addHeader(HEADER_FROM, request.getClientInfo().getFrom(), headers); - } - - // Manually add the host name and port when it is potentially - // different from the one specified in the target resource reference. - Reference hostRef = (request.getResourceRef().getBaseRef() != null) ? request.getResourceRef().getBaseRef() - : request.getResourceRef(); - - if (hostRef.getHostDomain() != null) { - String host = hostRef.getHostDomain(); - int hostRefPortValue = hostRef.getHostPort(); - - if ((hostRefPortValue != -1) && (hostRefPortValue != request.getProtocol().getDefaultPort())) { - host = host + ':' + hostRefPortValue; - } - - addHeader(HEADER_HOST, host, headers); - } - - Conditions conditions = request.getConditions(); - addHeader(HEADER_IF_MATCH, TagWriter.write(conditions.getMatch()), headers); - addHeader(HEADER_IF_NONE_MATCH, TagWriter.write(conditions.getNoneMatch()), headers); - - if (conditions.getModifiedSince() != null) { - addHeader(HEADER_IF_MODIFIED_SINCE, DateWriter.write(conditions.getModifiedSince()), headers); - } - - if (conditions.getRangeTag() != null && conditions.getRangeDate() != null) { - Context.getCurrentLogger().log(WARNING, - "Unable to format the HTTP If-Range header due to the presence of both entity tag and modification date."); - } else if (conditions.getRangeTag() != null) { - addHeader(HEADER_IF_RANGE, TagWriter.write(conditions.getRangeTag()), headers); - } else if (conditions.getRangeDate() != null) { - addHeader(HEADER_IF_RANGE, DateWriter.write(conditions.getRangeDate()), headers); - } - - if (conditions.getUnmodifiedSince() != null) { - addHeader(HEADER_IF_UNMODIFIED_SINCE, DateWriter.write(conditions.getUnmodifiedSince()), headers); - } - - if (request.getMaxForwards() > -1) { - addHeader(HEADER_MAX_FORWARDS, Integer.toString(request.getMaxForwards()), headers); - } - - if (!request.getRanges().isEmpty()) { - addHeader(HEADER_RANGE, RangeWriter.write(request.getRanges()), headers); - } - - if (request.getReferrerRef() != null) { - addHeader(HEADER_REFERRER, request.getReferrerRef().toString(), headers); - } - - if (request.getClientInfo().getAgent() != null) { - addHeader(HEADER_USER_AGENT, request.getClientInfo().getAgent(), headers); - } else { - addHeader(HEADER_USER_AGENT, Engine.VERSION_HEADER, headers); - } - - if (clientInfo.getExpectations().size() > 0) { - addHeader(HEADER_ACCEPT_ENCODING, PreferenceWriter.write(clientInfo.getAcceptedEncodings()), headers); - } - - // CORS headers - - if (request.getAccessControlRequestHeaders() != null) { - addHeader(HEADER_ACCESS_CONTROL_REQUEST_HEADERS, - StringWriter.write(request.getAccessControlRequestHeaders()), headers); - } - - if (request.getAccessControlRequestMethod() != null) { - addHeader(HEADER_ACCESS_CONTROL_REQUEST_METHOD, request.getAccessControlRequestMethod().getName(), headers); - } - - // ---------------------------------- - // 3) Add supported extension headers - // ---------------------------------- - - if (!request.getCookies().isEmpty()) { - addHeader(HEADER_COOKIE, CookieWriter.write(request.getCookies()), headers); - } - - // ------------------------------------- - // 4) Add user-defined extension headers - // ------------------------------------- - - Series
additionalHeaders = request.getHeaders(); - addExtensionHeaders(headers, additionalHeaders); - - // --------------------------------------- - // 5) Add authorization headers at the end - // --------------------------------------- - - // Add the security headers. NOTE: This must stay at the end because - // the AWS challenge scheme requires access to all HTTP headers - ChallengeResponse challengeResponse = request.getChallengeResponse(); - - if (challengeResponse != null) { - String authHeader = org.restlet.engine.security.AuthenticatorUtils.formatResponse(challengeResponse, - request, headers); - - if (authHeader != null) { - addHeader(HEADER_AUTHORIZATION, authHeader, headers); - } - } - - ChallengeResponse proxyChallengeResponse = request.getProxyChallengeResponse(); - - if (proxyChallengeResponse != null) { - String authHeader = org.restlet.engine.security.AuthenticatorUtils.formatResponse(proxyChallengeResponse, - request, headers); - - if (authHeader != null) { - addHeader(HEADER_PROXY_AUTHORIZATION, authHeader, headers); - } - } - } - - /** - * Adds the headers based on the {@link Response} to the given {@link Series}. - * - * @param response The {@link Response} to copy the headers from. - * @param headers The {@link Series} to copy the headers to. - */ - public static void addResponseHeaders(Response response, Series
headers) { - if (response.getServerInfo().isAcceptingRanges()) { - addHeader(HEADER_ACCEPT_RANGES, RANGE_BYTES_UNIT, headers); - } - - if (response.getAge() > 0) { - addHeader(HEADER_AGE, Integer.toString(response.getAge()), headers); - } - - if (CLIENT_ERROR_METHOD_NOT_ALLOWED.equals(response.getStatus()) - || OPTIONS.equals(response.getRequest().getMethod())) { - addHeader(HEADER_ALLOW, MethodWriter.write(response.getAllowedMethods()), headers); - } - - if (response.getLocationRef() != null) { - // The location header must contain an absolute URI. - addHeader(HEADER_LOCATION, response.getLocationRef().getTargetRef().toString(), headers); - } - - if (response.getProxyChallengeRequests() != null) { - for (ChallengeRequest challengeRequest : response.getProxyChallengeRequests()) { - addHeader(HEADER_PROXY_AUTHENTICATE, org.restlet.engine.security.AuthenticatorUtils - .formatRequest(challengeRequest, response, headers), headers); - } - } - - if (response.getRetryAfter() != null) { - addHeader(HEADER_RETRY_AFTER, DateWriter.write(response.getRetryAfter()), headers); - } - - if ((response.getServerInfo() != null) && (response.getServerInfo().getAgent() != null)) { - addHeader(HEADER_SERVER, response.getServerInfo().getAgent(), headers); - } else { - addHeader(HEADER_SERVER, Engine.VERSION_HEADER, headers); - } - - // Send the Vary header only to none-MSIE user agents as MSIE seems - // to support partially and badly this header (cf issue 261). - if (!((response.getRequest().getClientInfo().getAgent() != null) - && response.getRequest().getClientInfo().getAgent().contains("MSIE"))) { - // Add the Vary header if content negotiation was used - addHeader(HEADER_VARY, DimensionWriter.write(response.getDimensions()), headers); - } - - // Set the security data - if (response.getChallengeRequests() != null) { - for (ChallengeRequest challengeRequest : response.getChallengeRequests()) { - addHeader(HEADER_WWW_AUTHENTICATE, org.restlet.engine.security.AuthenticatorUtils - .formatRequest(challengeRequest, response, headers), headers); - } - } - - // CORS headers - - if (response.getAccessControlAllowCredentials() != null) { - addHeader(HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, response.getAccessControlAllowCredentials().toString(), - headers); - } - - if (response.getAccessControlAllowHeaders() != null) { - addHeader(HEADER_ACCESS_CONTROL_ALLOW_HEADERS, StringWriter.write(response.getAccessControlAllowHeaders()), - headers); - } - if (response.getAccessControlAllowOrigin() != null) { - addHeader(HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, response.getAccessControlAllowOrigin(), headers); - } - - if (response.getAccessControlAllowMethods() != null) { - addHeader(HEADER_ACCESS_CONTROL_ALLOW_METHODS, MethodWriter.write(response.getAccessControlAllowMethods()), - headers); - } - if (response.getAccessControlExposeHeaders() != null) { - addHeader(HeaderConstants.HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, - StringWriter.write(response.getAccessControlExposeHeaders()), headers); - } - if (response.getAccessControlMaxAge() > 0) { - addHeader(HeaderConstants.HEADER_ACCESS_CONTROL_MAX_AGE, - Integer.toString(response.getAccessControlMaxAge()), headers); - } - - // ---------------------------------- - // 3) Add supported extension headers - // ---------------------------------- - - // Add the Authentication-Info header - if (response.getAuthenticationInfo() != null) { - addHeader(HEADER_AUTHENTICATION_INFO, org.restlet.engine.security.AuthenticatorUtils - .formatAuthenticationInfo(response.getAuthenticationInfo()), headers); - } - - // Cookies settings should be written in a single header, but Web - // browsers does not seem to support it. - for (CookieSetting cookieSetting : response.getCookieSettings()) { - addHeader(HEADER_SET_COOKIE, CookieSettingWriter.write(cookieSetting), headers); - } - - // ------------------------------------- - // 4) Add user-defined extension headers - // ------------------------------------- - - Series
additionalHeaders = response.getHeaders(); - addExtensionHeaders(headers, additionalHeaders); - } - - /** - * Remove the headers that are mapped to the framework's API from the given - * message's list of headers. - * - * @param message The message to update. - */ - public static void keepExtensionHeadersOnly(Message message) { - Series
headers = message.getHeaders(); - Series
extensionHeaders = new Series
(Header.class); - for (Header header : headers) { - if (!STANDARD_HEADERS.contains(header.getName())) { - extensionHeaders.add(header); - } - } - message.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, extensionHeaders); - } - - /** - * Copies extension headers into a request or a response. - * - * @param headers The headers to copy. - * @param message The message to update. - */ - public static void copyExtensionHeaders(Series
headers, Message message) { - if (headers != null) { - Series
extensionHeaders = message.getHeaders(); - for (Header header : headers) { - if (!STANDARD_HEADERS.contains(header.getName())) { - extensionHeaders.add(header); - } - } - } - } - - /** - * Copies headers into a response. - * - * @param headers The headers to copy. - * @param response The response to update. - */ - public static void copyResponseTransportHeaders(Series
headers, Response response) { - if (headers != null) { - for (Header header : headers) { - if (HEADER_LOCATION.equalsIgnoreCase(header.getName())) { - response.setLocationRef(header.getValue()); - } else if (HEADER_AGE.equalsIgnoreCase(header.getName())) { - try { - response.setAge(Integer.parseInt(header.getValue())); - } catch (NumberFormatException nfe) { - Context.getCurrentLogger().log(Level.WARNING, - "Error during Age header parsing. Header: " + header.getValue(), nfe); - } - } else if (HEADER_DATE.equalsIgnoreCase(header.getName())) { - Date date = DateUtils.parse(header.getValue()); - - if (date == null) { - date = new Date(); - } - - response.setDate(date); - } else if (HEADER_RETRY_AFTER.equalsIgnoreCase(header.getName())) { - Date retryAfter = DateUtils.parse(header.getValue()); - - if (retryAfter == null) { - // The date might be expressed as a number of seconds - try { - int retryAfterSecs = (int) Double.parseDouble(header.getValue()); - java.util.Calendar calendar = java.util.Calendar.getInstance(); - calendar.add(java.util.Calendar.SECOND, retryAfterSecs); - retryAfter = calendar.getTime(); - } catch (NumberFormatException nfe) { - Context.getCurrentLogger().log(Level.WARNING, - "Error during Retry-After header parsing. Header: " + header.getValue(), nfe); - } - } - - response.setRetryAfter(retryAfter); - } else if (HEADER_SET_COOKIE.equalsIgnoreCase(header.getName()) - || HEADER_SET_COOKIE2.equalsIgnoreCase(header.getName())) { - try { - CookieSettingReader cr = new CookieSettingReader(header.getValue()); - response.getCookieSettings().add(cr.readValue()); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, - "Error during cookie setting parsing. Header: " + header.getValue(), e); - } - } else if (HEADER_WWW_AUTHENTICATE.equalsIgnoreCase(header.getName())) { - List crs = org.restlet.engine.security.AuthenticatorUtils.parseRequest(response, - header.getValue(), headers); - response.getChallengeRequests().addAll(crs); - } else if (HEADER_PROXY_AUTHENTICATE.equalsIgnoreCase(header.getName())) { - List crs = org.restlet.engine.security.AuthenticatorUtils.parseRequest(response, - header.getValue(), headers); - response.getProxyChallengeRequests().addAll(crs); - } else if (HEADER_AUTHENTICATION_INFO.equalsIgnoreCase(header.getName())) { - AuthenticationInfo authenticationInfo = org.restlet.engine.security.AuthenticatorUtils - .parseAuthenticationInfo(header.getValue()); - response.setAuthenticationInfo(authenticationInfo); - } else if (HEADER_SERVER.equalsIgnoreCase(header.getName())) { - response.getServerInfo().setAgent(header.getValue()); - } else if (HEADER_ALLOW.equalsIgnoreCase(header.getName())) { - MethodReader.addValues(header, response.getAllowedMethods()); - } else if (HEADER_VARY.equalsIgnoreCase(header.getName())) { - DimensionReader.addValues(header, response.getDimensions()); - } else if (HEADER_VIA.equalsIgnoreCase(header.getName())) { - RecipientInfoReader.addValues(header, response.getRecipientsInfo()); - } else if (HEADER_WARNING.equalsIgnoreCase(header.getName())) { - WarningReader.addValues(header, response.getWarnings()); - } else if (HEADER_CACHE_CONTROL.equalsIgnoreCase(header.getName())) { - CacheDirectiveReader.addValues(header, response.getCacheDirectives()); - } else if (HEADER_ACCEPT_RANGES.equalsIgnoreCase(header.getName())) { - TokenReader tr = new TokenReader(header.getValue()); - response.getServerInfo().setAcceptingRanges(tr.readValues().contains(RANGE_BYTES_UNIT)); - } else if (HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS.equalsIgnoreCase(header.getName())) { - response.setAccessControlAllowCredentials(parseBoolean(header.getValue())); - StringReader.addValues(header, response.getAccessControlAllowHeaders()); - } else if (HEADER_ACCESS_CONTROL_ALLOW_ORIGIN.equalsIgnoreCase(header.getName())) { - response.setAccessControlAllowOrigin(header.getValue()); - } else if (HEADER_ACCESS_CONTROL_ALLOW_METHODS.equalsIgnoreCase(header.getName())) { - MethodReader.addValues(header, response.getAccessControlAllowMethods()); - } else if (HEADER_ACCESS_CONTROL_MAX_AGE.equalsIgnoreCase(header.getName())) { - response.setAccessControlMaxAge(Integer.parseInt(header.getValue())); - } - } - } - } - - /** - * Extracts entity headers and updates a given representation or create an empty - * one when at least one entity header is present. - * - * @param headers The headers to copy. - * @param representation The representation to update or null. - * @return a representation updated with the given entity headers. - * @throws NumberFormatException - * @see HeaderUtils#copyResponseTransportHeaders(Series, Response) - */ - public static Representation extractEntityHeaders(Iterable
headers, Representation representation) - throws NumberFormatException { - Representation result = (representation == null) ? new EmptyRepresentation() : representation; - boolean entityHeaderFound = false; - - if (headers != null) { - for (Header header : headers) { - if (HEADER_CONTENT_TYPE.equalsIgnoreCase(header.getName())) { - ContentType contentType = new ContentType(header.getValue()); - result.setMediaType(contentType.getMediaType()); - - if ((result.getCharacterSet() == null) || (contentType.getCharacterSet() != null)) { - result.setCharacterSet(contentType.getCharacterSet()); - } - - entityHeaderFound = true; - } else if (HEADER_CONTENT_LENGTH.equalsIgnoreCase(header.getName())) { - entityHeaderFound = true; - } else if (HEADER_EXPIRES.equalsIgnoreCase(header.getName())) { - result.setExpirationDate(HeaderReader.readDate(header.getValue(), false)); - entityHeaderFound = true; - } else if (HEADER_CONTENT_ENCODING.equalsIgnoreCase(header.getName())) { - new EncodingReader(header.getValue()).addValues(result.getEncodings()); - entityHeaderFound = true; - } else if (HEADER_CONTENT_LANGUAGE.equalsIgnoreCase(header.getName())) { - new LanguageReader(header.getValue()).addValues(result.getLanguages()); - entityHeaderFound = true; - } else if (HEADER_LAST_MODIFIED.equalsIgnoreCase(header.getName())) { - result.setModificationDate(HeaderReader.readDate(header.getValue(), false)); - entityHeaderFound = true; - } else if (HEADER_ETAG.equalsIgnoreCase(header.getName())) { - result.setTag(Tag.parse(header.getValue())); - entityHeaderFound = true; - } else if (HEADER_CONTENT_LOCATION.equalsIgnoreCase(header.getName())) { - result.setLocationRef(header.getValue()); - entityHeaderFound = true; - } else if (HEADER_CONTENT_DISPOSITION.equalsIgnoreCase(header.getName())) { - try { - result.setDisposition(new DispositionReader(header.getValue()).readValue()); - entityHeaderFound = true; - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.WARNING, - "Error during Content-Disposition header parsing. Header: " + header.getValue(), ioe); - } - } else if (HEADER_CONTENT_RANGE.equalsIgnoreCase(header.getName())) { - org.restlet.engine.header.RangeReader.update(header.getValue(), result); - entityHeaderFound = true; - } else if (HEADER_CONTENT_MD5.equalsIgnoreCase(header.getName())) { - // Since an MD5 hash is 128 bits long, its base64 encoding - // is 22 bytes if unpadded, or 24 bytes if padded. If the - // header value is unpadded, append two base64 padding - // characters ("==") before passing the value to - // Base64.decode(), which requires its input argument's - // length to be a multiple of four. - String base64hash = header.getValue(); - if (base64hash.length() == 22) { - base64hash += "=="; - } - result.setDigest(new org.restlet.data.Digest(org.restlet.data.Digest.ALGORITHM_MD5, - java.util.Base64.getDecoder().decode(base64hash))); - entityHeaderFound = true; - } - } - } - - // If no representation was initially expected and no entity header - // is found, then do not return any representation - if ((representation == null) && !entityHeaderFound) { - result = null; - } - - return result; - } - - /** - * Returns the content length of the request entity if know, - * {@link Representation#UNKNOWN_SIZE} otherwise. - * - * @return The request content length. - */ - public static long getContentLength(Series
headers) { - long contentLength = UNKNOWN_SIZE; - - if (headers != null) { - // Extract the content length header - for (Header header : headers) { - if (HEADER_CONTENT_LENGTH.equalsIgnoreCase(header.getName())) { - try { - contentLength = Long.parseLong(header.getValue()); - } catch (NumberFormatException e) { - contentLength = UNKNOWN_SIZE; - } - } - } - } - - return contentLength; - } - - /** - * Indicates if the given character is alphabetical (a-z or A-Z). - * - * @param character The character to test. - * @return True if the given character is alphabetical (a-z or A-Z). - */ - public static boolean isAlpha(int character) { - return isUpperCase(character) || isLowerCase(character); - } - - /** - * Indicates if the given character is in ASCII range. - * - * @param character The character to test. - * @return True if the given character is in ASCII range. - */ - public static boolean isAsciiChar(int character) { - return (character >= 0) && (character <= 127); - } - - /** - * Indicates if the given character is a carriage return. - * - * @param character The character to test. - * @return True if the given character is a carriage return. - */ - public static boolean isCarriageReturn(int character) { - return (character == 13); - } - - /** - * Indicates if the entity is chunked. - * - * @return True if the entity is chunked. - */ - public static boolean isChunkedEncoding(Series
headers) { - boolean result = false; - - if (headers != null) { - final String header = headers.getFirstValue(HeaderConstants.HEADER_TRANSFER_ENCODING, true); - result = "chunked".equalsIgnoreCase(header); - } - - return result; - } - - /** - * Indicates if the given character is a comma, the character used as header - * value separator. - * - * @param character The character to test. - * @return True if the given character is a comma. - */ - public static boolean isComma(int character) { - return (character == ','); - } - - /** - * Indicates if the given character is a comment text. It means - * {@link #isText(int)} returns true and the character is not '(' or ')'. - * - * @param character The character to test. - * @return True if the given character is a quoted text. - */ - public static boolean isCommentText(int character) { - return isText(character) && (character != '(') && (character != ')'); - } - - /** - * Indicates if the connection must be closed. - * - * @param headers The headers to test. - * @return True if the connection must be closed. - */ - public static boolean isConnectionClose(Series
headers) { - boolean result = false; - - if (headers != null) { - String header = headers.getFirstValue(HeaderConstants.HEADER_CONNECTION, true); - result = "close".equalsIgnoreCase(header); - } - - return result; - } - - /** - * Indicates if the given character is a control character. - * - * @param character The character to test. - * @return True if the given character is a control character. - */ - public static boolean isControlChar(int character) { - return ((character >= 0) && (character <= 31)) || (character == 127); - } - - /** - * Indicates if the given character is a digit (0-9). - * - * @param character The character to test. - * @return True if the given character is a digit (0-9). - */ - public static boolean isDigit(int character) { - return (character >= '0') && (character <= '9'); - } - - /** - * Indicates if the given character is a double quote. - * - * @param character The character to test. - * @return True if the given character is a double quote. - */ - public static boolean isDoubleQuote(int character) { - return (character == 34); - } - - /** - * Indicates if the given character is an horizontal tab. - * - * @param character The character to test. - * @return True if the given character is an horizontal tab. - */ - public static boolean isHorizontalTab(int character) { - return (character == 9); - } - - /** - * Indicates if the given character is in ISO Latin 1 (8859-1) range. Note that - * this range is a superset of ASCII and a subrange of Unicode (UTF-8). - * - * @param character The character to test. - * @return True if the given character is in ISO Latin 1 range. - */ - public static boolean isLatin1Char(int character) { - return (character >= 0) && (character <= 255); - } - - /** - * Indicates if the given character is a value separator. - * - * @param character The character to test. - * @return True if the given character is a value separator. - */ - public static boolean isLinearWhiteSpace(int character) { - return (isCarriageReturn(character) || isSpace(character) || isLineFeed(character) - || HeaderUtils.isHorizontalTab(character)); - } - - /** - * Indicates if the given character is a line feed. - * - * @param character The character to test. - * @return True if the given character is a line feed. - */ - public static boolean isLineFeed(int character) { - return (character == 10); - } - - /** - * Indicates if the given character is lower case (a-z). - * - * @param character The character to test. - * @return True if the given character is lower case (a-z). - */ - public static boolean isLowerCase(int character) { - return (character >= 'a') && (character <= 'z'); - } - - /** - * Indicates if the given character marks the start of a quoted pair. - * - * @param character The character to test. - * @return True if the given character marks the start of a quoted pair. - */ - public static boolean isQuoteCharacter(int character) { - return (character == '\\'); - } - - /** - * Indicates if the given character is a quoted text. It means - * {@link #isText(int)} returns true and {@link #isDoubleQuote(int)} returns - * false. - * - * @param character The character to test. - * @return True if the given character is a quoted text. - */ - public static boolean isQuotedText(int character) { - return isText(character) && !isDoubleQuote(character); - } - - /** - * Indicates if the given character is a semicolon, the character used as header - * parameter separator. - * - * @param character The character to test. - * @return True if the given character is a semicolon. - */ - public static boolean isSemiColon(int character) { - return (character == ';'); - } - - /** - * Indicates if the given character is a separator. - * - * @param character The character to test. - * @return True if the given character is a separator. - */ - public static boolean isSeparator(int character) { - switch (character) { - case '(': - case ')': - case '<': - case '>': - case '@': - case ',': - case ';': - case ':': - case '\\': - case '"': - case '/': - case '[': - case ']': - case '?': - case '=': - case '{': - case '}': - case ' ': - case '\t': - return true; - - default: - return false; - } - } - - /** - * Indicates if the given character is a space. - * - * @param character The character to test. - * @return True if the given character is a space. - */ - public static boolean isSpace(int character) { - return (character == 32); - } - - /** - * Indicates if the given character is textual (ISO Latin 1 and not a control - * character). - * - * @param character The character to test. - * @return True if the given character is textual. - */ - public static boolean isText(int character) { - return isLatin1Char(character) && !isControlChar(character); - } - - /** - * Indicates if the token is valid.
- * Only contains valid token characters. - * - * @param token The token to check - * @return True if the token is valid. - */ - public static boolean isToken(CharSequence token) { - for (int i = 0; i < token.length(); i++) { - if (!isTokenChar(token.charAt(i))) { - return false; - } - } - - return true; - } - - /** - * Indicates if the given character is a token character (text and not a - * separator). - * - * @param character The character to test. - * @return True if the given character is a token character (text and not a - * separator). - */ - public static boolean isTokenChar(int character) { - return isAsciiChar(character) && !isSeparator(character); - } - - /** - * Indicates if the given character is upper case (A-Z). - * - * @param character The character to test. - * @return True if the given character is upper case (A-Z). - */ - public static boolean isUpperCase(int character) { - return (character >= 'A') && (character <= 'Z'); - } - - /** - * Writes a new line. - * - * @param os The output stream. - * @throws IOException - */ - public static void writeCRLF(OutputStream os) throws IOException { - os.write(13); // CR - os.write(10); // LF - } - - /** - * Writes a header line. - * - * @param header The header to write. - * @param os The output stream. - * @throws IOException - */ - public static void writeHeaderLine(Header header, OutputStream os) throws IOException { - os.write(StringUtils.getAsciiBytes(header.getName())); - os.write(':'); - os.write(' '); - - if (header.getValue() != null) { - os.write(StringUtils.getLatin1Bytes(header.getValue())); - } - - os.write(13); // CR - os.write(10); // LF - } - - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private HeaderUtils() { - } + /** Standard set of headers which cannot be modified. */ + private static final Set STANDARD_HEADERS = + Collections.unmodifiableSet( + new CaseInsensitiveHashSet( + Arrays.asList( + HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, + HEADER_ACCESS_CONTROL_ALLOW_HEADERS, + HEADER_ACCESS_CONTROL_ALLOW_METHODS, + HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, + HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, + HEADER_ACCESS_CONTROL_MAX_AGE, + HEADER_ACCESS_CONTROL_REQUEST_HEADERS, + HEADER_ACCESS_CONTROL_REQUEST_METHOD, + HEADER_ACCEPT, + HEADER_ACCEPT_CHARSET, + HEADER_ACCEPT_ENCODING, + HEADER_ACCEPT_LANGUAGE, + HEADER_ACCEPT_PATCH, + HEADER_ACCEPT_RANGES, + HEADER_AGE, + HEADER_ALLOW, + HEADER_AUTHENTICATION_INFO, + HEADER_AUTHORIZATION, + HEADER_CACHE_CONTROL, + HEADER_CONNECTION, + HEADER_CONTENT_DISPOSITION, + HEADER_CONTENT_ENCODING, + HEADER_CONTENT_LANGUAGE, + HEADER_CONTENT_LENGTH, + HEADER_CONTENT_LOCATION, + HEADER_CONTENT_MD5, + HEADER_CONTENT_RANGE, + HEADER_CONTENT_TYPE, + HEADER_COOKIE, + HEADER_DATE, + HEADER_ETAG, + HEADER_EXPECT, + HEADER_EXPIRES, + HEADER_FROM, + HEADER_HOST, + HEADER_IF_MATCH, + HEADER_IF_MODIFIED_SINCE, + HEADER_IF_NONE_MATCH, + HEADER_IF_RANGE, + HEADER_IF_UNMODIFIED_SINCE, + HEADER_LAST_MODIFIED, + HEADER_LOCATION, + HEADER_MAX_FORWARDS, + HEADER_PROXY_AUTHENTICATE, + HEADER_PROXY_AUTHORIZATION, + HEADER_RANGE, + HEADER_REFERRER, + HEADER_RETRY_AFTER, + HEADER_SERVER, + HEADER_SET_COOKIE, + HEADER_SET_COOKIE2, + HEADER_USER_AGENT, + HEADER_VARY, + HEADER_VIA, + HEADER_WARNING, + HEADER_WWW_AUTHENTICATE))); + + /** Set of unsupported headers that will be covered in future versions. */ + private static final Set UNSUPPORTED_STANDARD_HEADERS = + Collections.unmodifiableSet( + new CaseInsensitiveHashSet( + Arrays.asList( + HEADER_PRAGMA, + HEADER_TRAILER, + HEADER_TRANSFER_ENCODING, + HEADER_TRANSFER_EXTENSION, + HEADER_UPGRADE))); + + /** + * Adds the entity headers based on the {@link Representation} to the {@link Series}. + * + * @param entity The source entity {@link Representation}. + * @param headers The target headers {@link Series}. + */ + public static void addEntityHeaders(Representation entity, Series
headers) { + if (entity == null || !entity.isAvailable()) { + addHeader(HEADER_CONTENT_LENGTH, "0", headers); + } else if (entity.getAvailableSize() != UNKNOWN_SIZE) { + addHeader(HEADER_CONTENT_LENGTH, Long.toString(entity.getAvailableSize()), headers); + } + + if (entity != null) { + addHeader( + HEADER_CONTENT_ENCODING, EncodingWriter.write(entity.getEncodings()), headers); + addHeader( + HEADER_CONTENT_LANGUAGE, LanguageWriter.write(entity.getLanguages()), headers); + + if (entity.getLocationRef() != null) { + addHeader( + HEADER_CONTENT_LOCATION, + entity.getLocationRef().getTargetRef().toString(), + headers); + } + + if (entity.getDigest() != null + && ALGORITHM_MD5.equals(entity.getDigest().getAlgorithm())) { + addHeader( + HEADER_CONTENT_MD5, + new String( + java.util.Base64.getEncoder() + .encode(entity.getDigest().getValue())), + headers); + } + + if (entity.getRange() != null) { + Range range = entity.getRange(); + if (isBytesRange(range)) { + addHeader( + HEADER_CONTENT_RANGE, + RangeWriter.write(range, entity.getSize()), + headers); + } else { + addHeader( + HEADER_CONTENT_RANGE, + RangeWriter.write(range, range.getInstanceSize()), + headers); + } + } + + if (entity.getMediaType() != null) { + addHeader(HEADER_CONTENT_TYPE, ContentType.writeHeader(entity), headers); + } + + if (entity.getExpirationDate() != null) { + addHeader(HEADER_EXPIRES, DateWriter.write(entity.getExpirationDate()), headers); + } + + if (entity.getModificationDate() != null) { + addHeader( + HEADER_LAST_MODIFIED, + DateWriter.write(entity.getModificationDate()), + headers); + } + + if (entity.getTag() != null) { + addHeader(HEADER_ETAG, TagWriter.write(entity.getTag()), headers); + } + + if (entity.getDisposition() != null + && !TYPE_NONE.equals(entity.getDisposition().getType())) { + addHeader( + HEADER_CONTENT_DISPOSITION, + DispositionWriter.write(entity.getDisposition()), + headers); + } + } + } + + /** + * Adds extension headers if they are non-standard headers. + * + * @param existingHeaders The headers to update. + * @param additionalHeaders The headers to add. + */ + public static void addExtensionHeaders( + Series
existingHeaders, Series
additionalHeaders) { + if (additionalHeaders != null) { + for (Header param : additionalHeaders) { + if (STANDARD_HEADERS.contains(param.getName())) { + // Standard headers that can't be overridden + Context.getCurrentLogger() + .warning( + "Addition of the standard header \"" + + param.getName() + + "\" is not allowed. Please use the equivalent property in the Restlet API."); + } else if (UNSUPPORTED_STANDARD_HEADERS.contains(param.getName())) { + Context.getCurrentLogger() + .warning( + "Addition of the standard header \"" + + param.getName() + + "\" is discouraged as a future version of the Restlet API will directly support it."); + existingHeaders.add(param); + } else { + existingHeaders.add(param); + } + } + } + } + + /** + * Adds the general headers from the {@link Message} to the {@link Series}. + * + * @param message The source {@link Message}. + * @param headers The target headers {@link Series}. + */ + public static void addGeneralHeaders(Message message, Series
headers) { + addHeader( + HEADER_CACHE_CONTROL, + CacheDirectiveWriter.write(message.getCacheDirectives()), + headers); + + if (message.getDate() == null) { + message.setDate(new Date()); + } + + addHeader(HEADER_DATE, DateWriter.write(message.getDate()), headers); + + addHeader(HEADER_VIA, RecipientInfoWriter.write(message.getRecipientsInfo()), headers); + + addHeader(HEADER_WARNING, WarningWriter.write(message.getWarnings()), headers); + } + + /** + * Adds a header to the given list. Checks for exceptions and logs them. + * + * @param headerName The header name. + * @param headerValue The header value. + * @param headers The headers list. + */ + public static void addHeader(String headerName, String headerValue, Series
headers) { + if (headerName != null && !isNullOrEmpty(headerValue)) { + try { + headers.add(headerName, headerValue); + } catch (Throwable t) { + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to format the " + headerName + " header", t); + } + } + } + + /** + * Adds the entity headers based on the {@link Representation} to the {@link Series} when a 304 + * (Not Modified) status is returned. + * + * @param entity The source entity {@link Representation}. + * @param headers The target headers {@link Series}. + */ + public static void addNotModifiedEntityHeaders(Representation entity, Series
headers) { + if (entity != null) { + if (entity.getTag() != null) { + addHeader(HEADER_ETAG, TagWriter.write(entity.getTag()), headers); + } + + if (entity.getLocationRef() != null) { + addHeader( + HEADER_CONTENT_LOCATION, + entity.getLocationRef().getTargetRef().toString(), + headers); + } + } + } + + /** + * Adds the headers based on the {@link Request} to the given {@link Series} . + * + * @param request The {@link Request} to copy the headers from. + * @param headers The {@link Series} to copy the headers to. + */ + public static void addRequestHeaders(Request request, Series
headers) { + ClientInfo clientInfo = request.getClientInfo(); + + if (!clientInfo.getAcceptedMediaTypes().isEmpty()) { + addHeader( + HEADER_ACCEPT, + PreferenceWriter.write(clientInfo.getAcceptedMediaTypes()), + headers); + } else { + addHeader(HEADER_ACCEPT, MediaType.ALL.getName(), headers); + } + + if (!clientInfo.getAcceptedCharacterSets().isEmpty()) { + addHeader( + HEADER_ACCEPT_CHARSET, + PreferenceWriter.write(clientInfo.getAcceptedCharacterSets()), + headers); + } + + if (!clientInfo.getAcceptedEncodings().isEmpty()) { + addHeader( + HEADER_ACCEPT_ENCODING, + PreferenceWriter.write(clientInfo.getAcceptedEncodings()), + headers); + } + + if (!clientInfo.getAcceptedLanguages().isEmpty()) { + addHeader( + HEADER_ACCEPT_LANGUAGE, + PreferenceWriter.write(clientInfo.getAcceptedLanguages()), + headers); + } + + if (!clientInfo.getAcceptedPatches().isEmpty()) { + addHeader( + HEADER_ACCEPT_PATCH, + PreferenceWriter.write(clientInfo.getAcceptedPatches()), + headers); + } + + if (!clientInfo.getExpectations().isEmpty()) { + addHeader( + HEADER_EXPECT, ExpectationWriter.write(clientInfo.getExpectations()), headers); + } + + if (clientInfo.getFrom() != null) { + addHeader(HEADER_FROM, request.getClientInfo().getFrom(), headers); + } + + // Manually add the host name and port when it is potentially + // different from the one specified in the target resource reference. + Reference hostRef = + (request.getResourceRef().getBaseRef() != null) + ? request.getResourceRef().getBaseRef() + : request.getResourceRef(); + + if (hostRef.getHostDomain() != null) { + String host = hostRef.getHostDomain(); + int hostRefPortValue = hostRef.getHostPort(); + + if ((hostRefPortValue != -1) + && (hostRefPortValue != request.getProtocol().getDefaultPort())) { + host = host + ':' + hostRefPortValue; + } + + addHeader(HEADER_HOST, host, headers); + } + + Conditions conditions = request.getConditions(); + addHeader(HEADER_IF_MATCH, TagWriter.write(conditions.getMatch()), headers); + addHeader(HEADER_IF_NONE_MATCH, TagWriter.write(conditions.getNoneMatch()), headers); + + if (conditions.getModifiedSince() != null) { + addHeader( + HEADER_IF_MODIFIED_SINCE, + DateWriter.write(conditions.getModifiedSince()), + headers); + } + + if (conditions.getRangeTag() != null && conditions.getRangeDate() != null) { + Context.getCurrentLogger() + .log( + WARNING, + "Unable to format the HTTP If-Range header due to the presence of both entity tag and modification date."); + } else if (conditions.getRangeTag() != null) { + addHeader(HEADER_IF_RANGE, TagWriter.write(conditions.getRangeTag()), headers); + } else if (conditions.getRangeDate() != null) { + addHeader(HEADER_IF_RANGE, DateWriter.write(conditions.getRangeDate()), headers); + } + + if (conditions.getUnmodifiedSince() != null) { + addHeader( + HEADER_IF_UNMODIFIED_SINCE, + DateWriter.write(conditions.getUnmodifiedSince()), + headers); + } + + if (request.getMaxForwards() > -1) { + addHeader(HEADER_MAX_FORWARDS, Integer.toString(request.getMaxForwards()), headers); + } + + if (!request.getRanges().isEmpty()) { + addHeader(HEADER_RANGE, RangeWriter.write(request.getRanges()), headers); + } + + if (request.getReferrerRef() != null) { + addHeader(HEADER_REFERRER, request.getReferrerRef().toString(), headers); + } + + if (request.getClientInfo().getAgent() != null) { + addHeader(HEADER_USER_AGENT, request.getClientInfo().getAgent(), headers); + } else { + addHeader(HEADER_USER_AGENT, Engine.VERSION_HEADER, headers); + } + + if (!clientInfo.getExpectations().isEmpty()) { + addHeader( + HEADER_ACCEPT_ENCODING, + PreferenceWriter.write(clientInfo.getAcceptedEncodings()), + headers); + } + + // CORS headers + + if (request.getAccessControlRequestHeaders() != null) { + addHeader( + HEADER_ACCESS_CONTROL_REQUEST_HEADERS, + StringWriter.write(request.getAccessControlRequestHeaders()), + headers); + } + + if (request.getAccessControlRequestMethod() != null) { + addHeader( + HEADER_ACCESS_CONTROL_REQUEST_METHOD, + request.getAccessControlRequestMethod().getName(), + headers); + } + + // ---------------------------------- + // 3) Add supported extension headers + // ---------------------------------- + + if (!request.getCookies().isEmpty()) { + addHeader(HEADER_COOKIE, CookieWriter.write(request.getCookies()), headers); + } + + // ------------------------------------- + // 4) Add user-defined extension headers + // ------------------------------------- + + Series
additionalHeaders = request.getHeaders(); + addExtensionHeaders(headers, additionalHeaders); + + // --------------------------------------- + // 5) Add authorization headers at the end + // --------------------------------------- + + // Add the security headers. NOTE: This must stay at the end because + // the AWS challenge scheme requires access to all HTTP headers + ChallengeResponse challengeResponse = request.getChallengeResponse(); + + if (challengeResponse != null) { + String authHeader = + org.restlet.engine.security.AuthenticatorUtils.formatResponse( + challengeResponse, request, headers); + + if (authHeader != null) { + addHeader(HEADER_AUTHORIZATION, authHeader, headers); + } + } + + ChallengeResponse proxyChallengeResponse = request.getProxyChallengeResponse(); + + if (proxyChallengeResponse != null) { + String authHeader = + org.restlet.engine.security.AuthenticatorUtils.formatResponse( + proxyChallengeResponse, request, headers); + + if (authHeader != null) { + addHeader(HEADER_PROXY_AUTHORIZATION, authHeader, headers); + } + } + } + + /** + * Adds the headers based on the {@link Response} to the given {@link Series}. + * + * @param response The {@link Response} to copy the headers from. + * @param headers The {@link Series} to copy the headers to. + */ + public static void addResponseHeaders(Response response, Series
headers) { + if (response.getServerInfo().isAcceptingRanges()) { + addHeader(HEADER_ACCEPT_RANGES, RANGE_BYTES_UNIT, headers); + } + + if (response.getAge() > 0) { + addHeader(HEADER_AGE, Integer.toString(response.getAge()), headers); + } + + if (CLIENT_ERROR_METHOD_NOT_ALLOWED.equals(response.getStatus()) + || OPTIONS.equals(response.getRequest().getMethod())) { + addHeader(HEADER_ALLOW, MethodWriter.write(response.getAllowedMethods()), headers); + } + + if (response.getLocationRef() != null) { + // The location header must contain an absolute URI. + addHeader( + HEADER_LOCATION, response.getLocationRef().getTargetRef().toString(), headers); + } + + if (response.getProxyChallengeRequests() != null) { + for (ChallengeRequest challengeRequest : response.getProxyChallengeRequests()) { + addHeader( + HEADER_PROXY_AUTHENTICATE, + org.restlet.engine.security.AuthenticatorUtils.formatRequest( + challengeRequest, response, headers), + headers); + } + } + + if (response.getRetryAfter() != null) { + addHeader(HEADER_RETRY_AFTER, DateWriter.write(response.getRetryAfter()), headers); + } + + if ((response.getServerInfo() != null) && (response.getServerInfo().getAgent() != null)) { + addHeader(HEADER_SERVER, response.getServerInfo().getAgent(), headers); + } else { + addHeader(HEADER_SERVER, Engine.VERSION_HEADER, headers); + } + + // Send the "Vary" header only to none-MSIE user agents as MSIE seems + // to partially and badly support this header (cf. issue 261). + if (!((response.getRequest().getClientInfo().getAgent() != null) + && response.getRequest().getClientInfo().getAgent().contains("MSIE"))) { + // Add the "Vary" header if content negotiation was used + addHeader(HEADER_VARY, DimensionWriter.write(response.getDimensions()), headers); + } + + // Set the security data + if (response.getChallengeRequests() != null) { + for (ChallengeRequest challengeRequest : response.getChallengeRequests()) { + addHeader( + HEADER_WWW_AUTHENTICATE, + org.restlet.engine.security.AuthenticatorUtils.formatRequest( + challengeRequest, response, headers), + headers); + } + } + + // CORS headers + + if (response.getAccessControlAllowCredentials() != null) { + addHeader( + HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, + response.getAccessControlAllowCredentials().toString(), + headers); + } + + if (response.getAccessControlAllowHeaders() != null) { + addHeader( + HEADER_ACCESS_CONTROL_ALLOW_HEADERS, + StringWriter.write(response.getAccessControlAllowHeaders()), + headers); + } + if (response.getAccessControlAllowOrigin() != null) { + addHeader( + HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, + response.getAccessControlAllowOrigin(), + headers); + } + + if (response.getAccessControlAllowMethods() != null) { + addHeader( + HEADER_ACCESS_CONTROL_ALLOW_METHODS, + MethodWriter.write(response.getAccessControlAllowMethods()), + headers); + } + if (response.getAccessControlExposeHeaders() != null) { + addHeader( + HeaderConstants.HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, + StringWriter.write(response.getAccessControlExposeHeaders()), + headers); + } + if (response.getAccessControlMaxAge() > 0) { + addHeader( + HeaderConstants.HEADER_ACCESS_CONTROL_MAX_AGE, + Integer.toString(response.getAccessControlMaxAge()), + headers); + } + + // ---------------------------------- + // 3) Add supported extension headers + // ---------------------------------- + + // Add the Authentication-Info header + if (response.getAuthenticationInfo() != null) { + addHeader( + HEADER_AUTHENTICATION_INFO, + org.restlet.engine.security.AuthenticatorUtils.formatAuthenticationInfo( + response.getAuthenticationInfo()), + headers); + } + + // Cookies settings should be written in a single header, but Web + // browsers do not seem to support it. + for (CookieSetting cookieSetting : response.getCookieSettings()) { + addHeader(HEADER_SET_COOKIE, CookieSettingWriter.write(cookieSetting), headers); + } + + // ------------------------------------- + // 4) Add user-defined extension headers + // ------------------------------------- + + Series
additionalHeaders = response.getHeaders(); + addExtensionHeaders(headers, additionalHeaders); + } + + /** + * Remove the headers that are mapped to the framework's API from the given message's list of + * headers. + * + * @param message The message to update. + */ + public static void keepExtensionHeadersOnly(Message message) { + Series
headers = message.getHeaders(); + Series
extensionHeaders = new Series
(Header.class); + for (Header header : headers) { + if (!STANDARD_HEADERS.contains(header.getName())) { + extensionHeaders.add(header); + } + } + message.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, extensionHeaders); + } + + /** + * Copies extension headers into a request or a response. + * + * @param headers The headers to copy. + * @param message The message to update. + */ + public static void copyExtensionHeaders(Series
headers, Message message) { + if (headers != null) { + Series
extensionHeaders = message.getHeaders(); + for (Header header : headers) { + if (!STANDARD_HEADERS.contains(header.getName())) { + extensionHeaders.add(header); + } + } + } + } + + /** + * Copies headers into a response. + * + * @param headers The headers to copy. + * @param response The response to update. + */ + public static void copyResponseTransportHeaders(Series
headers, Response response) { + if (headers != null) { + for (Header header : headers) { + if (HEADER_LOCATION.equalsIgnoreCase(header.getName())) { + response.setLocationRef(header.getValue()); + } else if (HEADER_AGE.equalsIgnoreCase(header.getName())) { + try { + response.setAge(Integer.parseInt(header.getValue())); + } catch (NumberFormatException nfe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Error during Age header parsing. Header: " + + header.getValue(), + nfe); + } + } else if (HEADER_DATE.equalsIgnoreCase(header.getName())) { + Date date = DateUtils.parse(header.getValue()); + + if (date == null) { + date = new Date(); + } + + response.setDate(date); + } else if (HEADER_RETRY_AFTER.equalsIgnoreCase(header.getName())) { + Date retryAfter = DateUtils.parse(header.getValue()); + + if (retryAfter == null) { + // The date might be expressed as a number of seconds + try { + int retryAfterSecs = (int) Double.parseDouble(header.getValue()); + java.util.Calendar calendar = java.util.Calendar.getInstance(); + calendar.add(java.util.Calendar.SECOND, retryAfterSecs); + retryAfter = calendar.getTime(); + } catch (NumberFormatException nfe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Error during Retry-After header parsing. Header: " + + header.getValue(), + nfe); + } + } + + response.setRetryAfter(retryAfter); + } else if (HEADER_SET_COOKIE.equalsIgnoreCase(header.getName()) + || HEADER_SET_COOKIE2.equalsIgnoreCase(header.getName())) { + try { + CookieSettingReader cr = new CookieSettingReader(header.getValue()); + response.getCookieSettings().add(cr.readValue()); + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Error during cookie setting parsing. Header: " + + header.getValue(), + e); + } + } else if (HEADER_WWW_AUTHENTICATE.equalsIgnoreCase(header.getName())) { + List crs = + org.restlet.engine.security.AuthenticatorUtils.parseRequest( + response, header.getValue(), headers); + response.getChallengeRequests().addAll(crs); + } else if (HEADER_PROXY_AUTHENTICATE.equalsIgnoreCase(header.getName())) { + List crs = + org.restlet.engine.security.AuthenticatorUtils.parseRequest( + response, header.getValue(), headers); + response.getProxyChallengeRequests().addAll(crs); + } else if (HEADER_AUTHENTICATION_INFO.equalsIgnoreCase(header.getName())) { + AuthenticationInfo authenticationInfo = + org.restlet.engine.security.AuthenticatorUtils.parseAuthenticationInfo( + header.getValue()); + response.setAuthenticationInfo(authenticationInfo); + } else if (HEADER_SERVER.equalsIgnoreCase(header.getName())) { + response.getServerInfo().setAgent(header.getValue()); + } else if (HEADER_ALLOW.equalsIgnoreCase(header.getName())) { + MethodReader.addValues(header, response.getAllowedMethods()); + } else if (HEADER_VARY.equalsIgnoreCase(header.getName())) { + DimensionReader.addValues(header, response.getDimensions()); + } else if (HEADER_VIA.equalsIgnoreCase(header.getName())) { + RecipientInfoReader.addValues(header, response.getRecipientsInfo()); + } else if (HEADER_WARNING.equalsIgnoreCase(header.getName())) { + WarningReader.addValues(header, response.getWarnings()); + } else if (HEADER_CACHE_CONTROL.equalsIgnoreCase(header.getName())) { + CacheDirectiveReader.addValues(header, response.getCacheDirectives()); + } else if (HEADER_ACCEPT_RANGES.equalsIgnoreCase(header.getName())) { + TokenReader tr = new TokenReader(header.getValue()); + response.getServerInfo() + .setAcceptingRanges(tr.readValues().contains(RANGE_BYTES_UNIT)); + } else if (HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS.equalsIgnoreCase( + header.getName())) { + response.setAccessControlAllowCredentials(parseBoolean(header.getValue())); + StringReader.addValues(header, response.getAccessControlAllowHeaders()); + } else if (HEADER_ACCESS_CONTROL_ALLOW_ORIGIN.equalsIgnoreCase(header.getName())) { + response.setAccessControlAllowOrigin(header.getValue()); + } else if (HEADER_ACCESS_CONTROL_ALLOW_METHODS.equalsIgnoreCase(header.getName())) { + MethodReader.addValues(header, response.getAccessControlAllowMethods()); + } else if (HEADER_ACCESS_CONTROL_MAX_AGE.equalsIgnoreCase(header.getName())) { + response.setAccessControlMaxAge(Integer.parseInt(header.getValue())); + } + } + } + } + + /** + * Extracts entity headers and updates a given representation or create an empty one when at + * least one entity header is present. + * + * @param headers The headers to copy. + * @param representation The representation to update or null. + * @return a representation updated with the given entity headers. + * @throws NumberFormatException + * @see HeaderUtils#copyResponseTransportHeaders(Series, Response) + */ + public static Representation extractEntityHeaders( + Iterable
headers, Representation representation) throws NumberFormatException { + Representation result = + (representation == null) ? new EmptyRepresentation() : representation; + boolean entityHeaderFound = false; + + if (headers != null) { + for (Header header : headers) { + if (HEADER_CONTENT_TYPE.equalsIgnoreCase(header.getName())) { + ContentType contentType = new ContentType(header.getValue()); + result.setMediaType(contentType.getMediaType()); + + if ((result.getCharacterSet() == null) + || (contentType.getCharacterSet() != null)) { + result.setCharacterSet(contentType.getCharacterSet()); + } + + entityHeaderFound = true; + } else if (HEADER_CONTENT_LENGTH.equalsIgnoreCase(header.getName())) { + entityHeaderFound = true; + } else if (HEADER_EXPIRES.equalsIgnoreCase(header.getName())) { + result.setExpirationDate(HeaderReader.readDate(header.getValue(), false)); + entityHeaderFound = true; + } else if (HEADER_CONTENT_ENCODING.equalsIgnoreCase(header.getName())) { + new EncodingReader(header.getValue()).addValues(result.getEncodings()); + entityHeaderFound = true; + } else if (HEADER_CONTENT_LANGUAGE.equalsIgnoreCase(header.getName())) { + new LanguageReader(header.getValue()).addValues(result.getLanguages()); + entityHeaderFound = true; + } else if (HEADER_LAST_MODIFIED.equalsIgnoreCase(header.getName())) { + result.setModificationDate(HeaderReader.readDate(header.getValue(), false)); + entityHeaderFound = true; + } else if (HEADER_ETAG.equalsIgnoreCase(header.getName())) { + result.setTag(Tag.parse(header.getValue())); + entityHeaderFound = true; + } else if (HEADER_CONTENT_LOCATION.equalsIgnoreCase(header.getName())) { + result.setLocationRef(header.getValue()); + entityHeaderFound = true; + } else if (HEADER_CONTENT_DISPOSITION.equalsIgnoreCase(header.getName())) { + try { + result.setDisposition(new DispositionReader(header.getValue()).readValue()); + entityHeaderFound = true; + } catch (IOException ioe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Error during Content-Disposition header parsing. Header: " + + header.getValue(), + ioe); + } + } else if (HEADER_CONTENT_RANGE.equalsIgnoreCase(header.getName())) { + org.restlet.engine.header.RangeReader.update(header.getValue(), result); + entityHeaderFound = true; + } else if (HEADER_CONTENT_MD5.equalsIgnoreCase(header.getName())) { + // Since an MD5 hash is 128 bits long, its base64 encoding + // is 22 bytes if unpadded, or 24 bytes if padded. If the + // header value is unpadded, append two base64 padding + // characters ("==") before passing the value to + // Base64.decode(), which requires its input argument's + // length to be a multiple of four. + String base64hash = header.getValue(); + if (base64hash.length() == 22) { + base64hash += "=="; + } + result.setDigest( + new org.restlet.data.Digest( + org.restlet.data.Digest.ALGORITHM_MD5, + java.util.Base64.getDecoder().decode(base64hash))); + entityHeaderFound = true; + } + } + } + + // If no representation was initially expected and no entity header + // is found, then do not return any representation + if ((representation == null) && !entityHeaderFound) { + result = null; + } + + return result; + } + + /** + * Returns the content length of the request entity if known, {@link + * Representation#UNKNOWN_SIZE} otherwise. + * + * @return The request content length. + */ + public static long getContentLength(Series
headers) { + long contentLength = UNKNOWN_SIZE; + + if (headers != null) { + // Extract the content length header + for (Header header : headers) { + if (HEADER_CONTENT_LENGTH.equalsIgnoreCase(header.getName())) { + try { + contentLength = Long.parseLong(header.getValue()); + } catch (NumberFormatException e) { + contentLength = UNKNOWN_SIZE; + } + } + } + } + + return contentLength; + } + + /** + * Indicates if the given character is alphabetical (a-z or A-Z). + * + * @param character The character to test. + * @return True if the given character is alphabetical (a-z or A-Z). + */ + public static boolean isAlpha(int character) { + return isUpperCase(character) || isLowerCase(character); + } + + /** + * Indicates if the given character is in the ASCII range. + * + * @param character The character to test. + * @return True if the given character is in the ASCII range. + */ + public static boolean isAsciiChar(int character) { + return (character >= 0) && (character <= 127); + } + + /** + * Indicates if the given character is a carriage return. + * + * @param character The character to test. + * @return True if the given character is a carriage return. + */ + public static boolean isCarriageReturn(int character) { + return (character == 13); + } + + /** + * Indicates if the entity is chunked. + * + * @return True if the entity is chunked. + */ + public static boolean isChunkedEncoding(Series
headers) { + boolean result = false; + + if (headers != null) { + final String header = + headers.getFirstValue(HeaderConstants.HEADER_TRANSFER_ENCODING, true); + result = "chunked".equalsIgnoreCase(header); + } + + return result; + } + + /** + * Indicates if the given character is a comma, the character used as header value separator. + * + * @param character The character to test. + * @return True if the given character is a comma. + */ + public static boolean isComma(int character) { + return (character == ','); + } + + /** + * Indicates if the given character is a comment text. It means {@link #isText(int)} returns + * true and the character is not '(' or ')'. + * + * @param character The character to test. + * @return True if the given character is a quoted text. + */ + public static boolean isCommentText(int character) { + return isText(character) && (character != '(') && (character != ')'); + } + + /** + * Indicates if the connection must be closed. + * + * @param headers The headers to test. + * @return True if the connection must be closed. + */ + public static boolean isConnectionClose(Series
headers) { + boolean result = false; + + if (headers != null) { + String header = headers.getFirstValue(HeaderConstants.HEADER_CONNECTION, true); + result = "close".equalsIgnoreCase(header); + } + + return result; + } + + /** + * Indicates if the given character is a control character. + * + * @param character The character to test. + * @return True if the given character is a control character. + */ + public static boolean isControlChar(int character) { + return ((character >= 0) && (character <= 31)) || (character == 127); + } + + /** + * Indicates if the given character is a digit (0-9). + * + * @param character The character to test. + * @return True if the given character is a digit (0-9). + */ + public static boolean isDigit(int character) { + return (character >= '0') && (character <= '9'); + } + + /** + * Indicates if the given character is a double quote. + * + * @param character The character to test. + * @return True if the given character is a double quote. + */ + public static boolean isDoubleQuote(int character) { + return (character == 34); + } + + /** + * Indicates if the given character is a horizontal tab. + * + * @param character The character to test. + * @return True if the given character is a horizontal tab. + */ + public static boolean isHorizontalTab(int character) { + return (character == 9); + } + + /** + * Indicates if the given character is in ISO Latin 1 (8859-1) range. Note that this range is a + * superset of ASCII and a subrange of Unicode (UTF-8). + * + * @param character The character to test. + * @return True if the given character is in ISO Latin 1 range. + */ + public static boolean isLatin1Char(int character) { + return (character >= 0) && (character <= 255); + } + + /** + * Indicates if the given character is a value separator. + * + * @param character The character to test. + * @return True if the given character is a value separator. + */ + public static boolean isLinearWhiteSpace(int character) { + return (isCarriageReturn(character) + || isSpace(character) + || isLineFeed(character) + || HeaderUtils.isHorizontalTab(character)); + } + + /** + * Indicates if the given character is a line feed. + * + * @param character The character to test. + * @return True if the given character is a line feed. + */ + public static boolean isLineFeed(int character) { + return (character == 10); + } + + /** + * Indicates if the given character is lower case (a-z). + * + * @param character The character to test. + * @return True if the given character is lower case (a-z). + */ + public static boolean isLowerCase(int character) { + return (character >= 'a') && (character <= 'z'); + } + + /** + * Indicates if the given character marks the start of a quoted pair. + * + * @param character The character to test. + * @return True if the given character marks the start of a quoted pair. + */ + public static boolean isQuoteCharacter(int character) { + return (character == '\\'); + } + + /** + * Indicates if the given character is a quoted text. It means {@link #isText(int)} returns true + * and {@link #isDoubleQuote(int)} returns false. + * + * @param character The character to test. + * @return True if the given character is a quoted text. + */ + public static boolean isQuotedText(int character) { + return isText(character) && !isDoubleQuote(character); + } + + /** + * Indicates if the given character is a semicolon, the character used as header parameter + * separator. + * + * @param character The character to test. + * @return True if the given character is a semicolon. + */ + public static boolean isSemiColon(int character) { + return (character == ';'); + } + + /** + * Indicates if the given character is a separator. + * + * @param character The character to test. + * @return True if the given character is a separator. + */ + public static boolean isSeparator(int character) { + switch (character) { + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case ':': + case '\\': + case '"': + case '/': + case '[': + case ']': + case '?': + case '=': + case '{': + case '}': + case ' ': + case '\t': + return true; + + default: + return false; + } + } + + /** + * Indicates if the given character is a space. + * + * @param character The character to test. + * @return True if the given character is a space. + */ + public static boolean isSpace(int character) { + return (character == 32); + } + + /** + * Indicates if the given character is textual (ISO Latin 1 and not a control character). + * + * @param character The character to test. + * @return True if the given character is textual. + */ + public static boolean isText(int character) { + return isLatin1Char(character) && !isControlChar(character); + } + + /** + * Indicates if the token is valid.
+ * Only contains valid token characters. + * + * @param token The token to check + * @return True if the token is valid. + */ + public static boolean isToken(CharSequence token) { + for (int i = 0; i < token.length(); i++) { + if (!isTokenChar(token.charAt(i))) { + return false; + } + } + + return true; + } + + /** + * Indicates if the given character is a token character (text and not a separator). + * + * @param character The character to test. + * @return True if the given character is a token character (text and not a separator). + */ + public static boolean isTokenChar(int character) { + return isAsciiChar(character) && !isSeparator(character); + } + + /** + * Indicates if the given character is upper case (A-Z). + * + * @param character The character to test. + * @return True if the given character is upper case (A-Z). + */ + public static boolean isUpperCase(int character) { + return (character >= 'A') && (character <= 'Z'); + } + + /** + * Writes a new line. + * + * @param os The output stream. + * @throws IOException + */ + public static void writeCRLF(OutputStream os) throws IOException { + os.write(13); // CR + os.write(10); // LF + } + + /** + * Writes a header line. + * + * @param header The header to write. + * @param os The output stream. + * @throws IOException + */ + public static void writeHeaderLine(Header header, OutputStream os) throws IOException { + os.write(StringUtils.getAsciiBytes(header.getName())); + os.write(':'); + os.write(' '); + + if (header.getValue() != null) { + os.write(StringUtils.getLatin1Bytes(header.getValue())); + } + + os.write(13); // CR + os.write(10); // LF + } + + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private HeaderUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java index c46c82332e..db4f0168d7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java @@ -1,292 +1,288 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.StringWriter; +import java.util.Collection; import org.restlet.data.CharacterSet; import org.restlet.data.Encoding; import org.restlet.data.Reference; import org.restlet.util.NamedValue; -import java.io.StringWriter; -import java.util.Collection; - /** * HTTP-style header writer. - * + * * @param The value type. * @author Jerome Louvel */ public abstract class HeaderWriter extends StringWriter { - @Override - public HeaderWriter append(char c) { - super.append(c); - return this; - } - - /** - * Appends an array of characters. - * - * @param cs The array of characters. - * @return This writer. - */ - public HeaderWriter append(char[] cs) { - if (cs != null) { - for (char c : cs) { - append(c); - } - } - - return this; - } - - @Override - public HeaderWriter append(CharSequence csq) { - super.append(csq); - return this; - } - - /** - * Appends a collection of values. - * - * @param values The collection of values to append. - * @return This writer. - */ - public HeaderWriter append(Collection values) { - if ((values != null) && !values.isEmpty()) { - boolean first = true; - - for (V value : values) { - if (canWrite(value)) { - if (first) { - first = false; - } else { - appendValueSeparator(); - } - - append(value); - } - } - } - - return this; - } - - /** - * Appends an integer. - * - * @param i The value to append. - * @return This writer. - */ - public HeaderWriter append(int i) { - return append(Integer.toString(i)); - } - - /** - * Appends a long. - * - * @param l The value to append. - * @return This writer. - */ - public HeaderWriter append(long l) { - return append(Long.toString(l)); - } - - /** - * Appends a value. - * - * @param value The value. - * @return This writer. - */ - public abstract HeaderWriter append(V value); - - /** - * Appends a string as an HTTP comment, surrounded by parenthesis and with - * quoted pairs if needed. - * - * @param content The comment to write. - * @return This writer. - */ - public HeaderWriter appendComment(String content) { - append('('); - char c; - - for (int i = 0; i < content.length(); i++) { - c = content.charAt(i); - - if (HeaderUtils.isCommentText(c)) { - append(c); - } else { - appendQuotedPair(c); - } - } - - return append(')'); - } - - /** - * Formats and appends a parameter as an extension. If the value is not a token, - * then it is quoted. - * - * @param extension The parameter to format as an extension. - * @return This writer. - */ - public HeaderWriter appendExtension(NamedValue extension) { - if (extension != null) { - return appendExtension(extension.getName(), extension.getValue()); - } else { - return this; - } - } - - /** - * Appends an extension. If the value is not a token, then it is quoted. - * - * @param name The extension name. - * @param value The extension value. - * @return This writer. - */ - public HeaderWriter appendExtension(String name, String value) { - if ((name != null) && (!name.isEmpty())) { - append(name); - - if ((value != null) && (!value.isEmpty())) { - append("="); - - if (HeaderUtils.isToken(value)) { - append(value); - } else { - appendQuotedString(value); - } - } - } - - return this; - } - - /** - * Appends a semicolon as a parameter separator. - * - * @return This writer. - */ - public HeaderWriter appendParameterSeparator() { - return append(";"); - } - - /** - * Appends a product description. - * - * @param name The product name token. - * @param version The product version token. - * @return This writer. - */ - public HeaderWriter appendProduct(String name, String version) { - appendToken(name); - - if (version != null) { - append('/').appendToken(version); - } - - return this; - } - - /** - * Appends a quoted character, prefixing it with a backslash. - * - * @param character The character to quote. - * @return This writer. - */ - public HeaderWriter appendQuotedPair(char character) { - return append('\\').append(character); - } - - /** - * Appends a quoted string. - * - * @param content The string to quote and write. - * @return This writer. - */ - public HeaderWriter appendQuotedString(String content) { - if ((content != null) && (!content.isEmpty())) { - append('"'); - char c; - - for (int i = 0; i < content.length(); i++) { - c = content.charAt(i); - - if (HeaderUtils.isQuotedText(c)) { - append(c); - } else { - appendQuotedPair(c); - } - } - - append('"'); - } - - return this; - } - - /** - * Appends a space character. - * - * @return This writer. - */ - public HeaderWriter appendSpace() { - return append(' '); - } - - /** - * Appends a token. - * - * @param token The token to write. - * @return This writer. - */ - public HeaderWriter appendToken(String token) { - if (HeaderUtils.isToken(token)) { - return append(token); - } else { - throw new IllegalArgumentException("Unexpected character found in token: " + token); - } - } - - /** - * Formats and appends a source string as an URI encoded string. - * - * @param source The source string to format. - * @param characterSet The supported character encoding. - * @return This writer. - */ - public HeaderWriter appendUriEncoded(CharSequence source, CharacterSet characterSet) { - return append(Reference.encode(source.toString(), characterSet)); - } - - /** - * Appends a comma as a value separator. - * - * @return This writer. - */ - public HeaderWriter appendValueSeparator() { - return append(", "); - } - - /** - * Indicates if the value can be written to the header. Useful to prevent the - * writing of {@link Encoding#IDENTITY} constants for example. By default it - * returns true for non null values. - * - * @param value The value to add. - * @return True if the value can be added. - */ - protected boolean canWrite(V value) { - return (value != null); - } - + @Override + public HeaderWriter append(char c) { + super.append(c); + return this; + } + + /** + * Appends an array of characters. + * + * @param cs The array of characters. + * @return This writer. + */ + public HeaderWriter append(char[] cs) { + if (cs != null) { + for (char c : cs) { + append(c); + } + } + + return this; + } + + @Override + public HeaderWriter append(CharSequence csq) { + super.append(csq); + return this; + } + + /** + * Appends a collection of values. + * + * @param values The collection of values to append. + * @return This writer. + */ + public HeaderWriter append(Collection values) { + if ((values != null) && !values.isEmpty()) { + boolean first = true; + + for (V value : values) { + if (canWrite(value)) { + if (first) { + first = false; + } else { + appendValueSeparator(); + } + + append(value); + } + } + } + + return this; + } + + /** + * Appends an integer. + * + * @param i The value to append. + * @return This writer. + */ + public HeaderWriter append(int i) { + return append(Integer.toString(i)); + } + + /** + * Appends a long. + * + * @param l The value to append. + * @return This writer. + */ + public HeaderWriter append(long l) { + return append(Long.toString(l)); + } + + /** + * Appends a value. + * + * @param value The value. + * @return This writer. + */ + public abstract HeaderWriter append(V value); + + /** + * Appends a string as an HTTP comment, surrounded by parenthesis and with quoted pairs if + * needed. + * + * @param content The comment to write. + * @return This writer. + */ + public HeaderWriter appendComment(String content) { + append('('); + char c; + + for (int i = 0; i < content.length(); i++) { + c = content.charAt(i); + + if (HeaderUtils.isCommentText(c)) { + append(c); + } else { + appendQuotedPair(c); + } + } + + return append(')'); + } + + /** + * Formats and appends a parameter as an extension. If the value is not a token, then it is + * quoted. + * + * @param extension The parameter to format as an extension. + * @return This writer. + */ + public HeaderWriter appendExtension(NamedValue extension) { + if (extension != null) { + return appendExtension(extension.getName(), extension.getValue()); + } else { + return this; + } + } + + /** + * Appends an extension. If the value is not a token, then it is quoted. + * + * @param name The extension name. + * @param value The extension value. + * @return This writer. + */ + public HeaderWriter appendExtension(String name, String value) { + if ((name != null) && (!name.isEmpty())) { + append(name); + + if ((value != null) && (!value.isEmpty())) { + append("="); + + if (HeaderUtils.isToken(value)) { + append(value); + } else { + appendQuotedString(value); + } + } + } + + return this; + } + + /** + * Appends a semicolon as a parameter separator. + * + * @return This writer. + */ + public HeaderWriter appendParameterSeparator() { + return append(";"); + } + + /** + * Appends a product description. + * + * @param name The product name token. + * @param version The product version token. + * @return This writer. + */ + public HeaderWriter appendProduct(String name, String version) { + appendToken(name); + + if (version != null) { + append('/').appendToken(version); + } + + return this; + } + + /** + * Appends a quoted character, prefixing it with a backslash. + * + * @param character The character to quote. + * @return This writer. + */ + public HeaderWriter appendQuotedPair(char character) { + return append('\\').append(character); + } + + /** + * Appends a quoted string. + * + * @param content The string to quote and write. + * @return This writer. + */ + public HeaderWriter appendQuotedString(String content) { + if ((content != null) && (!content.isEmpty())) { + append('"'); + char c; + + for (int i = 0; i < content.length(); i++) { + c = content.charAt(i); + + if (HeaderUtils.isQuotedText(c)) { + append(c); + } else { + appendQuotedPair(c); + } + } + + append('"'); + } + + return this; + } + + /** + * Appends a space character. + * + * @return This writer. + */ + public HeaderWriter appendSpace() { + return append(' '); + } + + /** + * Appends a token. + * + * @param token The token to write. + * @return This writer. + */ + public HeaderWriter appendToken(String token) { + if (HeaderUtils.isToken(token)) { + return append(token); + } else { + throw new IllegalArgumentException("Unexpected character found in token: " + token); + } + } + + /** + * Formats and appends a source string as a URI encoded string. + * + * @param source The source string to format. + * @param characterSet The supported character encoding. + * @return This writer. + */ + public HeaderWriter appendUriEncoded(CharSequence source, CharacterSet characterSet) { + return append(Reference.encode(source.toString(), characterSet)); + } + + /** + * Appends a comma as a value separator. + * + * @return This writer. + */ + public HeaderWriter appendValueSeparator() { + return append(", "); + } + + /** + * Indicates if the value can be written to the header. Useful to prevent the writing of {@link + * Encoding#IDENTITY} constants for example. By default, it returns true for non-null values. + * + * @param value The value to add. + * @return True if the value can be added. + */ + protected boolean canWrite(V value) { + return (value != null); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java b/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java index e0f2e3010a..1c35dfc0df 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java @@ -1,37 +1,34 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Language; - import java.io.IOException; +import org.restlet.data.Language; /** * Language header reader. - * + * * @author Jerome Louvel */ public class LanguageReader extends HeaderReader { - /** - * Constructor. - * - * @param header The header to read. - */ - public LanguageReader(String header) { - super(header); - } - - @Override - public Language readValue() throws IOException { - return Language.valueOf(readRawValue()); - } + /** + * Constructor. + * + * @param header The header to read. + */ + public LanguageReader(String header) { + super(header); + } + @Override + public Language readValue() throws IOException { + return Language.valueOf(readRawValue()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java index e52c90b86e..f21c8f873e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java @@ -1,33 +1,30 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Language; - import java.util.List; +import org.restlet.data.Language; /** * Language header writer. - * + * * @author Jerome Louvel */ public class LanguageWriter extends MetadataWriter { - /** - * Writes a list of languages. - * - * @param languages The languages to write. - * @return This writer. - */ - public static String write(List languages) { - return new LanguageWriter().append(languages).toString(); - } - + /** + * Writes a list of languages. + * + * @param languages The languages to write. + * @return This writer. + */ + public static String write(List languages) { + return new LanguageWriter().append(languages).toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java index bbd23cefc2..84ebed348f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java @@ -1,26 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; import org.restlet.data.Metadata; /** * Metadata header writer. - * + * * @author Jerome Louvel */ public class MetadataWriter extends HeaderWriter { - @Override - public MetadataWriter append(M metadata) { - return (MetadataWriter) append(metadata.getName()); - } - + @Override + public MetadataWriter append(M metadata) { + return (MetadataWriter) append(metadata.getName()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java b/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java index 2c9e4d7a8c..ec84188f1d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java @@ -1,49 +1,46 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Header; -import org.restlet.data.Method; - import java.io.IOException; import java.util.Collection; +import org.restlet.data.Header; +import org.restlet.data.Method; /** * Method header reader. - * + * * @author Jerome Louvel */ public class MethodReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param collection The collection to update. - */ - public static void addValues(Header header, Collection collection) { - new MethodReader(header.getValue()).addValues(collection); - } - - /** - * Constructor. - * - * @param header The header to read. - */ - public MethodReader(String header) { - super(header); - } - - @Override - public Method readValue() throws IOException { - return Method.valueOf(readToken()); - } - + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param collection The collection to update. + */ + public static void addValues(Header header, Collection collection) { + new MethodReader(header.getValue()).addValues(collection); + } + + /** + * Constructor. + * + * @param header The header to read. + */ + public MethodReader(String header) { + super(header); + } + + @Override + public Method readValue() throws IOException { + return Method.valueOf(readToken()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java index 359e884101..1ac8552156 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java @@ -1,38 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Method; - import java.util.Set; +import org.restlet.data.Method; /** * Method header writer. - * + * * @author Jerome Louvel */ public class MethodWriter extends HeaderWriter { - /** - * Writes a set of methods with a comma separator. - * - * @param methods The set of methods. - * @return The formatted set of methods. - */ - public static String write(Set methods) { - return new MethodWriter().append(methods).toString(); - } - - @Override - public MethodWriter append(Method method) { - return (MethodWriter) appendToken(method.getName()); - } + /** + * Writes a set of methods with a comma separator. + * + * @param methods The set of methods. + * @return The formatted set of methods. + */ + public static String write(Set methods) { + return new MethodWriter().append(methods).toString(); + } + @Override + public MethodWriter append(Method method) { + return (MethodWriter) appendToken(method.getName()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java index a0a1602804..1a2b4f800d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java @@ -1,417 +1,437 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.*; -import org.restlet.util.Series; +import static org.restlet.engine.header.HeaderUtils.isComma; +import static org.restlet.engine.header.HeaderUtils.isDoubleQuote; +import static org.restlet.engine.header.HeaderUtils.isSpace; +import static org.restlet.engine.header.HeaderUtils.isText; +import static org.restlet.engine.header.HeaderUtils.isTokenChar; import java.io.IOException; import java.util.Iterator; - -import static org.restlet.engine.header.HeaderUtils.*; +import org.restlet.data.CharacterSet; +import org.restlet.data.ClientInfo; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Parameter; +import org.restlet.data.Preference; +import org.restlet.util.Series; /** - * Preference header reader. Works for character sets, encodings, languages or - * media types. - * + * Preference header reader. Works for character sets, encodings, languages, or media types. + * * @author Jerome Louvel */ public class PreferenceReader extends HeaderReader> { - public static final int TYPE_CHARACTER_SET = 1; - - public static final int TYPE_ENCODING = 2; - - public static final int TYPE_LANGUAGE = 3; - - public static final int TYPE_MEDIA_TYPE = 4; - - public static final int TYPE_PATCH = 5; - - /** - * Parses character set preferences from a header. - * - * @param acceptCharsetHeader The header to parse. - * @param clientInfo The client info to update. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void addCharacterSets(String acceptCharsetHeader, ClientInfo clientInfo) { - if (acceptCharsetHeader != null) { - // Implementation according to - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2 - if (acceptCharsetHeader.length() == 0) { - clientInfo.getAcceptedCharacterSets().add(new Preference(CharacterSet.ISO_8859_1)); - } else { - PreferenceReader pr = new PreferenceReader(PreferenceReader.TYPE_CHARACTER_SET, acceptCharsetHeader); - pr.addValues(clientInfo.getAcceptedCharacterSets()); - } - } else { - clientInfo.getAcceptedCharacterSets().add(new Preference(CharacterSet.ALL)); - } - } - - /** - * Parses encoding preferences from a header. - * - * @param acceptEncodingHeader The header to parse. - * @param clientInfo The client info to update. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void addEncodings(String acceptEncodingHeader, ClientInfo clientInfo) { - if (acceptEncodingHeader != null) { - PreferenceReader pr = new PreferenceReader(PreferenceReader.TYPE_ENCODING, acceptEncodingHeader); - pr.addValues(clientInfo.getAcceptedEncodings()); - } else { - clientInfo.getAcceptedEncodings().add(new Preference(Encoding.IDENTITY)); - } - } - - /** - * Adds language preferences from a header. - * - * @param acceptLanguageHeader The header to parse. - * @param clientInfo The client info to update. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void addLanguages(String acceptLanguageHeader, ClientInfo clientInfo) { - if (acceptLanguageHeader != null) { - PreferenceReader pr = new PreferenceReader(PreferenceReader.TYPE_LANGUAGE, acceptLanguageHeader); - pr.addValues(clientInfo.getAcceptedLanguages()); - } else { - clientInfo.getAcceptedLanguages().add(new Preference(Language.ALL)); - } - } - - /** - * Parses media type preferences from a header. - * - * @param acceptMediaTypeHeader The header to parse. - * @param clientInfo The client info to update. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void addMediaTypes(String acceptMediaTypeHeader, ClientInfo clientInfo) { - if (acceptMediaTypeHeader != null) { - PreferenceReader pr = new PreferenceReader(PreferenceReader.TYPE_MEDIA_TYPE, acceptMediaTypeHeader); - pr.addValues(clientInfo.getAcceptedMediaTypes()); - } else { - clientInfo.getAcceptedMediaTypes().add(new Preference(MediaType.ALL)); - } - } - - /** - * Parses patch preferences from a header. - * - * @param acceptPatchHeader The header to parse. - * @param clientInfo The client info to update. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void addPatches(String acceptPatchHeader, ClientInfo clientInfo) { - if (acceptPatchHeader != null) { - PreferenceReader pr = new PreferenceReader(PreferenceReader.TYPE_PATCH, acceptPatchHeader); - pr.addValues(clientInfo.getAcceptedPatches()); - } - } - - /** - * Parses a quality value.
- * If the quality is invalid, an IllegalArgumentException is thrown. - * - * @param quality The quality value as a string. - * @return The parsed quality value as a float. - */ - public static float readQuality(String quality) { - try { - float result = Float.valueOf(quality); - - if (PreferenceWriter.isValidQuality(result)) { - return result; - } - - throw new IllegalArgumentException("Invalid quality value detected. Value must be between 0 and 1."); - } catch (NumberFormatException nfe) { - throw new IllegalArgumentException("Invalid quality value detected. Value must be between 0 and 1."); - } - } - - /** The type of metadata read. */ - private volatile int type; - - /** - * Constructor. - * - * @param type The type of metadata read. - * @param header The header to read. - */ - public PreferenceReader(int type, String header) { - super(header); - this.type = type; - } - - /** - * Creates a new preference. - * - * @param metadata The metadata name. - * @param parameters The parameters list. - * @return The new preference. - */ - @SuppressWarnings("unchecked") - protected Preference createPreference(CharSequence metadata, Series parameters) { - Preference result; - - if (parameters == null) { - result = new Preference(); - - switch (this.type) { - case TYPE_CHARACTER_SET: - result.setMetadata((T) CharacterSet.valueOf(metadata.toString())); - break; - - case TYPE_ENCODING: - result.setMetadata((T) Encoding.valueOf(metadata.toString())); - break; - - case TYPE_LANGUAGE: - result.setMetadata((T) Language.valueOf(metadata.toString())); - break; - - case TYPE_MEDIA_TYPE: - case TYPE_PATCH: - result.setMetadata((T) MediaType.valueOf(metadata.toString())); - break; - } - } else { - final Series mediaParams = extractMediaParams(parameters); - final float quality = extractQuality(parameters); - result = new Preference(null, quality, parameters); - - switch (this.type) { - case TYPE_CHARACTER_SET: - result.setMetadata((T) new CharacterSet(metadata.toString())); - break; - - case TYPE_ENCODING: - result.setMetadata((T) new Encoding(metadata.toString())); - break; - - case TYPE_LANGUAGE: - result.setMetadata((T) new Language(metadata.toString())); - break; - - case TYPE_MEDIA_TYPE: - case TYPE_PATCH: - result.setMetadata((T) new MediaType(metadata.toString(), mediaParams)); - break; - } - } - - return result; - } - - /** - * Extract the media parameters. Only leave as the quality parameter if found. - * Modifies the parameters list. - * - * @param parameters All the preference parameters. - * @return The media parameters. - */ - protected Series extractMediaParams(Series parameters) { - Series result = null; - boolean qualityFound = false; - Parameter param = null; - - if (parameters != null) { - result = new Series(Parameter.class); - - for (final Iterator iter = parameters.iterator(); !qualityFound && iter.hasNext();) { - param = iter.next(); - - if (param.getName().equals("q")) { - qualityFound = true; - } else { - iter.remove(); - result.add(param); - } - } - } - - return result; - } - - /** - * Extract the quality value. If the value is not found, 1 is returned. - * - * @param parameters The preference parameters. - * @return The quality value. - */ - protected float extractQuality(Series parameters) { - float result = 1F; - boolean found = false; - - if (parameters != null) { - Parameter param = null; - - for (final Iterator iter = parameters.iterator(); !found && iter.hasNext();) { - param = iter.next(); - if (param.getName().equals("q")) { - result = readQuality(param.getValue()); - found = true; - - // Remove the quality parameter as we will directly store it - // in the Preference object - iter.remove(); - } - } - } - - return result; - } - - /** - * Read the next preference. - * - * @return The next preference. - */ - public Preference readValue() throws IOException { - Preference result = null; - - boolean readingMetadata = true; - boolean readingParamName = false; - boolean readingParamValue = false; - - StringBuilder metadataBuffer = new StringBuilder(); - StringBuilder paramNameBuffer = null; - StringBuilder paramValueBuffer = null; - - Series parameters = null; - int next = 0; - - while (result == null) { - next = read(); - - if (readingMetadata) { - if ((next == -1) || isComma(next)) { - if (metadataBuffer.length() > 0) { - // End of metadata section - // No parameters detected - result = createPreference(metadataBuffer, null); - } else { - // Ignore empty metadata name - break; - } - } else if (next == ';') { - if (metadataBuffer.length() > 0) { - // End of metadata section - // Parameters detected - readingMetadata = false; - readingParamName = true; - paramNameBuffer = new StringBuilder(); - parameters = new Series(Parameter.class); - } else { - throw new IOException("Empty metadata name detected."); - } - } else if (isSpace(next)) { - // Ignore spaces - } else if (isText(next)) { - metadataBuffer.append((char) next); - } else { - throw new IOException("Unexpected character \"" + (char) next + "\" detected."); - } - } else if (readingParamName) { - if (next == '=') { - if (paramNameBuffer.length() > 0) { - // End of parameter name section - readingParamName = false; - readingParamValue = true; - paramValueBuffer = new StringBuilder(); - } else { - throw new IOException("Empty parameter name detected."); - } - } else if ((next == -1) || isComma(next)) { - if (paramNameBuffer.length() > 0) { - // End of parameters section - parameters.add(Parameter.create(paramNameBuffer, null)); - result = createPreference(metadataBuffer, parameters); - } else { - throw new IOException("Empty parameter name detected."); - } - } else if (next == ';') { - // End of parameter - parameters.add(Parameter.create(paramNameBuffer, null)); - paramNameBuffer = new StringBuilder(); - readingParamName = true; - readingParamValue = false; - } else if (isSpace(next) && (paramNameBuffer.length() == 0)) { - // Ignore white spaces - } else if (isTokenChar(next)) { - paramNameBuffer.append((char) next); - } else { - throw new IOException("Unexpected character \"" + (char) next + "\" detected."); - } - } else if (readingParamValue) { - if ((next == -1) || isComma(next) || isSpace(next)) { - if (paramValueBuffer.length() > 0) { - // End of parameters section - parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); - result = createPreference(metadataBuffer, parameters); - } else { - throw new IOException("Empty parameter value detected"); - } - } else if (next == ';') { - // End of parameter - parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); - paramNameBuffer = new StringBuilder(); - readingParamName = true; - readingParamValue = false; - } else if ((next == '"') && (paramValueBuffer.length() == 0)) { - // Parse the quoted string - boolean done = false; - boolean quotedPair = false; - - while ((!done) && (next != -1)) { - next = read(); - - if (quotedPair) { - // End of quoted pair (escape sequence) - if (isText(next)) { - paramValueBuffer.append((char) next); - quotedPair = false; - } else { - throw new IOException( - "Invalid character detected in quoted string. Please check your value"); - } - } else if (isDoubleQuote(next)) { - // End of quoted string - done = true; - } else if (next == '\\') { - // Begin of quoted pair (escape sequence) - quotedPair = true; - } else if (isText(next)) { - paramValueBuffer.append((char) next); - } else { - throw new IOException( - "Invalid character detected in quoted string. Please check your value"); - } - } - } else if (isTokenChar(next)) { - paramValueBuffer.append((char) next); - } else { - throw new IOException("Unexpected character \"" + (char) next + "\" detected."); - } - } - } - - if (isComma(next)) { - // Unread character which isn't part of the value - unread(); - } - - return result; - } + public static final int TYPE_CHARACTER_SET = 1; + + public static final int TYPE_ENCODING = 2; + + public static final int TYPE_LANGUAGE = 3; + + public static final int TYPE_MEDIA_TYPE = 4; + + public static final int TYPE_PATCH = 5; + + /** + * Parses character set preferences from a header. + * + * @param acceptCharsetHeader The header to parse. + * @param clientInfo The client info to update. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void addCharacterSets(String acceptCharsetHeader, ClientInfo clientInfo) { + if (acceptCharsetHeader != null) { + // Implementation according to + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2 + if (acceptCharsetHeader.isEmpty()) { + clientInfo + .getAcceptedCharacterSets() + .add(new Preference<>(CharacterSet.ISO_8859_1)); + } else { + PreferenceReader pr = + new PreferenceReader( + PreferenceReader.TYPE_CHARACTER_SET, acceptCharsetHeader); + pr.addValues(clientInfo.getAcceptedCharacterSets()); + } + } else { + clientInfo.getAcceptedCharacterSets().add(new Preference(CharacterSet.ALL)); + } + } + + /** + * Parses encoding preferences from a header. + * + * @param acceptEncodingHeader The header to parse. + * @param clientInfo The client info to update. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void addEncodings(String acceptEncodingHeader, ClientInfo clientInfo) { + if (acceptEncodingHeader != null) { + PreferenceReader pr = + new PreferenceReader(PreferenceReader.TYPE_ENCODING, acceptEncodingHeader); + pr.addValues(clientInfo.getAcceptedEncodings()); + } else { + clientInfo.getAcceptedEncodings().add(new Preference(Encoding.IDENTITY)); + } + } + + /** + * Adds language preferences from a header. + * + * @param acceptLanguageHeader The header to parse. + * @param clientInfo The client info to update. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void addLanguages(String acceptLanguageHeader, ClientInfo clientInfo) { + if (acceptLanguageHeader != null) { + PreferenceReader pr = + new PreferenceReader(PreferenceReader.TYPE_LANGUAGE, acceptLanguageHeader); + pr.addValues(clientInfo.getAcceptedLanguages()); + } else { + clientInfo.getAcceptedLanguages().add(new Preference(Language.ALL)); + } + } + + /** + * Parses media type preferences from a header. + * + * @param acceptMediaTypeHeader The header to parse. + * @param clientInfo The client info to update. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void addMediaTypes(String acceptMediaTypeHeader, ClientInfo clientInfo) { + if (acceptMediaTypeHeader != null) { + PreferenceReader pr = + new PreferenceReader(PreferenceReader.TYPE_MEDIA_TYPE, acceptMediaTypeHeader); + pr.addValues(clientInfo.getAcceptedMediaTypes()); + } else { + clientInfo.getAcceptedMediaTypes().add(new Preference(MediaType.ALL)); + } + } + + /** + * Parses patch preferences from a header. + * + * @param acceptPatchHeader The header to parse. + * @param clientInfo The client info to update. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static void addPatches(String acceptPatchHeader, ClientInfo clientInfo) { + if (acceptPatchHeader != null) { + PreferenceReader pr = + new PreferenceReader(PreferenceReader.TYPE_PATCH, acceptPatchHeader); + pr.addValues(clientInfo.getAcceptedPatches()); + } + } + + /** + * Parses a quality value.
+ * If the quality is invalid, an IllegalArgumentException is thrown. + * + * @param quality The quality value as a string. + * @return The parsed quality value as a float. + */ + public static float readQuality(String quality) { + try { + float result = Float.parseFloat(quality); + + if (PreferenceWriter.isValidQuality(result)) { + return result; + } + + throw new IllegalArgumentException( + "Invalid quality value detected. Value must be between 0 and 1."); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException( + "Invalid quality value detected. Value must be between 0 and 1."); + } + } + + /** The type of metadata read. */ + private volatile int type; + + /** + * Constructor. + * + * @param type The type of metadata read. + * @param header The header to read. + */ + public PreferenceReader(int type, String header) { + super(header); + this.type = type; + } + + /** + * Creates a new preference. + * + * @param metadata The metadata name. + * @param parameters The parameters list. + * @return The new preference. + */ + @SuppressWarnings("unchecked") + protected Preference createPreference(CharSequence metadata, Series parameters) { + Preference result; + + if (parameters == null) { + result = new Preference(); + + switch (this.type) { + case TYPE_CHARACTER_SET: + result.setMetadata((T) CharacterSet.valueOf(metadata.toString())); + break; + + case TYPE_ENCODING: + result.setMetadata((T) Encoding.valueOf(metadata.toString())); + break; + + case TYPE_LANGUAGE: + result.setMetadata((T) Language.valueOf(metadata.toString())); + break; + + case TYPE_MEDIA_TYPE: + case TYPE_PATCH: + result.setMetadata((T) MediaType.valueOf(metadata.toString())); + break; + } + } else { + final Series mediaParams = extractMediaParams(parameters); + final float quality = extractQuality(parameters); + result = new Preference(null, quality, parameters); + + switch (this.type) { + case TYPE_CHARACTER_SET: + result.setMetadata((T) new CharacterSet(metadata.toString())); + break; + + case TYPE_ENCODING: + result.setMetadata((T) new Encoding(metadata.toString())); + break; + + case TYPE_LANGUAGE: + result.setMetadata((T) new Language(metadata.toString())); + break; + + case TYPE_MEDIA_TYPE: + case TYPE_PATCH: + result.setMetadata((T) new MediaType(metadata.toString(), mediaParams)); + break; + } + } + + return result; + } + + /** + * Extract the media parameters. Only leave as the quality parameter if found. Modifies the + * parameters list. + * + * @param parameters All the preference parameters. + * @return The media parameters. + */ + protected Series extractMediaParams(Series parameters) { + Series result = null; + boolean qualityFound = false; + Parameter param = null; + + if (parameters != null) { + result = new Series<>(Parameter.class); + + for (final Iterator iter = parameters.iterator(); + !qualityFound && iter.hasNext(); ) { + param = iter.next(); + + if (param.getName().equals("q")) { + qualityFound = true; + } else { + iter.remove(); + result.add(param); + } + } + } + + return result; + } + + /** + * Extract the quality value. If the value is not found, 1 is returned. + * + * @param parameters The preference parameters. + * @return The quality value. + */ + protected float extractQuality(Series parameters) { + float result = 1F; + boolean found = false; + + if (parameters != null) { + Parameter param = null; + + for (final Iterator iter = parameters.iterator(); + !found && iter.hasNext(); ) { + param = iter.next(); + if (param.getName().equals("q")) { + result = readQuality(param.getValue()); + found = true; + + // Remove the quality parameter as we will directly store it + // in the Preference object + iter.remove(); + } + } + } + + return result; + } + + /** + * Read the next preference. + * + * @return The next preference. + */ + public Preference readValue() throws IOException { + Preference result = null; + + boolean readingMetadata = true; + boolean readingParamName = false; + boolean readingParamValue = false; + + StringBuilder metadataBuffer = new StringBuilder(); + StringBuilder paramNameBuffer = null; + StringBuilder paramValueBuffer = null; + + Series parameters = null; + int next = 0; + + while (result == null) { + next = read(); + + if (readingMetadata) { + if ((next == -1) || isComma(next)) { + if (metadataBuffer.isEmpty()) { + // Ignore empty metadata name + break; + } else { + // End of metadata section + // No parameters detected + result = createPreference(metadataBuffer, null); + } + } else if (next == ';') { + if (metadataBuffer.isEmpty()) { + throw new IOException("Empty metadata name detected."); + } else { + // End of metadata section + // Parameters detected + readingMetadata = false; + readingParamName = true; + paramNameBuffer = new StringBuilder(); + parameters = new Series(Parameter.class); + } + } else if (isSpace(next)) { + // Ignore spaces + } else if (isText(next)) { + metadataBuffer.append((char) next); + } else { + throw new IOException("Unexpected character \"" + (char) next + "\" detected."); + } + } else if (readingParamName) { + if (next == '=') { + if (paramNameBuffer.isEmpty()) { + throw new IOException("Empty parameter name detected."); + } else { + // End of a parameter name section + readingParamName = false; + readingParamValue = true; + paramValueBuffer = new StringBuilder(); + } + } else if ((next == -1) || isComma(next)) { + if (paramNameBuffer.isEmpty()) { + throw new IOException("Empty parameter name detected."); + } else { + // End of a parameters section + parameters.add(Parameter.create(paramNameBuffer, null)); + result = createPreference(metadataBuffer, parameters); + } + } else if (next == ';') { + // End of parameter + parameters.add(Parameter.create(paramNameBuffer, null)); + paramNameBuffer = new StringBuilder(); + readingParamName = true; + readingParamValue = false; + } else if (isSpace(next) && paramNameBuffer.isEmpty()) { + // Ignore white spaces + } else if (isTokenChar(next)) { + paramNameBuffer.append((char) next); + } else { + throw new IOException("Unexpected character \"" + (char) next + "\" detected."); + } + } else if (readingParamValue) { + if ((next == -1) || isComma(next) || isSpace(next)) { + if (paramValueBuffer.isEmpty()) { + throw new IOException("Empty parameter value detected"); + } else { + // End of a parameters section + parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); + result = createPreference(metadataBuffer, parameters); + } + } else if (next == ';') { + // End of parameter + parameters.add(Parameter.create(paramNameBuffer, paramValueBuffer)); + paramNameBuffer = new StringBuilder(); + readingParamName = true; + readingParamValue = false; + } else if ((next == '"') && paramValueBuffer.isEmpty()) { + // Parse the quoted string + boolean done = false; + boolean quotedPair = false; + + while ((!done) && (next != -1)) { + next = read(); + + if (quotedPair) { + // End of a quoted pair (escape sequence) + if (isText(next)) { + paramValueBuffer.append((char) next); + quotedPair = false; + } else { + throw new IOException( + "Invalid character detected in quoted string. Please check your value"); + } + } else if (isDoubleQuote(next)) { + // End of quoted string + done = true; + } else if (next == '\\') { + // Begin of a quoted pair (escape sequence) + quotedPair = true; + } else if (isText(next)) { + paramValueBuffer.append((char) next); + } else { + throw new IOException( + "Invalid character detected in quoted string. Please check your value"); + } + } + } else if (isTokenChar(next)) { + paramValueBuffer.append((char) next); + } else { + throw new IOException("Unexpected character \"" + (char) next + "\" detected."); + } + } + } + + if (isComma(next)) { + // Unread character which isn't part of the value + unread(); + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java index 1b0ca1cd65..3e3b5eb2d3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java @@ -1,94 +1,92 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Parameter; -import org.restlet.data.Preference; - import java.io.IOException; -import java.util.Iterator; import java.util.List; +import org.restlet.data.Parameter; +import org.restlet.data.Preference; /** * Preference header writer. - * + * * @author Jerome Louvel */ public class PreferenceWriter extends HeaderWriter> { - /** - * Indicates if the quality value is valid. - * - * @param quality The quality value. - * @return True if the quality value is valid. - */ - public static boolean isValidQuality(float quality) { - return (quality >= 0F) && (quality <= 1F); - } - - /** - * Writes a list of preferences with a comma separator. - * - * @param prefs The list of preferences. - * @return The formatted list of preferences. - * @throws IOException - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static String write(List prefs) { - return new PreferenceWriter().append(prefs).toString(); - } - - @Override - public PreferenceWriter append(Preference pref) { - append(pref.getMetadata().getName()); - - if (pref.getQuality() < 1F) { - append(";q="); - appendQuality(pref.getQuality()); - } - - if (pref.getParameters() != null) { - Parameter param; - - for (Iterator iter = pref.getParameters().iterator(); iter.hasNext();) { - param = iter.next(); - - if (param.getName() != null) { - append(';').append(param.getName()); - - if ((param.getValue() != null) && (param.getValue().length() > 0)) { - append('=').append(param.getValue()); - } - } - } - } - - return this; - } - - /** - * Formats a quality value.
- * If the quality is invalid, an IllegalArgumentException is thrown. - * - * @param quality The quality value as a float. - * @return This writer. - */ - public PreferenceWriter appendQuality(float quality) { - if (!isValidQuality(quality)) { - throw new IllegalArgumentException("Invalid quality value detected. Value must be between 0 and 1."); - } - - java.text.NumberFormat formatter = java.text.NumberFormat.getNumberInstance(java.util.Locale.US); - formatter.setMaximumFractionDigits(2); - append(formatter.format(quality)); - - return this; - } - + /** + * Indicates if the quality value is valid. + * + * @param quality The quality value. + * @return True if the quality value is valid. + */ + public static boolean isValidQuality(float quality) { + return (quality >= 0F) && (quality <= 1F); + } + + /** + * Writes a list of preferences with a comma separator. + * + * @param prefs The list of preferences. + * @return The formatted list of preferences. + * @throws IOException + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static String write(List prefs) { + return new PreferenceWriter().append(prefs).toString(); + } + + @Override + public PreferenceWriter append(Preference pref) { + append(pref.getMetadata().getName()); + + if (pref.getQuality() < 1F) { + append(";q="); + appendQuality(pref.getQuality()); + } + + if (pref.getParameters() != null) { + Parameter param; + + for (final Parameter parameter : pref.getParameters()) { + param = parameter; + + if (param.getName() != null) { + append(';').append(param.getName()); + + if ((param.getValue() != null) && (!param.getValue().isEmpty())) { + append('=').append(param.getValue()); + } + } + } + } + + return this; + } + + /** + * Formats a quality value.
+ * If the quality is invalid, an IllegalArgumentException is thrown. + * + * @param quality The quality value as a float. + * @return This writer. + */ + public PreferenceWriter appendQuality(float quality) { + if (!isValidQuality(quality)) { + throw new IllegalArgumentException( + "Invalid quality value detected. Value must be between 0 and 1."); + } + + java.text.NumberFormat formatter = + java.text.NumberFormat.getNumberInstance(java.util.Locale.US); + formatter.setMaximumFractionDigits(2); + append(formatter.format(quality)); + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java index b8a4a7470c..805f96107a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java @@ -1,125 +1,120 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Product; - import java.util.ArrayList; import java.util.List; +import org.restlet.data.Product; /** * User agent header reader. - * + * * @author Thierry Boileau */ public class ProductReader { - /** - * Parses the given user agent String to a list of Product instances. - * - * @param userAgent - * @return the List of Product objects parsed from the String - * @throws IllegalArgumentException Thrown if the String can not be parsed as a - * list of Product instances. - */ - public static List read(String userAgent) throws IllegalArgumentException { - final List result = new ArrayList(); - - if (userAgent != null) { - String token = null; - String version = null; - String comment = null; - final char[] tab = userAgent.trim().toCharArray(); - StringBuilder tokenBuilder = new StringBuilder(); - StringBuilder versionBuilder = null; - StringBuilder commentBuilder = null; - int index = 0; - boolean insideToken = true; - boolean insideVersion = false; - boolean insideComment = false; - - for (index = 0; index < tab.length; index++) { - final char c = tab[index]; - if (insideToken) { - if (HeaderUtils.isTokenChar(c) || (c == ' ')) { - tokenBuilder.append(c); - } else { - token = tokenBuilder.toString().trim(); - insideToken = false; - if (c == '/') { - insideVersion = true; - versionBuilder = new StringBuilder(); - } else if (c == '(') { - insideComment = true; - commentBuilder = new StringBuilder(); - } - } - } else { - if (insideVersion) { - if (c != ' ') { - versionBuilder.append(c); - } else { - insideVersion = false; - version = versionBuilder.toString(); - } - } else { - if (c == '(') { - insideComment = true; - commentBuilder = new StringBuilder(); - } else { - if (insideComment) { - if (c == ')') { - insideComment = false; - comment = commentBuilder.toString(); - result.add(new Product(token, version, comment)); - insideToken = true; - tokenBuilder = new StringBuilder(); - } else { - commentBuilder.append(c); - } - } else { - result.add(new Product(token, version, null)); - insideToken = true; - tokenBuilder = new StringBuilder(); - tokenBuilder.append(c); - } - } - } - } - } + /** + * Parses the given user agent String to a list of Product instances. + * + * @param userAgent + * @return the List of Product objects parsed from the String + * @throws IllegalArgumentException Thrown if the String cannot be parsed as a list of Product + * instances. + */ + public static List read(String userAgent) throws IllegalArgumentException { + final List result = new ArrayList(); - if (insideComment) { - comment = commentBuilder.toString(); - result.add(new Product(token, version, comment)); - } else { - if (insideVersion) { - version = versionBuilder.toString(); - result.add(new Product(token, version, null)); - } else { - if (insideToken && (!tokenBuilder.isEmpty())) { - token = tokenBuilder.toString(); - result.add(new Product(token, null, null)); - } - } - } - } + if (userAgent != null) { + String token = null; + String version = null; + String comment = null; + final char[] tab = userAgent.trim().toCharArray(); + StringBuilder tokenBuilder = new StringBuilder(); + StringBuilder versionBuilder = null; + StringBuilder commentBuilder = null; + int index = 0; + boolean insideToken = true; + boolean insideVersion = false; + boolean insideComment = false; - return result; + for (index = 0; index < tab.length; index++) { + final char c = tab[index]; + if (insideToken) { + if (HeaderUtils.isTokenChar(c) || (c == ' ')) { + tokenBuilder.append(c); + } else { + token = tokenBuilder.toString().trim(); + insideToken = false; + if (c == '/') { + insideVersion = true; + versionBuilder = new StringBuilder(); + } else if (c == '(') { + insideComment = true; + commentBuilder = new StringBuilder(); + } + } + } else { + if (insideVersion) { + if (c != ' ') { + versionBuilder.append(c); + } else { + insideVersion = false; + version = versionBuilder.toString(); + } + } else { + if (c == '(') { + insideComment = true; + commentBuilder = new StringBuilder(); + } else { + if (insideComment) { + if (c == ')') { + insideComment = false; + comment = commentBuilder.toString(); + result.add(new Product(token, version, comment)); + insideToken = true; + tokenBuilder = new StringBuilder(); + } else { + commentBuilder.append(c); + } + } else { + result.add(new Product(token, version, null)); + insideToken = true; + tokenBuilder = new StringBuilder(); + tokenBuilder.append(c); + } + } + } + } + } - } + if (insideComment) { + comment = commentBuilder.toString(); + result.add(new Product(token, version, comment)); + } else { + if (insideVersion) { + version = versionBuilder.toString(); + result.add(new Product(token, version, null)); + } else { + if (insideToken && (!tokenBuilder.isEmpty())) { + token = tokenBuilder.toString(); + result.add(new Product(token, null, null)); + } + } + } + } - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private ProductReader() { - } + return result; + } + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private ProductReader() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java index ba90f6cb4d..b83c56b2bb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java @@ -1,58 +1,55 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Product; - import java.util.Iterator; import java.util.List; +import org.restlet.data.Product; /** * User agent header writer. - * + * * @author Thierry Boileau */ public class ProductWriter { - /** - * Formats the given List of Products to a String. - * - * @param products The list of products to format. - * @return the List of Products as String. - */ - public static String write(List products) { - StringBuilder builder = new StringBuilder(); - - for (Iterator iterator = products.iterator(); iterator.hasNext();) { - Product product = iterator.next(); + /** + * Formats the given List of Products to a String. + * + * @param products The list of products to format. + * @return the List of Products as String. + */ + public static String write(List products) { + StringBuilder builder = new StringBuilder(); - if ((product.getName() == null) || (product.getName().isEmpty())) { - throw new IllegalArgumentException("Product name cannot be null."); - } + for (Iterator iterator = products.iterator(); iterator.hasNext(); ) { + Product product = iterator.next(); - builder.append(product.getName()); + if ((product.getName() == null) || (product.getName().isEmpty())) { + throw new IllegalArgumentException("Product name cannot be null."); + } - if (product.getVersion() != null) { - builder.append("/").append(product.getVersion()); - } + builder.append(product.getName()); - if (product.getComment() != null) { - builder.append(" (").append(product.getComment()).append(")"); - } + if (product.getVersion() != null) { + builder.append("/").append(product.getVersion()); + } - if (iterator.hasNext()) { - builder.append(" "); - } - } + if (product.getComment() != null) { + builder.append(" (").append(product.getComment()).append(")"); + } - return builder.toString(); - } + if (iterator.hasNext()) { + builder.append(" "); + } + } + return builder.toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java b/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java index f82a34cbee..7b5d335cd2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java @@ -1,69 +1,68 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Range; -import org.restlet.representation.Representation; - import java.util.ArrayList; import java.util.List; +import org.restlet.data.Range; +import org.restlet.representation.Representation; /** * Range header reader. - * + * * @author Jerome Louvel */ public class RangeReader { - private static final String BYTES_RANGE_PREFIX = "bytes "; + private static final String BYTES_RANGE_PREFIX = "bytes "; - /** - * Parse the Content-Range header value and update the given representation. - * - * @param value Content-range header. - * @param representation Representation to update. - */ - public static void update(String value, Representation representation) { - if (value != null && value.startsWith(BYTES_RANGE_PREFIX)) { - value = value.substring(BYTES_RANGE_PREFIX.length()); + /** + * Parse the Content-Range header value and update the given representation. + * + * @param value Content-range header. + * @param representation Representation to update. + */ + public static void update(String value, Representation representation) { + if (value != null && value.startsWith(BYTES_RANGE_PREFIX)) { + value = value.substring(BYTES_RANGE_PREFIX.length()); - int index = value.indexOf("-"); - int index1 = value.indexOf("/"); + int index = value.indexOf('-'); + int index1 = value.indexOf('/'); - if (index != -1) { - long startIndex = (index == 0) ? Range.INDEX_LAST : Long.parseLong(value.substring(0, index)); - long endIndex = Long.parseLong(value.substring(index + 1, index1)); + if (index != -1) { + long startIndex = + (index == 0) ? Range.INDEX_LAST : Long.parseLong(value.substring(0, index)); + long endIndex = Long.parseLong(value.substring(index + 1, index1)); - representation.setRange(new Range(startIndex, endIndex - startIndex + 1)); - } + representation.setRange(new Range(startIndex, endIndex - startIndex + 1)); + } - String strLength = value.substring(index1 + 1); - if (!("*".equals(strLength))) { - representation.setSize(Long.parseLong(strLength)); - } - } - } + String strLength = value.substring(index1 + 1); + if (!("*".equals(strLength))) { + representation.setSize(Long.parseLong(strLength)); + } + } + } - /** - * Parse the Range header and returns the list of corresponding Range objects. - * - * @param rangeHeader The Range header value. - * @return The list of corresponding Range objects. - */ - public static List read(String rangeHeader) { - List result = new ArrayList(); - String prefix = "bytes="; - if (rangeHeader != null && rangeHeader.startsWith(prefix)) { - rangeHeader = rangeHeader.substring(prefix.length()); + /** + * Parse the Range header and returns the list of corresponding Range objects. + * + * @param rangeHeader The Range header value. + * @return The list of corresponding Range objects. + */ + public static List read(String rangeHeader) { + List result = new ArrayList<>(); + String prefix = "bytes="; + if (rangeHeader != null && rangeHeader.startsWith(prefix)) { + rangeHeader = rangeHeader.substring(prefix.length()); - String[] array = rangeHeader.split(","); + String[] array = rangeHeader.split(","); for (String s : array) { String value = s.trim(); long index = 0; @@ -83,15 +82,14 @@ public static List read(String rangeHeader) { } result.add(new Range(index, length)); } - } + } - return result; - } + return result; + } - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e., it isn't instantiable and extensible. - */ - private RangeReader() { - } + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private RangeReader() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java index fc1ca5e10e..e2d60dc87a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java @@ -1,132 +1,134 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.util.List; import org.restlet.data.Range; import org.restlet.representation.Representation; -import java.util.List; - /** * Range header writer. - * + * * @author Jerome Louvel */ public class RangeWriter extends HeaderWriter { - /** - * Formats {@code ranges} as a Range header value - * - * @param ranges List of ranges to format - * @return {@code ranges} formatted or null if the list is null or empty. - */ - public static String write(List ranges) { - return new RangeWriter().append(ranges).toString(); - } - - /** - * Formats {@code range} as a Content-Range header value. - * - * @param range Range to format - * @param size Total size of the entity - * @return {@code range} formatted - */ - public static String write(Range range, long size) { - StringBuilder b = new StringBuilder(range.getUnitName() + " "); - - if (range.getIndex() >= Range.INDEX_FIRST) { - b.append(range.getIndex()); - b.append("-"); - if (range.getSize() != Range.SIZE_MAX) { - b.append(range.getIndex() + range.getSize() - 1); - } else { - if (size != Representation.UNKNOWN_SIZE) { - b.append(size - 1); - } else { - throw new IllegalArgumentException( - "The entity has an unknown size, can't determine the last byte position."); - } - } - } else if (range.getIndex() == Range.INDEX_LAST) { - if (range.getSize() != Range.SIZE_MAX) { - if (size != Representation.UNKNOWN_SIZE) { - if (range.getSize() <= size) { - b.append(size - range.getSize()); - b.append("-"); - b.append(size - 1); - } else { - throw new IllegalArgumentException("The size of the range (" + range.getSize() - + ") is higher than the size of the entity (" + size + ")."); - } - } else { - throw new IllegalArgumentException( - "The entity has an unknown size, can't determine the last byte position."); - } - } else { - // This is not a valid range. - throw new IllegalArgumentException("The range provides no index and no size, it is invalid."); - } - } - - if (size != Representation.UNKNOWN_SIZE) { - b.append("/").append(size); - } else { - b.append("/*"); - } - - return b.toString(); - } - - /** - * Formats {@code ranges} as a Range header value - * - * @param ranges List of ranges to format - * @return This writer. - */ - public RangeWriter append(List ranges) { - if (ranges == null || ranges.isEmpty()) { - return this; - } - - append(ranges.get(0).getUnitName()); - append("="); - - for (int i = 0; i < ranges.size(); i++) { - if (i > 0) { - append(", "); - } - - append(ranges.get(i)); - } - - return this; - } - - @Override - public HeaderWriter append(Range range) { - if (range.getIndex() >= Range.INDEX_FIRST) { - append(range.getIndex()); - append("-"); - - if (range.getSize() != Range.SIZE_MAX) { - append(range.getIndex() + range.getSize() - 1); - } - } else if (range.getIndex() == Range.INDEX_LAST) { - append("-"); - - if (range.getSize() != Range.SIZE_MAX) { - append(range.getSize()); - } - } - - return this; - } - + /** + * Formats {@code ranges} as a Range header value + * + * @param ranges List of ranges to format + * @return {@code ranges} formatted or null if the list is null or empty. + */ + public static String write(List ranges) { + return new RangeWriter().append(ranges).toString(); + } + + /** + * Formats {@code range} as a Content-Range header value. + * + * @param range Range to format + * @param size Total size of the entity + * @return {@code range} formatted + */ + public static String write(Range range, long size) { + StringBuilder b = new StringBuilder(range.getUnitName() + " "); + + if (range.getIndex() >= Range.INDEX_FIRST) { + b.append(range.getIndex()); + b.append("-"); + if (range.getSize() != Range.SIZE_MAX) { + b.append(range.getIndex() + range.getSize() - 1); + } else { + if (size != Representation.UNKNOWN_SIZE) { + b.append(size - 1); + } else { + throw new IllegalArgumentException( + "The entity has an unknown size, can't determine the last byte position."); + } + } + } else if (range.getIndex() == Range.INDEX_LAST) { + if (range.getSize() != Range.SIZE_MAX) { + if (size != Representation.UNKNOWN_SIZE) { + if (range.getSize() <= size) { + b.append(size - range.getSize()); + b.append("-"); + b.append(size - 1); + } else { + throw new IllegalArgumentException( + "The size of the range (" + + range.getSize() + + ") is higher than the size of the entity (" + + size + + ")."); + } + } else { + throw new IllegalArgumentException( + "The entity has an unknown size, can't determine the last byte position."); + } + } else { + // This is not a valid range. + throw new IllegalArgumentException( + "The range provides no index and no size, it is invalid."); + } + } + + if (size != Representation.UNKNOWN_SIZE) { + b.append("/").append(size); + } else { + b.append("/*"); + } + + return b.toString(); + } + + /** + * Formats {@code ranges} as a Range header value + * + * @param ranges List of ranges to format + * @return This writer. + */ + public RangeWriter append(List ranges) { + if (ranges == null || ranges.isEmpty()) { + return this; + } + + append(ranges.getFirst().getUnitName()); + append("="); + + for (int i = 0; i < ranges.size(); i++) { + if (i > 0) { + append(", "); + } + + append(ranges.get(i)); + } + + return this; + } + + @Override + public HeaderWriter append(Range range) { + if (range.getIndex() >= Range.INDEX_FIRST) { + append(range.getIndex()); + append("-"); + + if (range.getSize() != Range.SIZE_MAX) { + append(range.getIndex() + range.getSize() - 1); + } + } else if (range.getIndex() == Range.INDEX_LAST) { + append("-"); + + if (range.getSize() != Range.SIZE_MAX) { + append(range.getSize()); + } + } + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java index 0c8480dd06..e28aa260a5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java @@ -1,73 +1,71 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.IOException; +import java.util.Collection; import org.restlet.data.Header; import org.restlet.data.Protocol; import org.restlet.data.RecipientInfo; -import java.io.IOException; -import java.util.Collection; - /** * Recipient info header reader. - * + * * @author Jerome Louvel */ public class RecipientInfoReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param collection The collection to update. - */ - public static void addValues(Header header, Collection collection) { - new RecipientInfoReader(header.getValue()).addValues(collection); - } + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param collection The collection to update. + */ + public static void addValues(Header header, Collection collection) { + new RecipientInfoReader(header.getValue()).addValues(collection); + } - /** - * Constructor. - * - * @param header The header to read. - */ - public RecipientInfoReader(String header) { - super(header); - } + /** + * Constructor. + * + * @param header The header to read. + */ + public RecipientInfoReader(String header) { + super(header); + } - @Override - public RecipientInfo readValue() throws IOException { - RecipientInfo result = new RecipientInfo(); - String protocolToken = readToken(); + @Override + public RecipientInfo readValue() throws IOException { + RecipientInfo result = new RecipientInfo(); + String protocolToken = readToken(); - if (protocolToken == null || protocolToken.isEmpty()) { - throw new IOException( - "Unexpected empty protocol token for while reading recipient info header, please check the value."); - } + if (protocolToken == null || protocolToken.isEmpty()) { + throw new IOException( + "Unexpected empty protocol token for while reading recipient info header, please check the value."); + } - if (peek() == '/') { - read(); - result.setProtocol(new Protocol(protocolToken, protocolToken, null, -1, readToken())); - } else { - result.setProtocol(new Protocol("HTTP", "HTTP", null, -1, protocolToken)); - } + if (peek() == '/') { + read(); + result.setProtocol(new Protocol(protocolToken, protocolToken, null, -1, readToken())); + } else { + result.setProtocol(new Protocol("HTTP", "HTTP", null, -1, protocolToken)); + } - // Move to the next text - if (skipSpaces()) { - result.setName(readRawText()); + // Move to the next text + if (skipSpaces()) { + result.setName(readRawText()); - // Move to the next text - if (skipSpaces()) { - result.setComment(readComment()); - } - } + // Move to the next text + if (skipSpaces()) { + result.setComment(readComment()); + } + } - return result; - } + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java index 7a4b030de4..f6ebf8a6d6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java @@ -1,58 +1,56 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.RecipientInfo; - import java.util.List; +import org.restlet.data.RecipientInfo; /** * Recipient info header writer. - * + * * @author Jerome Louvel */ public class RecipientInfoWriter extends HeaderWriter { - /** - * Creates a via header from the given recipients info. - * - * @param recipientsInfo The recipients info. - * @return Returns the Via header. - */ - public static String write(List recipientsInfo) { - return new RecipientInfoWriter().append(recipientsInfo).toString(); - } - - @Override - public RecipientInfoWriter append(RecipientInfo recipientInfo) { - if (recipientInfo.getProtocol() != null) { - appendToken(recipientInfo.getProtocol().getName()); - append('/'); - appendToken(recipientInfo.getProtocol().getVersion()); - appendSpace(); - - if (recipientInfo.getName() != null) { - append(recipientInfo.getName()); - - if (recipientInfo.getComment() != null) { - appendSpace(); - appendComment(recipientInfo.getComment()); - } - } else { - throw new IllegalArgumentException("The name (host or pseudonym) of a recipient can't be null"); - } - } else { - throw new IllegalArgumentException("The protocol of a recipient can't be null"); - } - - return this; - } - + /** + * Creates a via header from the given list of recipient info. + * + * @param recipientsInfo The list of recipient info. + * @return Returns the Via header. + */ + public static String write(List recipientsInfo) { + return new RecipientInfoWriter().append(recipientsInfo).toString(); + } + + @Override + public RecipientInfoWriter append(RecipientInfo recipientInfo) { + if (recipientInfo.getProtocol() != null) { + appendToken(recipientInfo.getProtocol().getName()); + append('/'); + appendToken(recipientInfo.getProtocol().getVersion()); + appendSpace(); + + if (recipientInfo.getName() != null) { + append(recipientInfo.getName()); + + if (recipientInfo.getComment() != null) { + appendSpace(); + appendComment(recipientInfo.getComment()); + } + } else { + throw new IllegalArgumentException( + "The name (host or pseudonym) of a recipient can't be null"); + } + } else { + throw new IllegalArgumentException("The protocol of a recipient can't be null"); + } + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java b/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java index 8334b5a7f3..13ba327a71 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java @@ -1,48 +1,45 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Header; - import java.io.IOException; import java.util.Collection; +import org.restlet.data.Header; /** * String header reader. - * + * * @author Manuel Boillod */ public class StringReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param collection The collection to update. - */ - public static void addValues(Header header, Collection collection) { - new StringReader(header.getValue()).addValues(collection); - } - - /** - * Constructor. - * - * @param header The header to read. - */ - public StringReader(String header) { - super(header); - } - - @Override - public String readValue() throws IOException { - return readToken(); - } - + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param collection The collection to update. + */ + public static void addValues(Header header, Collection collection) { + new StringReader(header.getValue()).addValues(collection); + } + + /** + * Constructor. + * + * @param header The header to read. + */ + public StringReader(String header) { + super(header); + } + + @Override + public String readValue() throws IOException { + return readToken(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java index cefecb7e30..f54b48d1c3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java @@ -1,36 +1,34 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; import java.util.Set; /** * String header writer. - * + * * @author Manuel Boillod */ public class StringWriter extends HeaderWriter { - /** - * Writes a set of values with a comma separator. - * - * @param values The set of values. - * @return The formatted set of values. - */ - public static String write(Set values) { - return new StringWriter().append(values).toString(); - } - - @Override - public StringWriter append(String value) { - return (StringWriter) appendToken(value); - } + /** + * Writes a set of values with a comma separator. + * + * @param values The set of values. + * @return The formatted set of values. + */ + public static String write(Set values) { + return new StringWriter().append(values).toString(); + } + @Override + public StringWriter append(String value) { + return (StringWriter) appendToken(value); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java b/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java index c517827e52..5178acbbe6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java @@ -1,49 +1,46 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Header; -import org.restlet.data.Tag; - import java.io.IOException; import java.util.Collection; +import org.restlet.data.Header; +import org.restlet.data.Tag; /** * Tag header reader. - * + * * @author Thierry Boileau */ public class TagReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param collection The collection to update. - */ - public static void addValues(Header header, Collection collection) { - new TagReader(header.getValue()).addValues(collection); - } - - /** - * Constructor. - * - * @param header The header to read. - */ - public TagReader(String header) { - super(header); - } - - @Override - public Tag readValue() throws IOException { - return Tag.parse(readRawValue()); - } - + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param collection The collection to update. + */ + public static void addValues(Header header, Collection collection) { + new TagReader(header.getValue()).addValues(collection); + } + + /** + * Constructor. + * + * @param header The header to read. + */ + public TagReader(String header) { + super(header); + } + + @Override + public Tag readValue() throws IOException { + return Tag.parse(readRawValue()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java index 5ede818710..6d570997a7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java @@ -1,48 +1,45 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.restlet.data.Tag; - import java.util.List; +import org.restlet.data.Tag; /** * Tag header writer. - * + * * @author Jerome Louvel */ public class TagWriter extends HeaderWriter { - /** - * Writes a list of tags. - * - * @param tags The tags to write. - * @return This writer. - */ - public static String write(List tags) { - return new TagWriter().append(tags).toString(); - } - - /** - * Writes a tag. - * - * @param tag The tag to write. - * @return This writer. - */ - public static String write(Tag tag) { - return tag.format(); - } - - @Override - public HeaderWriter append(Tag tag) { - return append(write(tag)); - } - + /** + * Writes a list of tags. + * + * @param tags The tags to write. + * @return This writer. + */ + public static String write(List tags) { + return new TagWriter().append(tags).toString(); + } + + /** + * Writes a tag. + * + * @param tag The tag to write. + * @return This writer. + */ + public static String write(Tag tag) { + return tag.format(); + } + + @Override + public HeaderWriter append(Tag tag) { + return append(write(tag)); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java b/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java index e472204a41..a76def013d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java @@ -1,35 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; import java.io.IOException; /** * Token header reader. - * + * * @author Jerome Louvel */ public class TokenReader extends HeaderReader { - /** - * Constructor. - * - * @param header The header to read. - */ - public TokenReader(String header) { - super(header); - } - - @Override - public String readValue() throws IOException { - return readToken(); - } + /** + * Constructor. + * + * @param header The header to read. + */ + public TokenReader(String header) { + super(header); + } + @Override + public String readValue() throws IOException { + return readToken(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java b/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java index 98df497f6f..90f68f4a60 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java @@ -1,76 +1,73 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.io.IOException; +import java.util.Collection; import org.restlet.data.Header; import org.restlet.data.Status; import org.restlet.data.Warning; import org.restlet.engine.util.DateUtils; -import java.io.IOException; -import java.util.Collection; - /** * Warning header reader. - * + * * @author Thierry Boileau */ public class WarningReader extends HeaderReader { - /** - * Adds values to the given collection. - * - * @param header The header to read. - * @param collection The collection to update. - */ - public static void addValues(Header header, Collection collection) { - new WarningReader(header.getValue()).addValues(collection); - } - - /** - * Constructor. - * - * @param header The header to read. - */ - public WarningReader(String header) { - super(header); - } + /** + * Adds values to the given collection. + * + * @param header The header to read. + * @param collection The collection to update. + */ + public static void addValues(Header header, Collection collection) { + new WarningReader(header.getValue()).addValues(collection); + } - @Override - public Warning readValue() throws IOException { - Warning result = new Warning(); + /** + * Constructor. + * + * @param header The header to read. + */ + public WarningReader(String header) { + super(header); + } - String code = readToken(); - skipSpaces(); - String agent = readRawText(); - skipSpaces(); - String text = readQuotedString(); - // The date is not mandatory - skipSpaces(); - String date = null; - if (peek() != -1) { - date = readQuotedString(); - } + @Override + public Warning readValue() throws IOException { + Warning result = new Warning(); - if ((code == null) || (agent == null) || (text == null)) { - throw new IOException("Warning header malformed."); - } + String code = readToken(); + skipSpaces(); + String agent = readRawText(); + skipSpaces(); + String text = readQuotedString(); + // The date is not mandatory + skipSpaces(); + String date = null; + if (peek() != -1) { + date = readQuotedString(); + } - result.setStatus(Status.valueOf(Integer.parseInt(code))); - result.setAgent(agent); - result.setText(text); - if (date != null) { - result.setDate(DateUtils.parse(date)); - } + if ((code == null) || (agent == null) || (text == null)) { + throw new IOException("Warning header malformed."); + } - return result; - } + result.setStatus(Status.valueOf(Integer.parseInt(code))); + result.setAgent(agent); + result.setText(text); + if (date != null) { + result.setDate(DateUtils.parse(date)); + } + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java index 3d7f3348b8..cea62bf122 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java @@ -1,64 +1,61 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import java.util.List; import org.restlet.data.Warning; import org.restlet.engine.util.DateUtils; -import java.util.List; - /** * Warning header writer. - * + * * @author Thierry Boileau */ public class WarningWriter extends HeaderWriter { - /** - * Writes a warning. - * - * @param warnings The list of warnings to format. - * @return The formatted warning. - */ - public static String write(List warnings) { - return new WarningWriter().append(warnings).toString(); - } - - @Override - public WarningWriter append(Warning warning) { - String agent = warning.getAgent(); - String text = warning.getText(); - - if (warning.getStatus() == null) { - throw new IllegalArgumentException("Can't write warning. Invalid status code detected"); - } - - if ((agent == null) || (agent.isEmpty())) { - throw new IllegalArgumentException("Can't write warning. Invalid agent detected"); - } - - if ((text == null) || (text.isEmpty())) { - throw new IllegalArgumentException("Can't write warning. Invalid text detected"); - } - - append(Integer.toString(warning.getStatus().getCode())); - append(" "); - append(agent); - append(" "); - appendQuotedString(text); - - if (warning.getDate() != null) { - appendQuotedString(DateUtils.format(warning.getDate())); - } - - return this; - } - + /** + * Writes a warning. + * + * @param warnings The list of warnings to format. + * @return The formatted warning. + */ + public static String write(List warnings) { + return new WarningWriter().append(warnings).toString(); + } + + @Override + public WarningWriter append(Warning warning) { + String agent = warning.getAgent(); + String text = warning.getText(); + + if (warning.getStatus() == null) { + throw new IllegalArgumentException("Can't write warning. Invalid status code detected"); + } + + if ((agent == null) || (agent.isEmpty())) { + throw new IllegalArgumentException("Can't write warning. Invalid agent detected"); + } + + if ((text == null) || (text.isEmpty())) { + throw new IllegalArgumentException("Can't write warning. Invalid text detected"); + } + + append(Integer.toString(warning.getStatus().getCode())); + append(" "); + append(agent); + append(" "); + appendQuotedString(text); + + if (warning.getDate() != null) { + appendQuotedString(DateUtils.format(warning.getDate())); + } + + return this; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java index 5803404e74..f53176c18a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java +++ b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java @@ -1,132 +1,152 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.internal; -import org.osgi.framework.*; +import java.net.URL; +import java.util.List; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; import org.restlet.Client; import org.restlet.Server; import org.restlet.engine.Engine; -import java.net.URL; -import java.util.List; - /** - * OSGi activator. It registers the NRE into the Restlet API and also introspect - * the bundles to find connector or authentication helpers. - * + * OSGi activator. It registers the NRE into the Restlet API and also introspect the bundles to find + * connector or authentication helpers. + * * @author Jerome Louvel */ public class Activator implements BundleActivator { - /** - * Registers the helpers for a given bundle. - * - * @param bundle The bundle to inspect. - * @param helpers The helpers list to update. - * @param constructorClass The class to use as constructor parameter. - * @param descriptorPath The descriptor file path. - */ - private void registerHelper(Bundle bundle, List helpers, Class constructorClass, String descriptorPath) { - // Discover server helpers - URL configUrl = bundle.getEntry(descriptorPath); - - if (configUrl == null) { - configUrl = bundle.getEntry("/src/" + descriptorPath); - } - - if (configUrl != null) { - registerHelper(bundle, helpers, constructorClass, configUrl); - } - } - - /** - * Registers the helpers for a given bundle. - * - * @param bundle The bundle to inspect. - * @param helpers The helpers list to update. - * @param constructorClass The class to use as constructor parameter. - * @param descriptorUrl The descriptor URL to inspect. - */ - private void registerHelper(final Bundle bundle, List helpers, Class constructorClass, URL descriptorUrl) { - Engine.getInstance().registerHelpers(new ClassLoader() { - @Override - public Class loadClass(String name) throws ClassNotFoundException { - return bundle.loadClass(name); - } - }, descriptorUrl, helpers, constructorClass); - } - - /** - * Registers the helpers for a given bundle. - * - * @param bundle The bundle to inspect. - */ - private void registerHelpers(Bundle bundle) { - // Register server helpers - registerHelper(bundle, Engine.getInstance().getRegisteredServers(), Server.class, - Engine.DESCRIPTOR_SERVER_PATH); - - // Register client helpers - registerHelper(bundle, Engine.getInstance().getRegisteredClients(), Client.class, - Engine.DESCRIPTOR_CLIENT_PATH); - - // Register authentication helpers - registerHelper(bundle, Engine.getInstance().getRegisteredAuthenticators(), null, - Engine.DESCRIPTOR_AUTHENTICATOR_PATH); - - // Register converter helpers - registerHelper(bundle, Engine.getInstance().getRegisteredConverters(), null, Engine.DESCRIPTOR_CONVERTER_PATH); - } - - /** - * Starts the OSGi bundle by registering the engine with the bundle of the - * Restlet API. - * - * @param context The bundle context. - */ - public void start(BundleContext context) throws Exception { - org.restlet.engine.Engine.register(false); - - // Discover helpers in installed bundles and start - // the bundle if necessary - for (final Bundle bundle : context.getBundles()) { - registerHelpers(bundle); - } - - // Listen to installed bundles - context.addBundleListener(new BundleListener() { - public void bundleChanged(BundleEvent event) { - switch (event.getType()) { - case BundleEvent.INSTALLED: - registerHelpers(event.getBundle()); - break; - - case BundleEvent.UNINSTALLED: - break; - } - } - }); - - Engine.getInstance().registerDefaultConnectors(); - Engine.getInstance().registerDefaultAuthentications(); - Engine.getInstance().registerDefaultConverters(); - } - - /** - * Stops the OSGi bundle by unregistering the engine with the bundle of the - * Restlet API. - * - * @param context The bundle context. - */ - public void stop(BundleContext context) throws Exception { - org.restlet.engine.Engine.clear(); - } - + /** + * Registers the helpers for a given bundle. + * + * @param bundle The bundle to inspect. + * @param helpers The helpers list to update. + * @param constructorClass The class to use as a constructor parameter. + * @param descriptorPath The descriptor file path. + */ + private void registerHelper( + Bundle bundle, List helpers, Class constructorClass, String descriptorPath) { + // Discover server helpers + URL configUrl = bundle.getEntry(descriptorPath); + + if (configUrl == null) { + configUrl = bundle.getEntry("/src/" + descriptorPath); + } + + if (configUrl != null) { + registerHelper(bundle, helpers, constructorClass, configUrl); + } + } + + /** + * Registers the helpers for a given bundle. + * + * @param bundle The bundle to inspect. + * @param helpers The helpers list to update. + * @param constructorClass The class to use as a constructor parameter. + * @param descriptorUrl The descriptor URL to inspect. + */ + private void registerHelper( + final Bundle bundle, List helpers, Class constructorClass, URL descriptorUrl) { + Engine.getInstance() + .registerHelpers( + new ClassLoader() { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return bundle.loadClass(name); + } + }, + descriptorUrl, + helpers, + constructorClass); + } + + /** + * Registers the helpers for a given bundle. + * + * @param bundle The bundle to inspect. + */ + private void registerHelpers(Bundle bundle) { + // Register server helpers + registerHelper( + bundle, + Engine.getInstance().getRegisteredServers(), + Server.class, + Engine.DESCRIPTOR_SERVER_PATH); + + // Register client helpers + registerHelper( + bundle, + Engine.getInstance().getRegisteredClients(), + Client.class, + Engine.DESCRIPTOR_CLIENT_PATH); + + // Register authentication helpers + registerHelper( + bundle, + Engine.getInstance().getRegisteredAuthenticators(), + null, + Engine.DESCRIPTOR_AUTHENTICATOR_PATH); + + // Register converter helpers + registerHelper( + bundle, + Engine.getInstance().getRegisteredConverters(), + null, + Engine.DESCRIPTOR_CONVERTER_PATH); + } + + /** + * Starts the OSGi bundle by registering the engine with the bundle of the Restlet API. + * + * @param context The bundle context. + */ + public void start(BundleContext context) throws Exception { + org.restlet.engine.Engine.register(false); + + // Discover helpers in installed bundles and start + // the bundle if necessary + for (final Bundle bundle : context.getBundles()) { + registerHelpers(bundle); + } + + // Listen to installed bundles + context.addBundleListener( + new BundleListener() { + public void bundleChanged(BundleEvent event) { + switch (event.getType()) { + case BundleEvent.INSTALLED: + registerHelpers(event.getBundle()); + break; + + case BundleEvent.UNINSTALLED: + break; + } + } + }); + + Engine.getInstance().registerDefaultConnectors(); + Engine.getInstance().registerDefaultAuthentications(); + Engine.getInstance().registerDefaultConverters(); + } + + /** + * Stops the OSGi bundle by unregistering the engine with the bundle of the Restlet API. + * + * @param context The bundle context. + */ + public void stop(BundleContext context) throws Exception { + org.restlet.engine.Engine.clear(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java index bceb55f382..9e35ec839a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java @@ -1,587 +1,603 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; +import static org.restlet.data.Range.isBytesRange; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Objects; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.data.CharacterSet; import org.restlet.data.Range; import org.restlet.engine.Engine; import org.restlet.representation.Representation; -import java.io.*; -import java.util.logging.Level; - -import static org.restlet.data.Range.isBytesRange; - /** * IO manipulation utilities. - * + * * @author Thierry Boileau */ public class IoUtils { - /** - * The size to use when instantiating buffered items such as instances of the - * {@link BufferedReader} class. It looks for the System property - * "org.restlet.engine.io.bufferSize" and if not defined, uses the "8192" - * default value. - */ - public static final int BUFFER_SIZE = getProperty("org.restlet.engine.io.bufferSize", 8192); - - /** Support for byte to hexa conversions. */ - private static final char[] HEXDIGITS = "0123456789ABCDEF".toCharArray(); - - /** - * The number of milliseconds after which IO operation will time out. It looks - * for the System property "org.restlet.engine.io.timeoutMs" and if not defined, - * uses the "60000" default value. - */ - public final static int TIMEOUT_MS = getProperty("org.restlet.engine.io.timeoutMs", 60000); - - /** - * Copies an input stream to an output stream. When the reading is done, the - * input stream is closed. - * - * @param inputStream The input stream. - * @param outputStream The output stream. - * @throws IOException - */ - public static void copy(InputStream inputStream, java.io.OutputStream outputStream) throws IOException { - if (inputStream != null) { - if (outputStream != null) { - inputStream.transferTo(outputStream); - outputStream.flush(); - inputStream.close(); - } else { - Context.getCurrentLogger().log(Level.FINE, - "Unable to copy input to output stream. Output stream is null."); - } - } else { - Context.getCurrentLogger().log(Level.FINE, "Unable to copy input to output stream. Input stream is null."); - } - } - - /** - * Copies an input stream to a random access file. When the reading is done, the - * input stream is closed. - * - * @param inputStream The input stream. - * @param randomAccessFile The random access file. - * @throws IOException - */ - public static void copy(InputStream inputStream, java.io.RandomAccessFile randomAccessFile) throws IOException { - int bytesRead; - byte[] buffer = new byte[2048]; - - while ((bytesRead = inputStream.read(buffer)) > 0) { - randomAccessFile.write(buffer, 0, bytesRead); - } - - inputStream.close(); - } - - /** - * Copies characters from a reader to a writer. When the reading is done, the - * reader is closed. - * - * @param reader The reader. - * @param writer The writer. - * @throws IOException - */ - public static void copy(Reader reader, java.io.Writer writer) throws IOException { - reader.transferTo(writer); - writer.flush(); - reader.close(); - } - - /** - * Deletes an individual file or an empty directory. - * - * @param file The individual file or directory to delete. - * @return True if the deletion was successful. - */ - public static boolean delete(java.io.File file) { - return IoUtils.delete(file, false); - } - - /** - * Deletes an individual file or a directory. A recursive deletion can be forced - * as well. Under Windows operating systems, the garbage collector will be - * invoked once before attempting to delete in order to prevent locking issues. - * - * @param file The individual file or directory to delete. - * @param recursive Indicates if directory with content should be deleted - * recursively as well. - * @return True if the deletion was successful or if the file or directory - * didn't exist. - */ - public static boolean delete(java.io.File file, boolean recursive) { - String osName = System.getProperty("os.name").toLowerCase(); - return IoUtils.delete(file, recursive, osName.startsWith("windows")); - } - - /** - * Deletes an individual file or a directory. A recursive deletion can be forced - * as well. The garbage collector can be run once before attempting to delete, - * to workaround lock issues under Windows operating systems. - * - * @param file The individual file or directory to delete. - * @param recursive Indicates if directory with content should be deleted - * recursively as well. - * @param garbageCollect Indicates if the garbage collector should be run. - * @return True if the deletion was successful or if the file or directory - * didn't exist. - */ - public static boolean delete(java.io.File file, boolean recursive, boolean garbageCollect) { - boolean result = true; - boolean runGC = garbageCollect; - - if (file.exists()) { - if (file.isDirectory()) { - java.io.File[] entries = file.listFiles(); - - // Check if the directory is empty - if (entries.length > 0) { - if (recursive) { - for (int i = 0; result && (i < entries.length); i++) { - if (runGC) { - System.gc(); - runGC = false; - } - - result = delete(entries[i], true, false); - } - } else { - result = false; - } - } - } - - if (runGC) { - System.gc(); - runGC = false; - } - - result = result && file.delete(); - } - - return result; - } - - /** - * Exhaust the content of the representation by reading it and silently - * discarding anything read. - * - * @param input The input stream to exhaust. - * @return The number of bytes consumed or -1 if unknown. - */ - public static long exhaust(InputStream input) throws IOException { - long result = -1L; - - if (input != null) { - byte[] buf = new byte[2048]; - int read = input.read(buf); - result = (read == -1) ? -1 : 0; - - while (read != -1) { - result += read; - read = input.read(buf); - } - } - - return result; - } - - /** - * Returns the size effectively available. This returns the same value as - * {@link Representation#getSize()} if no range is defined, otherwise it returns - * the size of the range using {@link Range#getSize()}. - * - * @param representation The representation to evaluate. - * @return The available size. - */ - public static long getAvailableSize(Representation representation) { - Range range = representation.getRange(); - if (range == null || !isBytesRange(range)) { - return representation.getSize(); - } else if (range.getSize() != Range.SIZE_MAX) { - if (representation.hasKnownSize()) { - return Math.min(range.getIndex() + range.getSize(), representation.getSize()) - range.getIndex(); - } else { - return Representation.UNKNOWN_SIZE; - } - } else if (representation.hasKnownSize()) { - if (range.getIndex() != Range.INDEX_LAST) { - return representation.getSize() - range.getIndex(); - } - - return representation.getSize(); - } - - return Representation.UNKNOWN_SIZE; - } - - private static int getProperty(String name, int defaultValue) { - int result = defaultValue; - - try { - result = Integer.parseInt(System.getProperty(name)); - } catch (NumberFormatException nfe) { - result = defaultValue; - } - - return result; - } - - /** - * Returns a reader from an input stream and a character set. - * - * @param stream The input stream. - * @param characterSet The character set. May be null. - * @return The equivalent reader. - * @throws UnsupportedEncodingException if a character set is given, but not - * supported - */ - public static Reader getReader(InputStream stream, CharacterSet characterSet) throws UnsupportedEncodingException { - if (characterSet != null) { - return new InputStreamReader(stream, characterSet.getName()); - } - - return new InputStreamReader(stream); - } - - /** - * Returns a reader from a writer representation.Internally, it uses a writer - * thread and a pipe stream. - * - * @param representation The representation to read from. - * @return The character reader. - * @throws IOException - */ - public static Reader getReader(final org.restlet.representation.WriterRepresentation representation) - throws IOException { - Reader result = null; - final java.io.PipedWriter pipedWriter = new java.io.PipedWriter(); - - java.io.PipedReader pipedReader = new java.io.PipedReader(pipedWriter); - - // Gets a thread that will handle the task of continuously - // writing the representation into the input side of the pipe - Runnable task = new org.restlet.engine.util.ContextualRunnable() { - public void run() { - try { - representation.write(pipedWriter); - pipedWriter.flush(); - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.WARNING, "Error while writing to the piped reader.", ioe); - } finally { - try { - pipedWriter.close(); - } catch (IOException ioe2) { - Context.getCurrentLogger().log(Level.WARNING, "Error while closing the pipe.", ioe2); - } - } - } - }; - - org.restlet.Context context = org.restlet.Context.getCurrent(); - - if (context != null && context.getExecutorService() != null) { - context.getExecutorService().execute(task); - } else { - Engine.createThreadWithLocalVariables(task, "Restlet-IoUtils").start(); - } - - result = pipedReader; - - return result; - - } - - /** - * Returns an output stream based on a given writer. - * - * @param writer The writer. - * @param characterSet The character set used to write on the output stream. - * @return the output stream of the writer - */ - public static java.io.OutputStream getStream(java.io.Writer writer, CharacterSet characterSet) { - return new WriterOutputStream(writer, characterSet); - } - - /** - * Returns an input stream based on a given character reader. - * - * @param reader The character reader. - * @param characterSet The stream character set. - * @return An input stream based on a given character reader. - */ - public static InputStream getStream(Reader reader, CharacterSet characterSet) { - InputStream result = null; - - try { - result = new ReaderInputStream(reader, characterSet); - } catch (IOException e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to create the reader input stream", e); - } - - return result; - } - - /** - * Returns an input stream based on the given representation's content and its - * write(OutputStream) method. Internally, it uses a writer thread and a pipe - * stream. - * - * @param representation the representation to get the - * {@link java.io.OutputStream} from. - * @return A stream with the representation's content. - */ - public static InputStream getStream(final Representation representation) { - InputStream result = null; - - if (representation == null) { - return null; - } - - final PipeStream pipe = new PipeStream(); - final java.io.OutputStream os = pipe.getOutputStream(); - - // Creates a thread that will handle the task of continuously - // writing the representation into the input side of the pipe - Runnable task = new org.restlet.engine.util.ContextualRunnable() { - public void run() { - try { - representation.write(os); - os.flush(); - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.WARNING, "Error while writing to the piped input stream.", - ioe); - } finally { - try { - os.close(); - } catch (IOException ioe2) { - Context.getCurrentLogger().log(Level.WARNING, "Error while closing the pipe.", ioe2); - } - } - } - }; - - org.restlet.Context context = org.restlet.Context.getCurrent(); - - if (context != null && context.getExecutorService() != null) { - context.getExecutorService().execute(task); - } else { - Engine.createThreadWithLocalVariables(task, "Restlet-IoUtils").start(); - } - - result = pipe.getInputStream(); - - return result; - } - - /** - * Converts the representation to a string value. Be careful when using this - * method as the conversion of large content to a string fully stored in memory - * can result in OutOfMemoryErrors being thrown. - * - * @param representation The representation to convert. - * @return The representation as a string value. - */ - public static String getText(Representation representation) throws IOException { - String result = null; - - if (representation.isAvailable()) { - if (representation.getSize() == 0) { - result = ""; - } else { - java.io.StringWriter sw = new java.io.StringWriter(); - representation.write(sw); - sw.flush(); - result = sw.toString(); - } - } - - return result; - } - - /** - * Returns a writer to the given output stream, using the given character set - * for encoding to bytes. - * - * @param outputStream The target output stream. - * @param characterSet The character set for encoding. - * @return The wrapping writer. - */ - public static Writer getWriter(OutputStream outputStream, CharacterSet characterSet) { - Writer result = null; - - if (characterSet != null) { - result = new OutputStreamWriter(outputStream, characterSet.toCharset()); - } else { - // Use the default HTTP character set - result = new OutputStreamWriter(outputStream, CharacterSet.ISO_8859_1.toCharset()); - } - - return result; - } - - /** - * Converts a char array into a byte array using the default character set. - * - * @param chars The source characters. - * @return The result bytes. - */ - public static byte[] toByteArray(char[] chars) { - return IoUtils.toByteArray(chars, java.nio.charset.Charset.defaultCharset().name()); - } - - /** - * Converts a char array into a byte array using the provided character set. - * - * @param chars The source characters. - * @param charsetName The character set to use. - * @return The result bytes. - */ - public static byte[] toByteArray(char[] chars, String charsetName) { - java.nio.CharBuffer cb = java.nio.CharBuffer.wrap(chars); - java.nio.ByteBuffer bb = java.nio.charset.Charset.forName(charsetName).encode(cb); - byte[] r = new byte[bb.remaining()]; - bb.get(r); - return r; - } - - /** - * Converts a byte array into a character array using the default character set. - * - * @param bytes The source bytes. - * @return The result characters. - */ - public static char[] toCharArray(byte[] bytes) { - return IoUtils.toCharArray(bytes, java.nio.charset.Charset.defaultCharset().name()); - } - - /** - * Converts a byte array into a character array using the default character set. - * - * @param bytes The source bytes. - * @param charsetName The character set to use. - * @return The result characters. - */ - public static char[] toCharArray(byte[] bytes, String charsetName) { - java.nio.ByteBuffer bb = java.nio.ByteBuffer.wrap(bytes); - java.nio.CharBuffer cb = java.nio.charset.Charset.forName(charsetName).decode(bb); - char[] r = new char[cb.remaining()]; - cb.get(r); - return r; - } - - /** - * Converts a byte array into an hexadecimal string. - * - * @param byteArray The byte array to convert. - * @return The hexadecimal string. - */ - public static String toHexString(byte[] byteArray) { - final char[] hexChars = new char[2 * byteArray.length]; - int i = 0; - - for (final byte b : byteArray) { - hexChars[i++] = HEXDIGITS[(b >> 4) & 0xF]; - hexChars[i++] = HEXDIGITS[b & 0xF]; - } - - return new String(hexChars); - } - - /** - * Converts an input stream to a string.
- * As this method uses the InputStreamReader class, the default character set is - * used for decoding the input stream. - * - * @see InputStreamReader - * @see IoUtils#toString(InputStream, CharacterSet) - * @param inputStream The input stream. - * @return The converted string. - */ - public static String toString(InputStream inputStream) { - return IoUtils.toString(inputStream, null); - } - - /** - * Converts an input stream to a string using the specified character set for - * decoding the input stream. Once read, the input stream is closed. - * - * @see InputStreamReader - * @param inputStream The input stream. - * @param characterSet The character set - * @return The converted string. - */ - public static String toString(InputStream inputStream, CharacterSet characterSet) { - String result = null; - - if (inputStream != null) { - try { - if (characterSet != null) { - result = IoUtils.toString(new InputStreamReader(inputStream, characterSet.getName())); - } else { - result = IoUtils.toString(new InputStreamReader(inputStream)); - } - - inputStream.close(); - } catch (Exception e) { - // Returns an empty string - } - } - - return result; - } - - /** - * Converts a reader to a string. - * - * @see InputStreamReader - * - * @param reader The characters reader. - * @return The converted string. - */ - public static String toString(Reader reader) { - String result = null; - - if (reader != null) { - try { - StringBuilder sb = new StringBuilder(); - BufferedReader br = (reader instanceof BufferedReader) ? (BufferedReader) reader - : new BufferedReader(reader, BUFFER_SIZE); - char[] buffer = new char[2048]; - int charsRead = br.read(buffer); - - while (charsRead != -1) { - sb.append(buffer, 0, charsRead); - charsRead = br.read(buffer); - } - - br.close(); - result = sb.toString(); - } catch (Exception e) { - // Returns an empty string - } - } - - return result; - } - - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private IoUtils() { - } + /** + * The size to use when instantiating buffered items such as instances of the {@link + * BufferedReader} class. It looks for the System property "org.restlet.engine.io.bufferSize" + * and if not defined, uses the "8192" default value. + */ + public static final int BUFFER_SIZE = getProperty("org.restlet.engine.io.bufferSize", 8192); + + /** Support for byte to hexa conversions. */ + private static final char[] HEXDIGITS = "0123456789ABCDEF".toCharArray(); + + /** + * The number of milliseconds after which IO operation will time out. It looks for the System + * property "org.restlet.engine.io.timeoutMs" and if not defined, uses the "60000" default + * value. + */ + public static final int TIMEOUT_MS = getProperty("org.restlet.engine.io.timeoutMs", 60000); + + /** + * Copies an input stream to an output stream. When the reading is done, the input stream is + * closed. + * + * @param inputStream The input stream. + * @param outputStream The output stream. + * @throws IOException + */ + public static void copy(InputStream inputStream, java.io.OutputStream outputStream) + throws IOException { + if (inputStream != null) { + if (outputStream != null) { + inputStream.transferTo(outputStream); + outputStream.flush(); + inputStream.close(); + } else { + Context.getCurrentLogger() + .log( + Level.FINE, + "Unable to copy input to output stream. Output stream is null."); + } + } else { + Context.getCurrentLogger() + .log( + Level.FINE, + "Unable to copy input to output stream. Input stream is null."); + } + } + + /** + * Copies an input stream to a random access file. When the reading is done, the input stream is + * closed. + * + * @param inputStream The input stream. + * @param randomAccessFile The random access file. + * @throws IOException + */ + public static void copy(InputStream inputStream, java.io.RandomAccessFile randomAccessFile) + throws IOException { + int bytesRead; + byte[] buffer = new byte[2048]; + + while ((bytesRead = inputStream.read(buffer)) > 0) { + randomAccessFile.write(buffer, 0, bytesRead); + } + + inputStream.close(); + } + + /** + * Copies characters from a reader to a writer. When the reading is done, the reader is closed. + * + * @param reader The reader. + * @param writer The writer. + * @throws IOException + */ + public static void copy(Reader reader, java.io.Writer writer) throws IOException { + reader.transferTo(writer); + writer.flush(); + reader.close(); + } + + /** + * Deletes an individual file or an empty directory. + * + * @param file The individual file or directory to delete. + * @return True if the deletion was successful. + */ + public static boolean delete(java.io.File file) { + return IoUtils.delete(file, false); + } + + /** + * Deletes an individual file or a directory. A recursive deletion can be forced as well. Under + * Windows operating systems, the garbage collector will be invoked once before attempting to + * delete to prevent locking issues. + * + * @param file The individual file or directory to delete. + * @param recursive Indicates if a directory with content should be deleted recursively as well. + * @return True if the deletion was successful or if the file or directory didn't exist. + */ + public static boolean delete(java.io.File file, boolean recursive) { + String osName = System.getProperty("os.name").toLowerCase(); + return IoUtils.delete(file, recursive, osName.startsWith("windows")); + } + + /** + * Deletes an individual file or a directory. A recursive deletion can be forced as well. The + * garbage collector can be run once before attempting to delete, to workaround lock issues + * under Windows operating systems. + * + * @param file The individual file or directory to delete. + * @param recursive Indicates if a directory with content should be deleted recursively as well. + * @param garbageCollect Indicates if the garbage collector should be run. + * @return True if the deletion was successful or if the file or directory didn't exist. + */ + public static boolean delete(java.io.File file, boolean recursive, boolean garbageCollect) { + boolean result = true; + boolean runGC = garbageCollect; + + if (file.exists()) { + if (file.isDirectory()) { + java.io.File[] entries = file.listFiles(); + + // Check if the directory is empty + if (entries != null && entries.length > 0) { + if (recursive) { + for (int i = 0; result && (i < entries.length); i++) { + if (runGC) { + System.gc(); + runGC = false; + } + + result = delete(entries[i], true, false); + } + } else { + result = false; + } + } + } + + if (runGC) { + System.gc(); + runGC = false; + } + + result = result && file.delete(); + } + + return result; + } + + /** + * Exhaust the content of the representation by reading it and silently discarding anything + * read. + * + * @param input The input stream to exhaust. + * @return The number of bytes consumed or -1 if unknown. + */ + public static long exhaust(InputStream input) throws IOException { + long result = -1L; + + if (input != null) { + byte[] buf = new byte[2048]; + int read = input.read(buf); + result = (read == -1) ? -1 : 0; + + while (read != -1) { + result += read; + read = input.read(buf); + } + } + + return result; + } + + /** + * Returns the size effectively available. This returns the same value as {@link + * Representation#getSize()} if no range is defined, otherwise it returns the size of the range + * using {@link Range#getSize()}. + * + * @param representation The representation to evaluate. + * @return The available size. + */ + public static long getAvailableSize(Representation representation) { + Range range = representation.getRange(); + if (range == null || !isBytesRange(range)) { + return representation.getSize(); + } else if (range.getSize() != Range.SIZE_MAX) { + if (representation.hasKnownSize()) { + return Math.min(range.getIndex() + range.getSize(), representation.getSize()) + - range.getIndex(); + } else { + return Representation.UNKNOWN_SIZE; + } + } else if (representation.hasKnownSize()) { + if (range.getIndex() != Range.INDEX_LAST) { + return representation.getSize() - range.getIndex(); + } + + return representation.getSize(); + } + + return Representation.UNKNOWN_SIZE; + } + + private static int getProperty(String name, int defaultValue) { + int result; + + try { + result = Integer.parseInt(System.getProperty(name)); + } catch (NumberFormatException nfe) { + result = defaultValue; + } + + return result; + } + + /** + * Returns a reader from an input stream and a character set. + * + * @param stream The input stream. + * @param characterSet The character set. Maybe null. + * @return The equivalent reader. + * @throws UnsupportedEncodingException if a character set is given but not supported + */ + public static Reader getReader(InputStream stream, CharacterSet characterSet) + throws UnsupportedEncodingException { + if (characterSet != null) { + return new InputStreamReader(stream, characterSet.getName()); + } + + return new InputStreamReader(stream); + } + + /** + * Returns a reader from a writer representation. Internally, it uses a writer thread and a pipe + * stream. + * + * @param representation The representation to read from. + * @return The character reader. + * @throws IOException + */ + public static Reader getReader( + final org.restlet.representation.WriterRepresentation representation) + throws IOException { + Reader result = null; + final java.io.PipedWriter pipedWriter = new java.io.PipedWriter(); + + java.io.PipedReader pipedReader = new java.io.PipedReader(pipedWriter); + + // Gets a thread that will handle the task of continuously + // writing the representation into the input side of the pipe + Runnable task = + new org.restlet.engine.util.ContextualRunnable() { + public void run() { + try { + representation.write(pipedWriter); + pipedWriter.flush(); + } catch (IOException ioe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Error while writing to the piped reader.", + ioe); + } finally { + try { + pipedWriter.close(); + } catch (IOException ioe2) { + Context.getCurrentLogger() + .log(Level.WARNING, "Error while closing the pipe.", ioe2); + } + } + } + }; + + org.restlet.Context context = org.restlet.Context.getCurrent(); + + if (context != null && context.getExecutorService() != null) { + context.getExecutorService().execute(task); + } else { + Engine.createThreadWithLocalVariables(task, "Restlet-IoUtils").start(); + } + + result = pipedReader; + + return result; + } + + /** + * Returns an output stream based on a given writer. + * + * @param writer The writer. + * @param characterSet The character set used to write on the output stream. + * @return the output stream of the writer + */ + public static java.io.OutputStream getStream(java.io.Writer writer, CharacterSet characterSet) { + return new WriterOutputStream(writer, characterSet); + } + + /** + * Returns an input stream based on a given character reader. + * + * @param reader The character reader. + * @param characterSet The stream character set. + * @return An input stream based on a given character reader. + */ + public static InputStream getStream(Reader reader, CharacterSet characterSet) { + InputStream result = null; + + try { + result = new ReaderInputStream(reader, characterSet); + } catch (IOException e) { + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to create the reader input stream", e); + } + + return result; + } + + /** + * Returns an input stream based on the given representation's content and its + * write(OutputStream) method. Internally, it uses a writer thread and a pipe stream. + * + * @param representation the representation to get the {@link java.io.OutputStream} from. + * @return A stream with the representation's content. + */ + public static InputStream getStream(final Representation representation) { + InputStream result = null; + + if (representation == null) { + return null; + } + + final PipeStream pipe = new PipeStream(); + final java.io.OutputStream os = pipe.getOutputStream(); + + // Creates a thread that will handle the task of continuously + // writing the representation into the input side of the pipe + Runnable task = + new org.restlet.engine.util.ContextualRunnable() { + public void run() { + try { + representation.write(os); + os.flush(); + } catch (IOException ioe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Error while writing to the piped input stream.", + ioe); + } finally { + try { + os.close(); + } catch (IOException ioe2) { + Context.getCurrentLogger() + .log(Level.WARNING, "Error while closing the pipe.", ioe2); + } + } + } + }; + + org.restlet.Context context = org.restlet.Context.getCurrent(); + + if (context != null && context.getExecutorService() != null) { + context.getExecutorService().execute(task); + } else { + Engine.createThreadWithLocalVariables(task, "Restlet-IoUtils").start(); + } + + result = pipe.getInputStream(); + + return result; + } + + /** + * Converts the representation to a string value. Be careful when using this method as the + * conversion of large content to a string fully stored in memory can result in + * OutOfMemoryErrors being thrown. + * + * @param representation The representation to convert. + * @return The representation as a string value. + */ + public static String getText(Representation representation) throws IOException { + String result = null; + + if (representation.isAvailable()) { + if (representation.getSize() == 0) { + result = ""; + } else { + java.io.StringWriter sw = new java.io.StringWriter(); + representation.write(sw); + sw.flush(); + result = sw.toString(); + } + } + + return result; + } + + /** + * Returns a writer to the given output stream, using the given character set for encoding to + * bytes. + * + * @param outputStream The target output stream. + * @param characterSet The character set for encoding. + * @return The wrapping writer. + */ + public static Writer getWriter(OutputStream outputStream, CharacterSet characterSet) { + // Use the default HTTP character set if necessary + final Charset charset = + Objects.requireNonNullElse(characterSet, CharacterSet.ISO_8859_1).toCharset(); + return new OutputStreamWriter(outputStream, charset); + } + + /** + * Converts a char array into a byte array using the default character set. + * + * @param chars The source characters. + * @return The result bytes. + */ + public static byte[] toByteArray(char[] chars) { + return IoUtils.toByteArray(chars, java.nio.charset.Charset.defaultCharset().name()); + } + + /** + * Converts a char array into a byte array using the provided character set. + * + * @param chars The source characters. + * @param charsetName The character set to use. + * @return The result bytes. + */ + public static byte[] toByteArray(char[] chars, String charsetName) { + java.nio.CharBuffer cb = java.nio.CharBuffer.wrap(chars); + java.nio.ByteBuffer bb = java.nio.charset.Charset.forName(charsetName).encode(cb); + byte[] r = new byte[bb.remaining()]; + bb.get(r); + return r; + } + + /** + * Converts a byte array into a character array using the default character set. + * + * @param bytes The source bytes. + * @return The result characters. + */ + public static char[] toCharArray(byte[] bytes) { + return IoUtils.toCharArray(bytes, java.nio.charset.Charset.defaultCharset().name()); + } + + /** + * Converts a byte array into a character array using the default character set. + * + * @param bytes The source bytes. + * @param charsetName The character set to use. + * @return The result characters. + */ + public static char[] toCharArray(byte[] bytes, String charsetName) { + java.nio.ByteBuffer bb = java.nio.ByteBuffer.wrap(bytes); + java.nio.CharBuffer cb = java.nio.charset.Charset.forName(charsetName).decode(bb); + char[] r = new char[cb.remaining()]; + cb.get(r); + return r; + } + + /** + * Converts a byte array into a hexadecimal string. + * + * @param byteArray The byte array to convert. + * @return The hexadecimal string. + */ + public static String toHexString(byte[] byteArray) { + final char[] hexChars = new char[2 * byteArray.length]; + int i = 0; + + for (final byte b : byteArray) { + hexChars[i++] = HEXDIGITS[(b >> 4) & 0xF]; + hexChars[i++] = HEXDIGITS[b & 0xF]; + } + + return new String(hexChars); + } + + /** + * Converts an input stream to a string.
+ * As this method uses the InputStreamReader class, the default character set is used for + * decoding the input stream. + * + * @see InputStreamReader + * @see IoUtils#toString(InputStream, CharacterSet) + * @param inputStream The input stream. + * @return The converted string. + */ + public static String toString(InputStream inputStream) { + return IoUtils.toString(inputStream, null); + } + + /** + * Converts an input stream to a string using the specified character set for decoding the input + * stream. Once read, the input stream is closed. + * + * @see InputStreamReader + * @param inputStream The input stream. + * @param characterSet The character set + * @return The converted string. + */ + public static String toString(InputStream inputStream, CharacterSet characterSet) { + String result = null; + + if (inputStream != null) { + try { + if (characterSet != null) { + result = + IoUtils.toString( + new InputStreamReader(inputStream, characterSet.getName())); + } else { + result = IoUtils.toString(new InputStreamReader(inputStream)); + } + + inputStream.close(); + } catch (Exception e) { + // Returns an empty string + } + } + + return result; + } + + /** + * Converts a reader to a string. + * + * @see InputStreamReader + * @param reader The characters' reader. + * @return The converted string. + */ + public static String toString(Reader reader) { + String result = null; + + if (reader != null) { + try { + StringBuilder sb = new StringBuilder(); + BufferedReader br = + (reader instanceof BufferedReader) + ? (BufferedReader) reader + : new BufferedReader(reader, BUFFER_SIZE); + char[] buffer = new char[2048]; + int charsRead = br.read(buffer); + + while (charsRead != -1) { + sb.append(buffer, 0, charsRead); + charsRead = br.read(buffer); + } + + br.close(); + result = sb.toString(); + } catch (Exception e) { + // Returns an empty string + } + } + + return result; + } + + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private IoUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java b/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java index d81151b765..db4ac9770b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; import java.io.IOException; @@ -17,84 +16,86 @@ import java.util.concurrent.TimeUnit; /** - * Pipe stream that pipes output streams into input streams. Implementation - * based on a shared synchronized queue. - * + * Pipe stream that pipes output streams into input streams. Implementation based on a shared + * synchronized queue. + * * @author Jerome Louvel */ public class PipeStream { - /** The queue timeout. */ - private static final long QUEUE_TIMEOUT = 5; - - /** The supporting synchronized queue. */ - private final BlockingQueue queue; + /** The queue timeout. */ + private static final long QUEUE_TIMEOUT = 5; - /** Constructor. */ - public PipeStream() { - this.queue = new ArrayBlockingQueue(1024); - } + /** The supporting synchronized queue. */ + private final BlockingQueue queue; - /** - * Returns a new input stream that can read from the pipe. - * - * @return A new input stream that can read from the pipe. - */ - public InputStream getInputStream() { - return new InputStream() { - private boolean endReached = false; + /** Constructor. */ + public PipeStream() { + this.queue = new ArrayBlockingQueue<>(1024); + } - @Override - public int read() throws IOException { - try { - if (this.endReached) { - return -1; - } + /** + * Returns a new input stream that can read from the pipe. + * + * @return A new input stream that can read from the pipe. + */ + public InputStream getInputStream() { + return new InputStream() { + private boolean endReached = false; - final Integer value = queue.poll(QUEUE_TIMEOUT, TimeUnit.SECONDS); + @Override + public int read() throws IOException { + try { + if (this.endReached) { + return -1; + } - if (value == null) { - throw new IOException("Timeout while reading from the queue-based input stream"); - } else { - this.endReached = (value == -1); - return value; - } - } catch (InterruptedException ie) { - throw new IOException("Interruption occurred while writing in the queue"); - } - } - }; - } + final Integer value = queue.poll(QUEUE_TIMEOUT, TimeUnit.SECONDS); - /** - * Returns a new output stream that can write into the pipe. - * - * @return A new output stream that can write into the pipe. - */ - public OutputStream getOutputStream() { - return new OutputStream() { - @Override - public void write(int b) throws IOException { - try { - if (!queue.offer(b & 0xff, QUEUE_TIMEOUT, TimeUnit.SECONDS)) { - throw new IOException("Timeout while writing to the queue-based output stream"); - } - } catch (InterruptedException ie) { - throw new IOException("Interruption occurred while writing in the queue"); - } - } + if (value == null) { + throw new IOException( + "Timeout while reading from the queue-based input stream"); + } else { + this.endReached = (value == -1); + return value; + } + } catch (InterruptedException ie) { + throw new IOException("Interruption occurred while writing in the queue"); + } + } + }; + } - @Override - public void close() throws IOException { - try { - if (!queue.offer(-1, QUEUE_TIMEOUT, TimeUnit.SECONDS)) { - throw new IOException("Timeout while writing to the queue-based output stream"); - } - } catch (InterruptedException ie) { - throw new IOException("Interruption occurred while writing in the queue"); - } - } - }; - } + /** + * Returns a new output stream that can write into the pipe. + * + * @return A new output stream that can write into the pipe. + */ + public OutputStream getOutputStream() { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + try { + if (!queue.offer(b & 0xff, QUEUE_TIMEOUT, TimeUnit.SECONDS)) { + throw new IOException( + "Timeout while writing to the queue-based output stream"); + } + } catch (InterruptedException ie) { + throw new IOException("Interruption occurred while writing in the queue"); + } + } + @Override + public void close() throws IOException { + try { + if (!queue.offer(-1, QUEUE_TIMEOUT, TimeUnit.SECONDS)) { + throw new IOException( + "Timeout while writing to the queue-based output stream"); + } + } catch (InterruptedException ie) { + throw new IOException("Interruption occurred while writing in the queue"); + } + } + }; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java index 591ac21a01..b093cc86e4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java @@ -1,167 +1,171 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; -import org.restlet.data.Range; -import org.restlet.representation.Representation; - import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import org.restlet.data.Range; +import org.restlet.representation.Representation; /** * Filters an input stream to expose only a given range. - * + * * @author Jerome Louvel */ public class RangeInputStream extends FilterInputStream { - /** The current position. */ - private volatile long position; - - /** The range to satisfy. */ - private volatile Range range; - - /** The total size of the source stream. */ - private volatile long totalSize; - - /** The start index inside the source stream. */ - private final long startIndex; - - /** The end index inside the source stream. */ - private final long endIndex; - - /** The range size available. */ - private volatile int availableSize; - - /** - * Constructs a stream exposing only a range of a given source stream. - * - * @param in The source input stream. - * @param totalSize The total size of the source stream. - * @param range The range to satisfy. - */ - public RangeInputStream(InputStream in, long totalSize, Range range) { - super(in); - this.range = range; - this.position = 0; - this.totalSize = totalSize; - this.availableSize = (int) range.getSize(); - - if (totalSize == Representation.UNKNOWN_SIZE) { - if (range.getIndex() == Range.INDEX_LAST) { - if (range.getSize() == Range.SIZE_MAX) { - // Read the whole stream - this.startIndex = -1; - this.endIndex = -1; - } else { - throw new IllegalArgumentException("Can't determine the start and end index."); - } - } else { - if (range.getSize() == Range.SIZE_MAX) { - this.startIndex = range.getIndex(); - this.endIndex = -1; - } else { - this.startIndex = range.getIndex(); - this.endIndex = range.getIndex() + range.getSize() - 1; - } - } - } else { - if (range.getIndex() == Range.INDEX_LAST) { - if (range.getSize() == Range.SIZE_MAX) { - this.startIndex = -1; - this.endIndex = -1; - } else { - this.startIndex = totalSize - range.getSize(); - this.endIndex = -1; - } - } else { - if (range.getSize() == Range.SIZE_MAX) { - this.startIndex = range.getIndex(); - this.endIndex = -1; - } else { - this.startIndex = range.getIndex(); - this.endIndex = range.getIndex() + range.getSize() - 1; - } - } - } - } - - @Override - public int available() throws IOException { - return this.availableSize; - } - - @Override - public synchronized void mark(int readlimit) { - if (range.getIndex() == Range.INDEX_LAST) { - super.mark(readlimit + (int) (totalSize - range.getSize())); - } else { - super.mark(readlimit + (int) range.getIndex()); - } - } - - @Override - public int read() throws IOException { - int result = super.read(); - - while ((result != -1) && !this.range.isIncluded(position++, totalSize)) { - result = super.read(); - } - - if ((result != -1) && (this.availableSize > 0)) { - this.availableSize--; - } - - return result; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - // Reach the start index. - while (!(position >= startIndex)) { - long skipped = skip(startIndex - position); - - if (skipped <= 0) { - throw new IOException("Cannot skip ahead in FilterInputStream"); - } - - position += skipped; - } - - int result = -1; - - if (endIndex != -1) { - // Read up until the end index - if (position > endIndex) { - // The end index is reached. - result = -1; - } else { - // Take care to read the right number of bytes according to the - // end index and the buffer size. - result = super.read(b, off, ((position + len) > endIndex) ? (int) (endIndex - position + 1) : len); - } - } else { - // Read normally up until the end of the stream. - result = super.read(b, off, len); - } - - if (result > 0) { - // Move the cursor. - position += result; - } - - if ((result != -1) && (this.availableSize > 0)) { - this.availableSize -= result; - } - - return result; - } + /** The current position. */ + private volatile long position; + + /** The range to satisfy. */ + private volatile Range range; + + /** The total size of the source stream. */ + private volatile long totalSize; + + /** The start index inside the source stream. */ + private final long startIndex; + + /** The end index inside the source stream. */ + private final long endIndex; + + /** The range size available. */ + private volatile int availableSize; + + /** + * Constructs a stream exposing only a range of a given source stream. + * + * @param in The source input stream. + * @param totalSize The total size of the source stream. + * @param range The range to satisfy. + */ + public RangeInputStream(InputStream in, long totalSize, Range range) { + super(in); + this.range = range; + this.position = 0; + this.totalSize = totalSize; + this.availableSize = (int) range.getSize(); + + if (totalSize == Representation.UNKNOWN_SIZE) { + if (range.getIndex() == Range.INDEX_LAST) { + if (range.getSize() == Range.SIZE_MAX) { + // Read the whole stream + this.startIndex = -1; + this.endIndex = -1; + } else { + throw new IllegalArgumentException("Can't determine the start and end index."); + } + } else { + if (range.getSize() == Range.SIZE_MAX) { + this.startIndex = range.getIndex(); + this.endIndex = -1; + } else { + this.startIndex = range.getIndex(); + this.endIndex = range.getIndex() + range.getSize() - 1; + } + } + } else { + if (range.getIndex() == Range.INDEX_LAST) { + if (range.getSize() == Range.SIZE_MAX) { + this.startIndex = -1; + this.endIndex = -1; + } else { + this.startIndex = totalSize - range.getSize(); + this.endIndex = -1; + } + } else { + if (range.getSize() == Range.SIZE_MAX) { + this.startIndex = range.getIndex(); + this.endIndex = -1; + } else { + this.startIndex = range.getIndex(); + this.endIndex = range.getIndex() + range.getSize() - 1; + } + } + } + } + + @Override + public int available() throws IOException { + return this.availableSize; + } + + @Override + public synchronized void mark(int readLimit) { + if (range.getIndex() == Range.INDEX_LAST) { + super.mark(readLimit + (int) (totalSize - range.getSize())); + } else { + super.mark(readLimit + (int) range.getIndex()); + } + } + + @Override + public int read() throws IOException { + int result = super.read(); + + while ((result != -1) && !this.range.isIncluded(position++, totalSize)) { + result = super.read(); + } + + if ((result != -1) && (this.availableSize > 0)) { + this.availableSize--; + } + + return result; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + // Reach the start index. + while (!(position >= startIndex)) { + long skipped = skip(startIndex - position); + + if (skipped <= 0) { + throw new IOException("Cannot skip ahead in FilterInputStream"); + } + + position += skipped; + } + + int result; + + if (endIndex != -1) { + // Read up until the end index + if (position > endIndex) { + // The end index is reached. + result = -1; + } else { + // Take care to read the right number of bytes according to the + // end index and the buffer size. + result = + super.read( + b, + off, + ((position + len) > endIndex) + ? (int) (endIndex - position + 1) + : len); + } + } else { + // Read normally up until the end of the stream. + result = super.read(b, off, len); + } + + if (result > 0) { + // Move the cursor. + position += result; + } + + if ((result != -1) && (this.availableSize > 0)) { + this.availableSize -= result; + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java index 1a24b53e79..6eb3f13a78 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java @@ -1,16 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; -import org.restlet.data.CharacterSet; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -18,125 +15,130 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharsetEncoder; +import org.restlet.data.CharacterSet; /** - * Input stream based on a reader. The implementation relies on the NIO - * {@link CharsetEncoder} class. - * + * Input stream based on a reader. The implementation relies on the NIO {@link CharsetEncoder} + * class. + * * @author Jerome Louvel */ public class ReaderInputStream extends InputStream { - /** The NIO byte buffer. */ - private final ByteBuffer byteBuffer; - - /** The NIO character buffer. */ - private final CharBuffer charBuffer; - - /** The character set encoder. */ - private final CharsetEncoder charsetEncoder; - - /** Indicates if the end of the wrapped reader has been reached. */ - private volatile boolean endReached; - - /** The wrapped reader. */ - private final BufferedReader reader; - - /** - * Constructor. Uses the {@link CharacterSet#ISO_8859_1} character set by - * default. - * - * @param reader The reader to wrap as an input stream. - * @throws IOException - */ - public ReaderInputStream(Reader reader) throws IOException { - this(reader, CharacterSet.ISO_8859_1); - } - - /** - * Constructor. - * - * @param reader The reader to wrap as an input stream. - * @param characterSet The character set to use for encoding. - * @throws IOException - */ - public ReaderInputStream(Reader reader, CharacterSet characterSet) throws IOException { - this.byteBuffer = ByteBuffer.allocate(1024); - this.byteBuffer.flip(); - this.charBuffer = CharBuffer.allocate(1024); - this.charBuffer.flip(); - this.charsetEncoder = (characterSet == null) ? CharacterSet.ISO_8859_1.toCharset().newEncoder() - : characterSet.toCharset().newEncoder(); - this.endReached = false; - this.reader = (reader instanceof BufferedReader) ? (BufferedReader) reader - : new BufferedReader(reader, IoUtils.BUFFER_SIZE); - } - - @Override - public int available() throws IOException { - return this.byteBuffer.hasRemaining() ? this.byteBuffer.remaining() : 0; - } - - /** - * Closes the wrapped reader. - */ - @Override - public void close() throws IOException { - this.reader.close(); - } - - @Override - public int read() throws IOException { - byte[] temp = new byte[1]; - return (read(temp) == -1) ? -1 : temp[0] & 0xFF; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - int result = 0; - boolean iterate = true; - - while (iterate) { - // Do we need to refill the byte buffer? - if (!this.byteBuffer.hasRemaining() && !this.endReached) { - // Do we need to refill the char buffer? - if (!this.charBuffer.hasRemaining()) { - this.charBuffer.clear(); - int read = this.reader.read(this.charBuffer); - this.charBuffer.flip(); - - if (read == -1) { - this.endReached = true; - } - } - - if ((len > 0) && this.charBuffer.hasRemaining()) { - // Refill the byte buffer - this.byteBuffer.clear(); - this.charsetEncoder.encode(this.charBuffer, this.byteBuffer, this.endReached); - this.byteBuffer.flip(); - } - } - - // Copies as much bytes as possible in the target array - int readLength = Math.min(len, this.byteBuffer.remaining()); - - if (readLength > 0) { - this.byteBuffer.get(b, off, readLength); - off += readLength; - len -= readLength; - result += readLength; - } - - // Can we iterate again? - iterate = (len > 0) - && (!this.endReached || this.byteBuffer.hasRemaining() || this.charBuffer.hasRemaining()); - } - - if (this.endReached && (result == 0)) { - result = -1; - } - - return result; - } + /** The NIO byte buffer. */ + private final ByteBuffer byteBuffer; + + /** The NIO character buffer. */ + private final CharBuffer charBuffer; + + /** The character set encoder. */ + private final CharsetEncoder charsetEncoder; + + /** Indicates if the end of the wrapped reader has been reached. */ + private volatile boolean endReached; + + /** The wrapped reader. */ + private final BufferedReader reader; + + /** + * Constructor. Uses the {@link CharacterSet#ISO_8859_1} character set by default. + * + * @param reader The reader to wrap as an input stream. + * @throws IOException + */ + public ReaderInputStream(Reader reader) throws IOException { + this(reader, CharacterSet.ISO_8859_1); + } + + /** + * Constructor. + * + * @param reader The reader to wrap as an input stream. + * @param characterSet The character set to use for encoding. + * @throws IOException + */ + public ReaderInputStream(Reader reader, CharacterSet characterSet) throws IOException { + this.byteBuffer = ByteBuffer.allocate(1024); + this.byteBuffer.flip(); + this.charBuffer = CharBuffer.allocate(1024); + this.charBuffer.flip(); + this.charsetEncoder = + (characterSet == null) + ? CharacterSet.ISO_8859_1.toCharset().newEncoder() + : characterSet.toCharset().newEncoder(); + this.endReached = false; + this.reader = + (reader instanceof BufferedReader) + ? (BufferedReader) reader + : new BufferedReader(reader, IoUtils.BUFFER_SIZE); + } + + @Override + public int available() throws IOException { + return this.byteBuffer.hasRemaining() ? this.byteBuffer.remaining() : 0; + } + + /** Closes the wrapped reader. */ + @Override + public void close() throws IOException { + this.reader.close(); + } + + @Override + public int read() throws IOException { + byte[] temp = new byte[1]; + return (read(temp) == -1) ? -1 : temp[0] & 0xFF; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int result = 0; + boolean iterate = true; + + while (iterate) { + // Do we need to refill the byte buffer? + if (!this.byteBuffer.hasRemaining() && !this.endReached) { + // Do we need to refill the char buffer? + if (!this.charBuffer.hasRemaining()) { + this.charBuffer.clear(); + int read = this.reader.read(this.charBuffer); + this.charBuffer.flip(); + + if (read == -1) { + this.endReached = true; + } + } + + if ((len > 0) && this.charBuffer.hasRemaining()) { + // Refill the byte buffer + this.byteBuffer.clear(); + this.charsetEncoder.encode(this.charBuffer, this.byteBuffer, this.endReached); + this.byteBuffer.flip(); + } + } + + // Copies as many bytes as possible in the target array + int readLength = Math.min(len, this.byteBuffer.remaining()); + + if (readLength > 0) { + this.byteBuffer.get(b, off, readLength); + off += readLength; + len -= readLength; + result += readLength; + } + + // Can we iterate again? + iterate = + (len > 0) + && (!this.endReached + || this.byteBuffer.hasRemaining() + || this.charBuffer.hasRemaining()); + } + + if (this.endReached && (result == 0)) { + result = -1; + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java index ad7169338c..586db5ee7b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; import java.io.FilterInputStream; @@ -14,24 +13,21 @@ import java.io.InputStream; /** - * InputStream decorator to trap {@code close()} calls so that the underlying - * stream is not closed. - * + * InputStream decorator to trap {@code close()} calls so that the underlying stream is not closed. + * * @author Kevin Conaway - * */ public class UnclosableInputStream extends FilterInputStream { - /** - * Constructor. - * - * @param source The source input stream. - */ - public UnclosableInputStream(InputStream source) { - super(source); - } + /** + * Constructor. + * + * @param source The source input stream. + */ + public UnclosableInputStream(InputStream source) { + super(source); + } - @Override - public void close() throws IOException { - } + @Override + public void close() throws IOException {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java index 096eab5729..f716085423 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; import java.io.FilterOutputStream; @@ -14,23 +13,21 @@ import java.io.OutputStream; /** - * OutputStream decorator to trap close() calls so that the decorated stream - * does not get closed. - * + * OutputStream decorator to trap close() calls so that the decorated stream does not get closed. + * * @author Kevin Conaway */ public class UnclosableOutputStream extends FilterOutputStream { - /** - * Constructor. - * - * @param source The decorated source stream. - */ - public UnclosableOutputStream(OutputStream source) { - super(source); - } + /** + * Constructor. + * + * @param source The decorated source stream. + */ + public UnclosableOutputStream(OutputStream source) { + super(source); + } - @Override - public void close() throws IOException { - } + @Override + public void close() throws IOException {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java index b184bf6d49..04fc2401fc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java @@ -1,16 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; -import org.restlet.data.CharacterSet; - import java.io.IOException; import java.io.OutputStream; import java.io.Writer; @@ -18,52 +15,54 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import org.restlet.data.CharacterSet; /** * Output stream wrapping a character writer. - * + * * @author Kevin Conaway */ public class WriterOutputStream extends OutputStream { - /** The character set to use when parsing byte arrays. */ - private final Charset charSet; + /** The character set to use when parsing byte arrays. */ + private final Charset charSet; - /** The wrapped writer. */ - private final Writer writer; + /** The wrapped writer. */ + private final Writer writer; - /** - * Constructor. - * - * @param writer The wrapped writer. - * @param characterSet The character set. Use {@link CharacterSet#ISO_8859_1} by - * default if a null value is given. - */ - public WriterOutputStream(Writer writer, CharacterSet characterSet) { - this.writer = writer; - this.charSet = (characterSet == null) ? StandardCharsets.ISO_8859_1 : characterSet.toCharset(); - } + /** + * Constructor. + * + * @param writer The wrapped writer. + * @param characterSet The character set. Use {@link CharacterSet#ISO_8859_1} by default if a + * null value is given. + */ + public WriterOutputStream(Writer writer, CharacterSet characterSet) { + this.writer = writer; + this.charSet = + (characterSet == null) ? StandardCharsets.ISO_8859_1 : characterSet.toCharset(); + } - @Override - public void close() throws IOException { - super.close(); - this.writer.close(); - } + @Override + public void close() throws IOException { + super.close(); + this.writer.close(); + } - @Override - public void flush() throws IOException { - super.flush(); - this.writer.flush(); - } + @Override + public void flush() throws IOException { + super.flush(); + this.writer.flush(); + } - @Override - public void write(byte[] b, int off, int len) throws IOException { - CharBuffer charBuffer = this.charSet.decode(ByteBuffer.wrap(b, off, len)); - this.writer.write(charBuffer.toString()); - } + @Override + public void write(byte[] b, int off, int len) throws IOException { + CharBuffer charBuffer = this.charSet.decode(ByteBuffer.wrap(b, off, len)); + this.writer.write(charBuffer.toString()); + } - @Override - public void write(int b) throws IOException { - this.writer.write(b); - } + @Override + public void write(int b) throws IOException { + this.writer.write(b); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java index 63f0754827..5ba4338869 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java @@ -1,169 +1,175 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; -import org.restlet.Client; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.*; -import org.restlet.representation.InputRepresentation; -import org.restlet.representation.Representation; -import org.restlet.service.MetadataService; - import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Date; import java.util.logging.Level; +import org.restlet.Client; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.LocalReference; +import org.restlet.data.Method; +import org.restlet.data.Protocol; +import org.restlet.data.Reference; +import org.restlet.data.Status; +import org.restlet.representation.InputRepresentation; +import org.restlet.representation.Representation; +import org.restlet.service.MetadataService; /** - * Connector to the resources accessed via class loaders. Note that if you use - * the class authority for your CLAP URIs, you can provide a custom classloader - * instead of the one of the connector. For this, your requests need to have a - * "org.restlet.clap.classLoader" attribute set with the instance of your - * classloader and use the {@link LocalReference#CLAP_CLASS} authority. - * + * Connector to the resources accessed via class loaders. Note that if you use the class authority + * for your CLAP URIs, you can provide a custom classloader instead of the one of the connector. For + * this, your requests need to have a "org.restlet.clap.classLoader" attribute set with the instance + * of your classloader and use the {@link LocalReference#CLAP_CLASS} authority. + * * @author Jerome Louvel */ public class ClapClientHelper extends LocalClientHelper { - /** - * Constructor. - * - * @param client The client to help. - */ - public ClapClientHelper(Client client) { - super(client); - getProtocols().add(Protocol.CLAP); - } - - /** - * Handles a call with a given class loader. - * - * @param request The request to handle. - * @param response The response to update. - */ - protected void handleClassLoader(Request request, Response response, ClassLoader classLoader) { - MetadataService metadataService = getMetadataService(); - - if (!request.getMethod().equals(Method.GET) && !request.getMethod().equals(Method.HEAD)) { - response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - response.getAllowedMethods().add(Method.GET); - response.getAllowedMethods().add(Method.HEAD); - - return; - } - - String path = request.getResourceRef().getPath(); - URL url = null; - Date modificationDate = null; - - // Prepare a classloader URI, removing the leading slash - if ((path != null) && path.startsWith("/")) { - path = path.substring(1); - } - - // Get the URL to the classloader 'resource' - if (classLoader != null) { - // As the path may be percent-encoded, it has to be - // percent-decoded. - url = classLoader.getResource(Reference.decode(path)); - } else { - getLogger().warning("Unable to get the resource. The selected classloader is null."); - } - - // The ClassLoader returns a directory listing in some cases. - // As this listing is partial, it is of little value in the context - // of the CLAP client, so we have to ignore them. - if (url != null) { - if (url.getProtocol().equals("file")) { - File file = new File(url.getFile()); - modificationDate = new Date(file.lastModified()); - - if (file.isDirectory()) { - url = null; - } - } - } - - if (url == null) { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return; - } - - try { - InputStream inputStream = url.openStream(); - - // check for empty input stream on jar directories - if (url.getProtocol().equals("jar")) { - if (inputStream.available() == 0) { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return; - } - } - - Representation output = new InputRepresentation(inputStream, metadataService.getDefaultMediaType()); - output.setLocationRef(request.getResourceRef()); - output.setModificationDate(modificationDate); - - // Update the expiration date - long timeToLive = getTimeToLive(); - - if (timeToLive == 0) { - output.setExpirationDate(null); - } else if (timeToLive > 0) { - output.setExpirationDate(new Date(System.currentTimeMillis() + (1000L * timeToLive))); - } - - // Update the metadata based on file extensions - String name = path.substring(path.lastIndexOf('/') + 1); - Entity.updateMetadata(name, output, true, getMetadataService()); - - // Update the response - response.setEntity(output); - response.setStatus(Status.SUCCESS_OK); - } catch (IOException ioe) { - getLogger().log(Level.WARNING, "Unable to open the representation's input stream", ioe); - response.setStatus(Status.SERVER_ERROR_INTERNAL); - } - } - - @Override - protected void handleLocal(Request request, Response response, String decodedPath) { - String scheme = request.getResourceRef().getScheme(); - - if (scheme.equalsIgnoreCase(Protocol.CLAP.getSchemeName())) { - LocalReference cr = new LocalReference(request.getResourceRef()); - ClassLoader classLoader = null; - - if ((cr.getClapAuthorityType() == LocalReference.CLAP_CLASS) - || (cr.getClapAuthorityType() == LocalReference.CLAP_DEFAULT)) { - // Sometimes, a specific class loader needs to be used, - // make sure that it can be provided as a request's attribute - Object classLoaderAttribute = request.getAttributes().get("org.restlet.clap.classLoader"); - - if (classLoaderAttribute != null) { - classLoader = (ClassLoader) classLoaderAttribute; - } else { - classLoader = getClass().getClassLoader(); - } - } else if (cr.getClapAuthorityType() == LocalReference.CLAP_SYSTEM) { - classLoader = ClassLoader.getSystemClassLoader(); - } else if (cr.getClapAuthorityType() == LocalReference.CLAP_THREAD) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - - handleClassLoader(request, response, classLoader); - } else { - throw new IllegalArgumentException( - "Protocol \"" + scheme + "\" not supported by the connector. Only CLAP is supported."); - } - } + /** + * Constructor. + * + * @param client The client to help. + */ + public ClapClientHelper(Client client) { + super(client); + getProtocols().add(Protocol.CLAP); + } + + /** + * Handles a call with a given class loader. + * + * @param request The request to handle. + * @param response The response to update. + */ + protected void handleClassLoader(Request request, Response response, ClassLoader classLoader) { + MetadataService metadataService = getMetadataService(); + + if (!request.getMethod().equals(Method.GET) && !request.getMethod().equals(Method.HEAD)) { + response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + response.getAllowedMethods().add(Method.GET); + response.getAllowedMethods().add(Method.HEAD); + + return; + } + + String path = request.getResourceRef().getPath(); + URL url = null; + Date modificationDate = null; + + // Prepare a classloader URI, removing the leading slash + if ((path != null) && path.startsWith("/")) { + path = path.substring(1); + } + + // Get the URL to the classloader 'resource' + if (classLoader != null) { + // As the path may be percent-encoded, it has to be + // percent-decoded. + url = classLoader.getResource(Reference.decode(path)); + } else { + getLogger().warning("Unable to get the resource. The selected classloader is null."); + } + + // The ClassLoader returns a directory listing in some cases. + // As this listing is partial, it is of little value in the context + // of the CLAP client, so we have to ignore them. + if (url != null) { + if (url.getProtocol().equals("file")) { + File file = new File(url.getFile()); + modificationDate = new Date(file.lastModified()); + + if (file.isDirectory()) { + url = null; + } + } + } + + if (url == null) { + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + return; + } + + try { + InputStream inputStream = url.openStream(); + + // check for empty input stream on jar directories + if (url.getProtocol().equals("jar")) { + if (inputStream.available() == 0) { + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + return; + } + } + + Representation output = + new InputRepresentation(inputStream, metadataService.getDefaultMediaType()); + output.setLocationRef(request.getResourceRef()); + output.setModificationDate(modificationDate); + + // Update the expiration date + long timeToLive = getTimeToLive(); + + if (timeToLive == 0) { + output.setExpirationDate(null); + } else if (timeToLive > 0) { + output.setExpirationDate( + new Date(System.currentTimeMillis() + (1000L * timeToLive))); + } + + // Update the metadata based on file extensions + String name = path.substring(path.lastIndexOf('/') + 1); + Entity.updateMetadata(name, output, true, getMetadataService()); + + // Update the response + response.setEntity(output); + response.setStatus(Status.SUCCESS_OK); + } catch (IOException ioe) { + getLogger().log(Level.WARNING, "Unable to open the representation's input stream", ioe); + response.setStatus(Status.SERVER_ERROR_INTERNAL); + } + } + + @Override + protected void handleLocal(Request request, Response response, String decodedPath) { + String scheme = request.getResourceRef().getScheme(); + + if (scheme.equalsIgnoreCase(Protocol.CLAP.getSchemeName())) { + LocalReference cr = new LocalReference(request.getResourceRef()); + ClassLoader classLoader = null; + + if ((cr.getClapAuthorityType() == LocalReference.CLAP_CLASS) + || (cr.getClapAuthorityType() == LocalReference.CLAP_DEFAULT)) { + // Sometimes, a specific class loader needs to be used, + // make sure that it can be provided as a request's attribute + Object classLoaderAttribute = + request.getAttributes().get("org.restlet.clap.classLoader"); + + if (classLoaderAttribute != null) { + classLoader = (ClassLoader) classLoaderAttribute; + } else { + classLoader = getClass().getClassLoader(); + } + } else if (cr.getClapAuthorityType() == LocalReference.CLAP_SYSTEM) { + classLoader = ClassLoader.getSystemClassLoader(); + } else if (cr.getClapAuthorityType() == LocalReference.CLAP_THREAD) { + classLoader = Thread.currentThread().getContextClassLoader(); + } + + handleClassLoader(request, response, classLoader); + } else { + throw new IllegalArgumentException( + "Protocol \"" + + scheme + + "\" not supported by the connector. Only CLAP is supported."); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java index 35c2ce4a27..bfeca911e2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java @@ -1,18 +1,29 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.logging.Level; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; -import org.restlet.data.*; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Reference; +import org.restlet.data.ReferenceList; +import org.restlet.data.Status; import org.restlet.engine.util.StringUtils; import org.restlet.representation.Representation; import org.restlet.representation.Variant; @@ -20,453 +31,463 @@ import org.restlet.resource.ResourceException; import org.restlet.resource.ServerResource; -import java.io.IOException; -import java.util.*; -import java.util.logging.Level; - /** - * Resource supported by a set of context representations (from file system, - * class loaders and webapp context). A content negotiation mechanism (similar - * to Apache HTTP server) is available. It is based on path extensions to detect - * variants (languages, media types or character sets). - * - * @see Apache - * mod_negotiation module + * Resource supported by a set of context representations (from file system, class loaders, and + * webapp context). A content negotiation mechanism (similar to Apache HTTP Server) is available. It + * is based on path extensions to detect variants (languages, media types, or character sets). + * + * @see Apache + * mod_negotiation module * @author Jerome Louvel * @author Thierry Boileau */ public class DirectoryServerResource extends ServerResource { - /** The list of variants for the GET method. */ - private volatile List variantsGet; - - /** - * The local base name of the resource. For example, "foo.en" and - * "foo.en-GB.html" return "foo". - */ - private volatile String baseName; - - /** The base variant. */ - private volatile Variant baseVariant; - - /** The parent directory client dispatcher. */ - private volatile Restlet directoryClientDispatcher; - - /** The parent directory handler. */ - private volatile Directory directory; - - /** If the resource is a directory, this contains its content. */ - private volatile ReferenceList directoryContent; - - /** - * If the resource is a directory, the non-trailing slash character leads to - * redirection. - */ - private volatile boolean directoryRedirection; - - /** Indicates if the target resource is a directory. */ - private volatile boolean directoryTarget; - - /** The context's directory URI (file, clap URI). */ - private volatile String directoryUri; - - /** If the resource is a file, this contains its content. */ - private volatile Representation fileContent; - - /** Indicates if the target resource is a file. */ - private volatile boolean fileTarget; - - /** Indicates if the target resource is a directory with an index. */ - private volatile boolean indexTarget; - - /** The original target URI, in case of extensions tunneling. */ - private volatile Reference originalRef; - - /** The prototype variant. */ - private volatile Variant protoVariant; - - /** The resource path relative to the directory URI. */ - private volatile String relativePart; - - /** The context's target URI (file, clap URI). */ - private volatile String targetUri; - - /** The unique representation of the target URI, if it exists. */ - private volatile Reference uniqueReference; - - @Override - public Representation delete() throws ResourceException { - if (!this.directory.isModifiable()) { - setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED, "The directory is not modifiable."); - return null; - } - - Request contextRequest = new Request(Method.DELETE, this.targetUri); - Response contextResponse = new Response(contextRequest); - - if (this.directoryTarget && !this.indexTarget) { - // let the client handle the directory's deletion - contextRequest.setResourceRef(this.targetUri); - dispatchRequest(contextRequest, contextResponse); - - setStatus(contextResponse.getStatus()); - return null; - } - - ReferenceList references = getVariantsReferences(); - - if (references.isEmpty()) { - // no representation found - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } else if (this.uniqueReference != null) { - // only one representation - contextRequest.setResourceRef(this.uniqueReference); - dispatchRequest(contextRequest, contextResponse); - setStatus(contextResponse.getStatus()); - } else { - // several variants found, but not the right one - setStatus(Status.CLIENT_ERROR_NOT_ACCEPTABLE, - "Unable to process properly the request. Several variants exist but none of them suits precisely. "); - } - - return null; - } - - /** - * This initialization method aims at answering the following questions:
- *

    - *
  • does this request target a directory?
  • - *
  • does this request target a directory, with an index file?
  • - *
  • should this request be redirected (target is a directory with no trailing - * "/")?
  • - *
  • does this request target a file?
  • - *
- *
- * The following constraints must be taken into account:
- *
    - *
  • the underlying helper may not support content negotiation and be able to - * return the list of possible variants of the target file (e.g. the CLAP - * helper).
  • - *
  • the underlying helper may not support directory listing
  • - *
  • the extensions tunneling cannot apply on a directory
  • - *
  • underlying helpers that do not support content negotiation cannot support - * extensions tunneling
  • - *
- */ - @Override - public void doInit() throws ResourceException { - this.directory = (Directory) getRequestAttributes().get("org.restlet.directory"); - this.directoryClientDispatcher = getDirectory().getContext() != null - ? getDirectory().getContext().getClientDispatcher() - : null; - if (getClientDispatcher() == null) { - getLogger().warning("No client dispatcher is available. Can't get the target URI: " + this.targetUri); - - throw new ResourceException(Status.SERVER_ERROR_INTERNAL, "No client dispatcher is available."); - } - - // Update the member variables - setNegotiated(this.directory.isNegotiatingContent()); - this.relativePart = getReference().getRemainingPart(false, false); - this.originalRef = getOriginalRef(); - if (this.originalRef != null) { - // Restore the original URI in case the call has been tunneled. - if ((getApplication() != null) && getApplication().getTunnelService().isExtensionsTunnel()) { - Reference originalBaseRef = new Reference(this.originalRef); - originalBaseRef.setPath(getReference().getBaseRef().getPath()); - this.originalRef.setBaseRef(originalBaseRef); - this.relativePart = this.originalRef.getRemainingPart(false, false); - } - } - - if (this.relativePart.startsWith("/")) { - // We enforce the leading slash on the root URI - this.relativePart = this.relativePart.substring(1); - } - - // The target URI does not take into account the query and fragment - // parts of the resource. - this.targetUri = new Reference(directory.getRootRef().toString() + this.relativePart).toString(false, false); - preventUpperDirectoryAccess(); - - // Try to detect the presence of a directory - Response contextResponse = getRepresentation(this.targetUri); - - if (contextResponse.getEntity() != null) { - // As a convention, underlying client connectors return the - // directory listing with the media-type "MediaType.TEXT_URI_LIST" when handling - // directories - if (MediaType.TEXT_URI_LIST.equals(contextResponse.getEntity().getMediaType())) { - this.directoryTarget = true; - this.fileTarget = false; - this.directoryContent = tryToConvertAsReferenceList(contextResponse.getEntity()); - - if (!getReference().getPath().endsWith("/")) { - // All requests will be automatically redirected - this.directoryRedirection = true; - } - - if (!this.targetUri.endsWith("/")) { - this.targetUri += "/"; - this.relativePart += "/"; - } - - // Append the index name - if (!StringUtils.isNullOrEmpty(getDirectory().getIndexName())) { - this.directoryUri = this.targetUri; - this.baseName = getDirectory().getIndexName(); - this.targetUri = this.directoryUri + this.baseName; - this.indexTarget = true; - } else { - this.directoryUri = this.targetUri; - this.baseName = null; - } - } else { - // Allows underlying helpers that do not support "content negotiation" to return - // the targeted file. - // Sometimes we immediately reach the target entity, so we return it directly. - this.directoryTarget = false; - this.fileTarget = true; - this.fileContent = contextResponse.getEntity(); - } - } else { - this.directoryTarget = false; - this.fileTarget = false; - - // Let's try with the optional index, in case the underlying - // client connector does not handle directory listing. - if (this.targetUri.endsWith("/")) { - // In this case, the trailing "/" shows that the URI must point to a directory - if (!StringUtils.isNullOrEmpty(getDirectory().getIndexName())) { - this.directoryUri = this.targetUri; - this.directoryTarget = true; - - contextResponse = getRepresentation(this.directoryUri + getDirectory().getIndexName()); - if (contextResponse.getEntity() != null) { - this.baseName = getDirectory().getIndexName(); - this.targetUri = this.directoryUri + this.baseName; - this.directoryContent = new ReferenceList(); - this.directoryContent.add(new Reference(this.targetUri)); - this.indexTarget = true; - } - } - } else { - // Try to determine if this target URI with no trailing "/" is a directory, in - // order to force the - // redirection. - if (!StringUtils.isNullOrEmpty(getDirectory().getIndexName())) { - // Append the index name - contextResponse = getRepresentation(this.targetUri + "/" + getDirectory().getIndexName()); - if (contextResponse.getEntity() != null) { - this.directoryUri = this.targetUri + "/"; - this.baseName = getDirectory().getIndexName(); - this.targetUri = this.directoryUri + this.baseName; - this.directoryTarget = true; - this.directoryRedirection = true; - this.directoryContent = new ReferenceList(); - this.directoryContent.add(new Reference(this.targetUri)); - this.indexTarget = true; - } - } - } - } - - // In case the request does not target a directory and the file - // has not been found, try with the tunneled URI. - if (isNegotiated() && !this.directoryTarget && !this.fileTarget && this.originalRef != null) { - this.relativePart = getReference().getRemainingPart(); - - // The target URI does not take into account the query and fragment parts of the - // resource. - this.targetUri = new Reference(directory.getRootRef().toString() + this.relativePart).normalize() - .toString(false, false); - if (!this.targetUri.startsWith(directory.getRootRef().toString())) { - // Prevent the client from accessing resources in upper directories - this.targetUri = directory.getRootRef().toString(); - } - } - - if (!fileTarget || fileContent == null || !getRequest().getMethod().isSafe()) { - // Try to get the directory content, in case the request does not target a - // directory - if (!this.directoryTarget) { - int lastSlashIndex = this.targetUri.lastIndexOf('/'); - if (lastSlashIndex == -1) { - this.directoryUri = ""; - this.baseName = this.targetUri; - } else { - this.directoryUri = this.targetUri.substring(0, lastSlashIndex + 1); - this.baseName = this.targetUri.substring(lastSlashIndex + 1); - } - - contextResponse = getRepresentation(this.directoryUri); - if ((contextResponse.getEntity() != null) - && MediaType.TEXT_URI_LIST.equals(contextResponse.getEntity().getMediaType())) { - this.directoryContent = tryToConvertAsReferenceList(contextResponse.getEntity()); - } - } - - if (this.baseName != null) { - // Analyze extensions - this.baseVariant = new Variant(); - Entity.updateMetadata(this.baseName, this.baseVariant, true, getMetadataService()); - this.protoVariant = new Variant(); - Entity.updateMetadata(this.baseName, this.protoVariant, false, getMetadataService()); - - // Remove stored extensions from the base name - this.baseName = Entity.getBaseName(this.baseName, getMetadataService()); - } - - // Check if the resource exists or not. - List variants = getVariants(Method.GET); - if ((variants == null) || (variants.isEmpty())) { - setExisting(false); - } - } - - // Check if the resource is located in a sub directory. - if (isExisting() && !this.directory.isDeeplyAccessible()) { - // Count the number of "/" character. - int index = this.relativePart.indexOf("/"); - if (index != -1) { - index = this.relativePart.indexOf("/", index); - setExisting((index == -1)); - } - } - - // Log results - getLogger().fine("Converted target URI: " + this.targetUri); - getLogger().fine("Converted base name : " + this.baseName); - - } - - private ReferenceList tryToConvertAsReferenceList(Representation entity) throws ResourceException { - try { - return new ReferenceList(entity); - } catch (IOException e) { - throw new ResourceException(e); - } - } - - @Override - protected Representation get() throws ResourceException { - // Content negotiation has been disabled - // The variant that may need to meet the request conditions - - List variants = getVariants(Method.GET); - if ((variants == null) || (variants.isEmpty())) { - // Resource not found - throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND); - } - - if (variants.size() == 1) { - return (Representation) variants.get(0); - } - - ReferenceList variantRefs = new ReferenceList(); - - for (Variant variant : variants) { - if (variant.getLocationRef() != null) { - variantRefs.add(variant.getLocationRef()); - } else { - getLogger().warning( - "A resource with multiple variants should provide a location for each variant when content negotiation is turned off"); - } - } - - if (!variantRefs.isEmpty()) { - // Return the list of variants - setStatus(Status.REDIRECTION_MULTIPLE_CHOICES); - return variantRefs.getTextRepresentation(); - } - - throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND); - } - - /** - * Returns the local base name of the file. For example, "foo.en" and - * "foo.en-GB.html" return "foo". - * - * @return The local name of the file. - */ - public String getBaseName() { - return this.baseName; - } - - /** - * Returns a client dispatcher. - * - * @return A client dispatcher. - */ - protected Restlet getClientDispatcher() { - return directoryClientDispatcher; - } - - /** - * Returns the parent directory handler. - * - * @return The parent directory handler. - */ - public Directory getDirectory() { - return this.directory; - } - - /** - * If the resource is a directory, this returns its content. - * - * @return The directory content. - */ - protected ReferenceList getDirectoryContent() { - return directoryContent; - } - - /** - * Returns the context's directory URI (file, clap URI). - * - * @return The context's directory URI (file, clap URI). - */ - public String getDirectoryUri() { - return this.directoryUri; - } - - /** - * Returns a representation of the resource at the target URI. Leverages the - * client dispatcher of the parent directory's context. - * - * @param resourceUri The URI of the target resource. - * @return A response with the representation if success. - */ - private Response getRepresentation(String resourceUri) { - return dispatchRequest(new Request(Method.GET, resourceUri)); - } - - /** - * Returns a representation of the resource at the target URI. Leverages the - * client dispatcher of the parent directory's context. - * - * @param resourceUri The URI of the target resource. - * @param acceptedMediaType The accepted media type or null. - * @return A response with the representation if success. - */ - protected Response getRepresentation(String resourceUri, MediaType acceptedMediaType) { - Request request = new Request(Method.GET, resourceUri); - - if (acceptedMediaType != null) { - request.getClientInfo().accept(acceptedMediaType); - } - - return dispatchRequest(request); - } - - /** - * Allows sorting the list of representations set by the resource. - * - * @return A Comparator instance imposing a sort order of representations or - * null if no special order is wanted. - */ - private Comparator getRepresentationsComparator() { - // Sort the list of representations by their identifier. + /** The list of variants for the GET method. */ + private volatile List variantsGet; + + /** + * The local base name of the resource. For example, "foo.en" and "foo.en-GB.html" return "foo". + */ + private volatile String baseName; + + /** The base variant. */ + private volatile Variant baseVariant; + + /** The parent directory client dispatcher. */ + private volatile Restlet directoryClientDispatcher; + + /** The parent directory handler. */ + private volatile Directory directory; + + /** If the resource is a directory, this contains its content. */ + private volatile ReferenceList directoryContent; + + /** If the resource is a directory, the non-trailing slash character leads to redirection. */ + private volatile boolean directoryRedirection; + + /** Indicates if the target resource is a directory. */ + private volatile boolean directoryTarget; + + /** The context's directory URI (file, clap URI). */ + private volatile String directoryUri; + + /** If the resource is a file, this contains its content. */ + private volatile Representation fileContent; + + /** Indicates if the target resource is a file. */ + private volatile boolean fileTarget; + + /** Indicates if the target resource is a directory with an index. */ + private volatile boolean indexTarget; + + /** The original target URI, in case of extensions tunneling. */ + private volatile Reference originalRef; + + /** The prototype variant. */ + private volatile Variant protoVariant; + + /** The resource path relative to the directory URI. */ + private volatile String relativePart; + + /** The context's target URI (file, clap URI). */ + private volatile String targetUri; + + /** The unique representation of the target URI, if it exists. */ + private volatile Reference uniqueReference; + + @Override + public Representation delete() throws ResourceException { + if (!this.directory.isModifiable()) { + setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED, "The directory is not modifiable."); + return null; + } + + Request contextRequest = new Request(Method.DELETE, this.targetUri); + Response contextResponse = new Response(contextRequest); + + if (this.directoryTarget && !this.indexTarget) { + // let the client handle the directory's deletion + contextRequest.setResourceRef(this.targetUri); + dispatchRequest(contextRequest, contextResponse); + + setStatus(contextResponse.getStatus()); + return null; + } + + ReferenceList references = getVariantsReferences(); + + if (references.isEmpty()) { + // no representation found + setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } else if (this.uniqueReference != null) { + // only one representation + contextRequest.setResourceRef(this.uniqueReference); + dispatchRequest(contextRequest, contextResponse); + setStatus(contextResponse.getStatus()); + } else { + // several variants found, but not the right one + setStatus( + Status.CLIENT_ERROR_NOT_ACCEPTABLE, + "Unable to process properly the request. Several variants exist but none of them suits precisely. "); + } + + return null; + } + + /** + * This initialization method aims at answering the following questions:
+ * + *
    + *
  • does this request target a directory? + *
  • does this request target a directory, with an index file? + *
  • should this request be redirected (target is a directory with no trailing "/")? + *
  • does this request target a file? + *
+ * + *
+ * The following constraints must be taken into account:
+ * + *
    + *
  • the underlying helper may not support content negotiation and be able to return the + * list of possible variants of the target file (e.g., the CLAP helper). + *
  • the underlying helper may not support directory listing + *
  • the extensions tunneling cannot apply on a directory + *
  • underlying helpers that do not support content negotiation cannot support extensions + * tunneling + *
+ */ + @Override + public void doInit() throws ResourceException { + this.directory = (Directory) getRequestAttributes().get("org.restlet.directory"); + this.directoryClientDispatcher = + getDirectory().getContext() != null + ? getDirectory().getContext().getClientDispatcher() + : null; + if (getClientDispatcher() == null) { + getLogger() + .warning( + "No client dispatcher is available. Can't get the target URI: " + + this.targetUri); + + throw new ResourceException( + Status.SERVER_ERROR_INTERNAL, "No client dispatcher is available."); + } + + // Update the member variables + setNegotiated(this.directory.isNegotiatingContent()); + this.relativePart = getReference().getRemainingPart(false, false); + this.originalRef = getOriginalRef(); + if (this.originalRef != null) { + // Restore the original URI in case the call has been tunneled. + if ((getApplication() != null) + && getApplication().getTunnelService().isExtensionsTunnel()) { + Reference originalBaseRef = new Reference(this.originalRef); + originalBaseRef.setPath(getReference().getBaseRef().getPath()); + this.originalRef.setBaseRef(originalBaseRef); + this.relativePart = this.originalRef.getRemainingPart(false, false); + } + } + + if (this.relativePart.startsWith("/")) { + // We enforce the leading slash on the root URI + this.relativePart = this.relativePart.substring(1); + } + + // The target URI does not take into account the query and fragment + // parts of the resource. + this.targetUri = + new Reference(directory.getRootRef().toString() + this.relativePart) + .toString(false, false); + preventUpperDirectoryAccess(); + + // Try to detect the presence of a directory + Response contextResponse = getRepresentation(this.targetUri); + + if (contextResponse.getEntity() != null) { + // As a convention, underlying client connectors return the + // directory listing with the media-type "MediaType.TEXT_URI_LIST" when handling + // directories + if (MediaType.TEXT_URI_LIST.equals(contextResponse.getEntity().getMediaType())) { + this.directoryTarget = true; + this.fileTarget = false; + this.directoryContent = tryToConvertAsReferenceList(contextResponse.getEntity()); + + if (!getReference().getPath().endsWith("/")) { + // All requests will be automatically redirected + this.directoryRedirection = true; + } + + if (!this.targetUri.endsWith("/")) { + this.targetUri += "/"; + this.relativePart += "/"; + } + + // Append the index name + if (!StringUtils.isNullOrEmpty(getDirectory().getIndexName())) { + this.directoryUri = this.targetUri; + this.baseName = getDirectory().getIndexName(); + this.targetUri = this.directoryUri + this.baseName; + this.indexTarget = true; + } else { + this.directoryUri = this.targetUri; + this.baseName = null; + } + } else { + // Allows underlying helpers that do not support "content negotiation" to return + // the targeted file. + // Sometimes we immediately reach the target entity, so we return it directly. + this.directoryTarget = false; + this.fileTarget = true; + this.fileContent = contextResponse.getEntity(); + } + } else { + this.directoryTarget = false; + this.fileTarget = false; + + // Let's try with the optional index, in case the underlying + // client connector does not handle directory listing. + if (this.targetUri.endsWith("/")) { + // In this case, the trailing "/" shows that the URI must point to a directory + if (!StringUtils.isNullOrEmpty(getDirectory().getIndexName())) { + this.directoryUri = this.targetUri; + this.directoryTarget = true; + + contextResponse = + getRepresentation(this.directoryUri + getDirectory().getIndexName()); + if (contextResponse.getEntity() != null) { + this.baseName = getDirectory().getIndexName(); + this.targetUri = this.directoryUri + this.baseName; + this.directoryContent = new ReferenceList(); + this.directoryContent.add(new Reference(this.targetUri)); + this.indexTarget = true; + } + } + } else { + // Try to determine if this target URI with no trailing "/" is a directory to force + // the redirection. + if (!StringUtils.isNullOrEmpty(getDirectory().getIndexName())) { + // Append the index name + contextResponse = + getRepresentation(this.targetUri + "/" + getDirectory().getIndexName()); + if (contextResponse.getEntity() != null) { + this.directoryUri = this.targetUri + "/"; + this.baseName = getDirectory().getIndexName(); + this.targetUri = this.directoryUri + this.baseName; + this.directoryTarget = true; + this.directoryRedirection = true; + this.directoryContent = new ReferenceList(); + this.directoryContent.add(new Reference(this.targetUri)); + this.indexTarget = true; + } + } + } + } + + // In case the request does not target a directory and the file + // has not been found, try with the tunneled URI. + if (isNegotiated() + && !this.directoryTarget + && !this.fileTarget + && this.originalRef != null) { + this.relativePart = getReference().getRemainingPart(); + + // The target URI does not take into account the query and fragment parts of the + // resource. + this.targetUri = + new Reference(directory.getRootRef().toString() + this.relativePart) + .normalize() + .toString(false, false); + if (!this.targetUri.startsWith(directory.getRootRef().toString())) { + // Prevent the client from accessing resources in upper directories + this.targetUri = directory.getRootRef().toString(); + } + } + + if (!fileTarget || fileContent == null || !getRequest().getMethod().isSafe()) { + // Try to get the directory content, in case the request does not target a + // directory + if (!this.directoryTarget) { + int lastSlashIndex = this.targetUri.lastIndexOf('/'); + if (lastSlashIndex == -1) { + this.directoryUri = ""; + this.baseName = this.targetUri; + } else { + this.directoryUri = this.targetUri.substring(0, lastSlashIndex + 1); + this.baseName = this.targetUri.substring(lastSlashIndex + 1); + } + + contextResponse = getRepresentation(this.directoryUri); + if ((contextResponse.getEntity() != null) + && MediaType.TEXT_URI_LIST.equals( + contextResponse.getEntity().getMediaType())) { + this.directoryContent = + tryToConvertAsReferenceList(contextResponse.getEntity()); + } + } + + if (this.baseName != null) { + // Analyze extensions + this.baseVariant = new Variant(); + Entity.updateMetadata(this.baseName, this.baseVariant, true, getMetadataService()); + this.protoVariant = new Variant(); + Entity.updateMetadata( + this.baseName, this.protoVariant, false, getMetadataService()); + + // Remove stored extensions from the base name + this.baseName = Entity.getBaseName(this.baseName, getMetadataService()); + } + + // Check if the resource exists or not. + List variants = getVariants(Method.GET); + if ((variants == null) || (variants.isEmpty())) { + setExisting(false); + } + } + + // Check if the resource is located in a subdirectory. + if (isExisting() && !this.directory.isDeeplyAccessible()) { + // Count the number of "/" characters. + int index = this.relativePart.indexOf('/'); + if (index != -1) { + index = this.relativePart.indexOf('/', index); + setExisting((index == -1)); + } + } + + // Log results + getLogger().fine("Converted target URI: " + this.targetUri); + getLogger().fine("Converted base name : " + this.baseName); + } + + private ReferenceList tryToConvertAsReferenceList(Representation entity) + throws ResourceException { + try { + return new ReferenceList(entity); + } catch (IOException e) { + throw new ResourceException(e); + } + } + + @Override + protected Representation get() throws ResourceException { + // Content negotiation has been disabled + // The variant that may need to meet the request conditions + + List variants = getVariants(Method.GET); + if ((variants == null) || (variants.isEmpty())) { + // Resource not found + throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND); + } + + if (variants.size() == 1) { + return (Representation) variants.getFirst(); + } + + ReferenceList variantRefs = new ReferenceList(); + + for (Variant variant : variants) { + if (variant.getLocationRef() != null) { + variantRefs.add(variant.getLocationRef()); + } else { + getLogger() + .warning( + "A resource with multiple variants should provide a location for each variant when content negotiation is turned off"); + } + } + + if (!variantRefs.isEmpty()) { + // Return the list of variants + setStatus(Status.REDIRECTION_MULTIPLE_CHOICES); + return variantRefs.getTextRepresentation(); + } + + throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND); + } + + /** + * Returns the local base name of the file. For example, "foo.en" and "foo.en-GB.html" return + * "foo". + * + * @return The local name of the file. + */ + public String getBaseName() { + return this.baseName; + } + + /** + * Returns a client dispatcher. + * + * @return A client dispatcher. + */ + protected Restlet getClientDispatcher() { + return directoryClientDispatcher; + } + + /** + * Returns the parent directory handler. + * + * @return The parent directory handler. + */ + public Directory getDirectory() { + return this.directory; + } + + /** + * If the resource is a directory, this returns its content. + * + * @return The directory content. + */ + protected ReferenceList getDirectoryContent() { + return directoryContent; + } + + /** + * Returns the context's directory URI (file, clap URI). + * + * @return The context's directory URI (file, clap URI). + */ + public String getDirectoryUri() { + return this.directoryUri; + } + + /** + * Returns a representation of the resource at the target URI. Leverages the client dispatcher + * of the parent directory's context. + * + * @param resourceUri The URI of the target resource. + * @return A response with the representation if success. + */ + private Response getRepresentation(String resourceUri) { + return dispatchRequest(new Request(Method.GET, resourceUri)); + } + + /** + * Returns a representation of the resource at the target URI. Leverages the client dispatcher + * of the parent directory's context. + * + * @param resourceUri The URI of the target resource. + * @param acceptedMediaType The accepted media type or null. + * @return A response with the representation if success. + */ + protected Response getRepresentation(String resourceUri, MediaType acceptedMediaType) { + Request request = new Request(Method.GET, resourceUri); + + if (acceptedMediaType != null) { + request.getClientInfo().accept(acceptedMediaType); + } + + return dispatchRequest(request); + } + + /** + * Allows sorting the list of representations set by the resource. + * + * @return A Comparator instance imposing a sort order of representations or null if no special + * order is wanted. + */ + private Comparator getRepresentationsComparator() { + // Sort the list of representations by their identifier. return (rep0, rep1) -> { boolean bRep0Null = (rep0.getLocationRef() == null); boolean bRep1Null = (rep1.getLocationRef() == null); @@ -481,311 +502,324 @@ private Comparator getRepresentationsComparator() { return 1; } - return rep0.getLocationRef().getLastSegment().compareTo(rep1.getLocationRef().getLastSegment()); + return rep0.getLocationRef() + .getLastSegment() + .compareTo(rep1.getLocationRef().getLastSegment()); }; - } - - /** - * Returns the context's target URI (file, clap URI). - * - * @return The context's target URI (file, clap URI). - */ - public String getTargetUri() { - return this.targetUri; - } - - @Override - public List getVariants() { - return getVariants(getMethod()); - } - - /** - * Returns the list of variants for the given method. - * - * @param method The related method. - * @return The list of variants for the given method. - */ - @Override - protected List getVariants(Method method) { - if (!Method.GET.equals(method) && !Method.HEAD.equals(method)) { - return null; - } - - if (variantsGet != null) { - return variantsGet; - } - - getLogger().fine("Getting variants for: " + getTargetUri()); - - if (this.fileTarget && (this.fileContent != null)) { - // found a target file, set its content location - if (getOriginalRef() != null) { - this.fileContent.setLocationRef(getRequest().getOriginalRef()); - } else { - this.fileContent.setLocationRef(getReference()); - } - - variantsGet = Arrays.asList(this.fileContent); - - return variantsGet; - } - - if ((this.directoryContent != null) && (getReference() != null) && (getReference().getBaseRef() != null)) { - // filter the directory listing - - // Allow sorting the list of representations - SortedSet resultSet = new TreeSet(getRepresentationsComparator()); - - // Compute the base reference (from a call's client point of view) - String baseReference = getVariantsBaseReference(); - - int rootLength = getDirectoryUri().length(); - - if (this.baseName != null) { - String filePath; - for (Reference ref : getVariantsReferences()) { - // Add the new variant to the result list - Response contextResponse = getRepresentation(ref.toString()); - if (contextResponse.getStatus().isSuccess() && (contextResponse.getEntity() != null)) { - filePath = ref.toString(false, false).substring(rootLength); - Representation rep = contextResponse.getEntity(); - - if (filePath.startsWith("/")) { - rep.setLocationRef(baseReference + filePath); - } else { - rep.setLocationRef(baseReference + "/" + filePath); - } - - resultSet.add(rep); - } - } - } - - if (!resultSet.isEmpty()) { - this.variantsGet = new ArrayList(resultSet); - - return this.variantsGet; - } - - if (this.directoryTarget && getDirectory().isListingAllowed()) { - // computes variants from the directory listing - ReferenceList userList = new ReferenceList(this.directoryContent.size()); - // Set the list identifier - userList.setIdentifier(baseReference); - - SortedSet sortedSet = new TreeSet(getDirectory().getComparator()); - sortedSet.addAll(this.directoryContent); - - for (Reference ref : sortedSet) { - String filePart = ref.toString(false, false).substring(rootLength); - StringBuilder filePath = new StringBuilder(); - if ((!baseReference.endsWith("/")) && (!filePart.startsWith("/"))) { - filePath.append('/'); - } - filePath.append(filePart); - userList.add(baseReference + filePath); - } - List list = getDirectory().getIndexVariants(userList); - - if (list != null && !list.isEmpty()) { - this.variantsGet = new ArrayList(); - for (Variant variant : list) { - this.variantsGet.add(getDirectory().getIndexRepresentation(variant, userList)); - } - } - } - } - - return this.variantsGet; - } - - private String getVariantsBaseReference() { - String baseRef = getReference().getBaseRef().toString(false, false); - - if (!baseRef.endsWith("/")) { - baseRef += "/"; - } - - int lastIndex = this.relativePart.lastIndexOf("/"); - - if (lastIndex != -1) { - baseRef += this.relativePart.substring(0, lastIndex); - } - return baseRef; - } - - /** - * Returns the references of the representations of the target resource - * according to the directory handler property - * - * @return The list of variants references - */ - private ReferenceList getVariantsReferences() { - this.uniqueReference = null; - - // Ask for the list of all variants of this resource - Response contextResponse = getRepresentation(this.targetUri, MediaType.TEXT_URI_LIST); - - if (contextResponse.getEntity() == null) { - return new ReferenceList(0); - } - - if (!MediaType.TEXT_URI_LIST.equals(contextResponse.getEntity().getMediaType())) { - // The unique reference has been found. - this.uniqueReference = contextResponse.getEntity().getLocationRef(); - return new ReferenceList(Arrays.asList(contextResponse.getEntity().getLocationRef())); - } - - ReferenceList listVariants; - try { - // Test if the given response is the list of all variants for this resource - listVariants = new ReferenceList(contextResponse.getEntity()); - } catch (IOException ioe) { - getLogger().log(Level.WARNING, "Unable to get resource variants", ioe); - return new ReferenceList(0); - } - - ReferenceList variantsReferences = new ReferenceList(0); - for (Reference variantReference : listVariants) { - String entryUri = variantReference.toString(); - int lastSlashIndex = entryUri.lastIndexOf('/'); - String fullEntryName = (lastSlashIndex == -1) ? entryUri : entryUri.substring(lastSlashIndex + 1); - - // Remove the extensions from the base name - int firstDotIndex = fullEntryName.indexOf('.'); - String baseEntryName = (firstDotIndex != -1) ? fullEntryName.substring(0, firstDotIndex) : fullEntryName; - - if (!baseEntryName.equals(this.baseName)) { - // Not a valid variant - continue; - } - - // Test if the variant is included in the base prototype variant - Variant variant = new Variant(); - Entity.updateMetadata(fullEntryName, variant, true, getMetadataService()); - - if (!this.protoVariant.includes(variant)) { - // Not a valid variant - continue; - } - - variantsReferences.add(variantReference); - - if (variant.equals(this.baseVariant)) { - // The unique reference has been found. - this.uniqueReference = variantReference; - } - } - - return variantsReferences; - } - - @Override - public Representation handle() { - if (!this.directoryRedirection) { - return super.handle(); - } - - // detected a directory, but the current reference lacks the trailing "/", let's - // redirect. - Reference directoryReference = (this.originalRef != null) ? this.originalRef : getReference().getTargetRef(); - if (directoryReference.hasQuery()) { - redirectSeeOther(directoryReference.toString(false, false) + "/?" + directoryReference.getQuery()); - } else { - redirectSeeOther(directoryReference.toString(false, false) + "/"); - } - - return null; - } - - /** - * Indicates if the target resource is a directory. - * - * @return True if the target resource is a directory. - */ - public boolean isDirectoryTarget() { - return this.directoryTarget; - } - - /** - * Indicates if the target resource is a file. - * - * @return True if the target resource is a file. - */ - public boolean isFileTarget() { - return this.fileTarget; - } - - /** - * Transmit the given request to the clientDispatcher.
- * It completes the request's attributes map with the current Directory - * ("org.restlet.directory" key). - * - * @param request The request to send. - * @return The response - */ - private Response dispatchRequest(final Request request) { - final Response response = new Response(request); - dispatchRequest(request, response); - - if (response.getStatus().equals(Status.CLIENT_ERROR_FORBIDDEN)) { - throw new ResourceException(response.getStatus()); - } - - return response; - } - - /** - * Transmit the given request to the clientDispatcher.
- * It completes the request's attributes map with the current Directory - * ("org.restlet.directory" key). - * - * @param request The request to send. - * @param response The related response. - */ - private void dispatchRequest(final Request request, final Response response) { - request.getAttributes().put("org.restlet.directory", this.directory); - getClientDispatcher().handle(request, response); - } - - /** - * Prevent the client from accessing resources in upper directories - */ - public void preventUpperDirectoryAccess() { - String targetUriPath = Reference.decode(targetUri); - - if (!targetUriPath.startsWith(Reference.decode(directory.getRootRef().toString()))) { - throw new ResourceException(Status.CLIENT_ERROR_FORBIDDEN); - } - } - - @Override - public Representation put(Representation entity) throws ResourceException { - if (!this.directory.isModifiable()) { - setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED, "The directory is not modifiable."); - return null; - } - - // Transfer of PUT calls is only allowed if the readOnly flag is not set. - Request contextRequest = new Request(Method.PUT, this.targetUri); - - // Add support of partial PUT calls. - contextRequest.getRanges().addAll(getRanges()); - contextRequest.setEntity(entity); - Response contextResponse = new Response(contextRequest); - contextRequest.setResourceRef(this.targetUri); - dispatchRequest(contextRequest, contextResponse); - setStatus(contextResponse.getStatus()); - - return null; - } - - /** - * Sets the context's target URI (file, clap URI). - * - * @param targetUri The context's target URI. - */ - public void setTargetUri(String targetUri) { - this.targetUri = targetUri; - } + } + + /** + * Returns the context's target URI (file, clap URI). + * + * @return The context's target URI (file, clap URI). + */ + public String getTargetUri() { + return this.targetUri; + } + + @Override + public List getVariants() { + return getVariants(getMethod()); + } + + /** + * Returns the list of variants for the given method. + * + * @param method The related method. + * @return The list of variants for the given method. + */ + @Override + protected List getVariants(Method method) { + if (!Method.GET.equals(method) && !Method.HEAD.equals(method)) { + return null; + } + + if (variantsGet != null) { + return variantsGet; + } + + getLogger().fine("Getting variants for: " + getTargetUri()); + + if (this.fileTarget && (this.fileContent != null)) { + // found a target file, set its content location + if (getOriginalRef() != null) { + this.fileContent.setLocationRef(getRequest().getOriginalRef()); + } else { + this.fileContent.setLocationRef(getReference()); + } + + variantsGet = Arrays.asList(this.fileContent); + + return variantsGet; + } + + if ((this.directoryContent != null) + && (getReference() != null) + && (getReference().getBaseRef() != null)) { + // filter the directory listing + + // Allow sorting the list of representations + SortedSet resultSet = + new TreeSet(getRepresentationsComparator()); + + // Compute the base reference (from a call's client point of view) + String baseReference = getVariantsBaseReference(); + + int rootLength = getDirectoryUri().length(); + + if (this.baseName != null) { + String filePath; + for (Reference ref : getVariantsReferences()) { + // Add the new variant to the result list + Response contextResponse = getRepresentation(ref.toString()); + if (contextResponse.getStatus().isSuccess() + && (contextResponse.getEntity() != null)) { + filePath = ref.toString(false, false).substring(rootLength); + Representation rep = contextResponse.getEntity(); + + if (filePath.startsWith("/")) { + rep.setLocationRef(baseReference + filePath); + } else { + rep.setLocationRef(baseReference + "/" + filePath); + } + + resultSet.add(rep); + } + } + } + + if (!resultSet.isEmpty()) { + this.variantsGet = new ArrayList<>(resultSet); + + return this.variantsGet; + } + + if (this.directoryTarget && getDirectory().isListingAllowed()) { + // computes variants from the directory listing + ReferenceList userList = new ReferenceList(this.directoryContent.size()); + // Set the list identifier + userList.setIdentifier(baseReference); + + SortedSet sortedSet = new TreeSet<>(getDirectory().getComparator()); + sortedSet.addAll(this.directoryContent); + + for (Reference ref : sortedSet) { + String filePart = ref.toString(false, false).substring(rootLength); + StringBuilder filePath = new StringBuilder(); + if ((!baseReference.endsWith("/")) && (!filePart.startsWith("/"))) { + filePath.append('/'); + } + filePath.append(filePart); + userList.add(baseReference + filePath); + } + List list = getDirectory().getIndexVariants(userList); + + if (list != null && !list.isEmpty()) { + this.variantsGet = new ArrayList<>(); + for (Variant variant : list) { + this.variantsGet.add( + getDirectory().getIndexRepresentation(variant, userList)); + } + } + } + } + + return this.variantsGet; + } + + private String getVariantsBaseReference() { + String baseRef = getReference().getBaseRef().toString(false, false); + + if (!baseRef.endsWith("/")) { + baseRef += "/"; + } + + int lastIndex = this.relativePart.lastIndexOf('/'); + + if (lastIndex != -1) { + baseRef += this.relativePart.substring(0, lastIndex); + } + return baseRef; + } + + /** + * Returns the references of the representations of the target resource according to the + * directory handler property + * + * @return The list of variants references + */ + private ReferenceList getVariantsReferences() { + this.uniqueReference = null; + + // Ask for the list of all variants of this resource + Response contextResponse = getRepresentation(this.targetUri, MediaType.TEXT_URI_LIST); + + if (contextResponse.getEntity() == null) { + return new ReferenceList(0); + } + + if (!MediaType.TEXT_URI_LIST.equals(contextResponse.getEntity().getMediaType())) { + // The unique reference has been found. + this.uniqueReference = contextResponse.getEntity().getLocationRef(); + return new ReferenceList(Arrays.asList(contextResponse.getEntity().getLocationRef())); + } + + ReferenceList listVariants; + try { + // Test if the given response is the list of all variants for this resource + listVariants = new ReferenceList(contextResponse.getEntity()); + } catch (IOException ioe) { + getLogger().log(Level.WARNING, "Unable to get resource variants", ioe); + return new ReferenceList(0); + } + + ReferenceList variantsReferences = new ReferenceList(0); + for (Reference variantReference : listVariants) { + String entryUri = variantReference.toString(); + int lastSlashIndex = entryUri.lastIndexOf('/'); + String fullEntryName = + (lastSlashIndex == -1) ? entryUri : entryUri.substring(lastSlashIndex + 1); + + // Remove the extensions from the base name + int firstDotIndex = fullEntryName.indexOf('.'); + String baseEntryName = + (firstDotIndex != -1) + ? fullEntryName.substring(0, firstDotIndex) + : fullEntryName; + + if (!baseEntryName.equals(this.baseName)) { + // Not a valid variant + continue; + } + + // Test if the variant is included in the base prototype variant + Variant variant = new Variant(); + Entity.updateMetadata(fullEntryName, variant, true, getMetadataService()); + + if (!this.protoVariant.includes(variant)) { + // Not a valid variant + continue; + } + + variantsReferences.add(variantReference); + + if (variant.equals(this.baseVariant)) { + // The unique reference has been found. + this.uniqueReference = variantReference; + } + } + + return variantsReferences; + } + + @Override + public Representation handle() { + if (!this.directoryRedirection) { + return super.handle(); + } + + // detected a directory, but the current reference lacks the trailing "/", let's + // redirect. + Reference directoryReference = + (this.originalRef != null) ? this.originalRef : getReference().getTargetRef(); + if (directoryReference.hasQuery()) { + redirectSeeOther( + directoryReference.toString(false, false) + + "/?" + + directoryReference.getQuery()); + } else { + redirectSeeOther(directoryReference.toString(false, false) + "/"); + } + + return null; + } + + /** + * Indicates if the target resource is a directory. + * + * @return True if the target resource is a directory. + */ + public boolean isDirectoryTarget() { + return this.directoryTarget; + } + + /** + * Indicates if the target resource is a file. + * + * @return True if the target resource is a file. + */ + public boolean isFileTarget() { + return this.fileTarget; + } + + /** + * Transmit the given request to the clientDispatcher.
+ * It completes the request's attributes map with the current Directory ("org.restlet.directory" + * key). + * + * @param request The request to send. + * @return The response + */ + private Response dispatchRequest(final Request request) { + final Response response = new Response(request); + dispatchRequest(request, response); + + if (response.getStatus().equals(Status.CLIENT_ERROR_FORBIDDEN)) { + throw new ResourceException(response.getStatus()); + } + + return response; + } + + /** + * Transmit the given request to the clientDispatcher.
+ * It completes the request's attributes map with the current Directory ("org.restlet.directory" + * key). + * + * @param request The request to send. + * @param response The related response. + */ + private void dispatchRequest(final Request request, final Response response) { + request.getAttributes().put("org.restlet.directory", this.directory); + getClientDispatcher().handle(request, response); + } + + /** Prevent the client from accessing resources in upper directories */ + public void preventUpperDirectoryAccess() { + String targetUriPath = Reference.decode(targetUri); + + if (!targetUriPath.startsWith(Reference.decode(directory.getRootRef().toString()))) { + throw new ResourceException(Status.CLIENT_ERROR_FORBIDDEN); + } + } + + @Override + public Representation put(Representation entity) throws ResourceException { + if (!this.directory.isModifiable()) { + setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED, "The directory is not modifiable."); + return null; + } + + // Transfer of PUT calls is only allowed if the readOnly flag is not set. + Request contextRequest = new Request(Method.PUT, this.targetUri); + + // Add support for partial PUT calls. + contextRequest.getRanges().addAll(getRanges()); + contextRequest.setEntity(entity); + Response contextResponse = new Response(contextRequest); + contextRequest.setResourceRef(this.targetUri); + dispatchRequest(contextRequest, contextResponse); + setStatus(contextResponse.getStatus()); + + return null; + } + + /** + * Sets the context's target URI (file, clap URI). + * + * @param targetUri The context's target URI. + */ + public void setTargetUri(String targetUri) { + this.targetUri = targetUri; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java index 42dfe7d2bc..6e265778cc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java @@ -1,315 +1,319 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; -import org.restlet.data.*; -import org.restlet.representation.Representation; -import org.restlet.representation.Variant; -import org.restlet.service.MetadataService; - import java.io.File; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.TreeSet; +import org.restlet.data.CharacterSet; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.representation.Representation; +import org.restlet.representation.Variant; +import org.restlet.service.MetadataService; /** * Represents a local entity, for example a regular file or a directory. - * + * * @author Thierry Boileau * @author Jerome Louvel */ public abstract class Entity { - /** - * Return the base name that is to say the longest part of a given name without - * known extensions (beginning from the left). - * - * @param name The given name. - * @param metadataService Service that holds the known extensions. - * @return The base name of this entity. - */ - public static String getBaseName(String name, MetadataService metadataService) { - final String[] result = name.split("\\."); - final StringBuilder baseName = new StringBuilder().append(result[0]); - boolean extensionFound = false; - for (int i = 1; (i < result.length) && !extensionFound; i++) { - extensionFound = metadataService.getMetadata(result[i]) != null; - if (!extensionFound) { - baseName.append(".").append(result[i]); - } - } - return baseName.toString(); - } - - /** - * Returns the list of known extensions taken from a given entity name. - * - * @param name the given name. - * @param metadataService Service that holds the known extensions. - * @return The list of known extensions taken from the entity name. - */ - public static Collection getExtensions(String name, MetadataService metadataService) { - final Set result = new TreeSet(); - final String[] tokens = name.split("\\."); - boolean extensionFound = false; - - int i; - for (i = 1; (i < tokens.length) && !extensionFound; i++) { - extensionFound = metadataService.getMetadata(tokens[i]) != null; - } - if (extensionFound) { - for (--i; (i < tokens.length); i++) { - result.add(tokens[i]); - } - } - - return result; - } - - /** - * Returns the list of known extensions taken from a given variant. - * - * @param variant the given variant. - * @param metadataService Service that holds the known extensions. - * @return The list of known extensions taken from the variant. - */ - public static Collection getExtensions(Variant variant, MetadataService metadataService) { - final Set result = new TreeSet(); - - String extension = metadataService.getExtension(variant.getCharacterSet()); - if (extension != null) { - result.add(extension); - } - extension = metadataService.getExtension(variant.getMediaType()); - if (extension != null) { - result.add(extension); - } - for (Language language : variant.getLanguages()) { - extension = metadataService.getExtension(language); - if (extension != null) { - result.add(extension); - } - } - for (Encoding encoding : variant.getEncodings()) { - extension = metadataService.getExtension(encoding); - if (extension != null) { - result.add(extension); - } - } - - return result; - } - - /** - * Updates some variant metadata based on a given entry name with extensions. - * - * @param entryName The entry name with extensions. - * @param variant The variant to update. - * @param applyDefault Indicate if default metadata must be applied. - * @param metadataService The parent metadata service. - */ - public static void updateMetadata(String entryName, Variant variant, boolean applyDefault, - MetadataService metadataService) { - if (variant != null) { - String[] tokens = entryName.split("\\."); - Metadata current; - - // We found a potential variant - for (int j = 1; j < tokens.length; j++) { - current = metadataService.getMetadata(tokens[j]); - - if (current != null) { - // Metadata extension detected - if (current instanceof MediaType) { - variant.setMediaType((MediaType) current); - } else if (current instanceof CharacterSet) { - variant.setCharacterSet((CharacterSet) current); - } else if (current instanceof Encoding) { - // Do we need to add this metadata? - boolean found = false; - for (int i = 0; !found && i < variant.getEncodings().size(); i++) { - found = current.includes(variant.getEncodings().get(i)); - } - if (!found) { - variant.getEncodings().add((Encoding) current); - } - } else if (current instanceof Language) { - // Do we need to add this metadata? - boolean found = false; - for (int i = 0; !found && i < variant.getLanguages().size(); i++) { - found = current.includes(variant.getLanguages().get(i)); - } - if (!found) { - variant.getLanguages().add((Language) current); - } - } - } - - final int dashIndex = tokens[j].indexOf('-'); - if (dashIndex != -1) { - // We found a language extension with a region area - // specified. - // Try to find a language matching the primary part of the - // extension. - final String primaryPart = tokens[j].substring(0, dashIndex); - current = metadataService.getMetadata(primaryPart); - if (current instanceof Language) { - variant.getLanguages().add((Language) current); - } - } - } - - if (applyDefault) { - // If no language is defined, take the default one - if (variant.getLanguages().isEmpty()) { - final Language defaultLanguage = metadataService.getDefaultLanguage(); - - if ((defaultLanguage != null) && !defaultLanguage.equals(Language.ALL)) { - variant.getLanguages().add(defaultLanguage); - } - } - - // If no media type is defined, take the default one - if (variant.getMediaType() == null) { - final MediaType defaultMediaType = metadataService.getDefaultMediaType(); - - if ((defaultMediaType != null) && !defaultMediaType.equals(MediaType.ALL)) { - variant.setMediaType(defaultMediaType); - } - } - - // If no encoding is defined, take the default one - if (variant.getEncodings().isEmpty()) { - final Encoding defaultEncoding = metadataService.getDefaultEncoding(); - - if ((defaultEncoding != null) && !defaultEncoding.equals(Encoding.ALL) - && !defaultEncoding.equals(Encoding.IDENTITY)) { - variant.getEncodings().add(defaultEncoding); - } - } - - // If no character set is defined, take the default one - if (variant.getCharacterSet() == null) { - final CharacterSet defaultCharacterSet = metadataService.getDefaultCharacterSet(); - - if ((defaultCharacterSet != null) && !defaultCharacterSet.equals(CharacterSet.ALL)) { - variant.setCharacterSet(defaultCharacterSet); - } - } - } - } - } - - /** The metadata service to use. */ - private volatile MetadataService metadataService; - - /** - * Constructor. - * - * @param metadataService The metadata service to use. - */ - public Entity(MetadataService metadataService) { - this.metadataService = metadataService; - } - - /** - * Indicates if the entity does exist. - * - * @return True if the entity does exists. - */ - public abstract boolean exists(); - - /** - * Return the base name of this entity that is to say the longest part of the - * name without known extensions (beginning from the left). - * - * @return The base name of this entity. - */ - public String getBaseName() { - return getBaseName(getName(), getMetadataService()); - } - - /** - * Returns the list of contained entities if the current entity is a directory, - * null otherwise. - * - * @return The list of contained entities. - */ - public abstract List getChildren(); - - /** - * Returns the list of known extensions. - * - * @return The list of known extensions taken from the entity name. - */ - public Collection getExtensions() { - return getExtensions(getName(), getMetadataService()); - } - - /** - * Returns the metadata service to use. - * - * @return The metadata service to use. - */ - public MetadataService getMetadataService() { - return metadataService; - } - - /** - * Returns the name. - * - * @return The name. - */ - public abstract String getName(); - - /** - * Returns the parent directory (if any). - * - * @return The parent directory, null otherwise. - */ - public abstract Entity getParent(); - - /** - * Returns a representation of this local entity. - * - * @return A representation of this entity. - */ - public abstract Representation getRepresentation(MediaType defaultMediaType, int timeToLive); - - /** - * Returns a variant corresponding to the extensions of this entity. - * - * @return A variant corresponding to the extensions of this entity. - */ - public Variant getVariant() { - Variant result = new Variant(); - updateMetadata(getName(), result, true, getMetadataService()); - return result; - } - - /** - * Indicates if the entity is a directory. - * - * @return True if the entity is a directory. - */ - public abstract boolean isDirectory(); - - /** - * Indicates if the entity is a normal entity, especially if it is not a - * directory. - * - * @return True if the entity is a normal entity. - * @see File#isFile() - * @see File#isDirectory() - */ - public abstract boolean isNormal(); - + /** + * Return the base name that is to say the longest part of a given name without known extensions + * (beginning from the left). + * + * @param name The given name. + * @param metadataService Service that holds the known extensions. + * @return The base name of this entity. + */ + public static String getBaseName(String name, MetadataService metadataService) { + final String[] result = name.split("\\."); + final StringBuilder baseName = new StringBuilder().append(result[0]); + boolean extensionFound = false; + for (int i = 1; (i < result.length) && !extensionFound; i++) { + extensionFound = metadataService.getMetadata(result[i]) != null; + if (!extensionFound) { + baseName.append(".").append(result[i]); + } + } + return baseName.toString(); + } + + /** + * Returns the list of known extensions taken from a given entity name. + * + * @param name the given name. + * @param metadataService Service that holds the known extensions. + * @return The list of known extensions taken from the entity name. + */ + public static Collection getExtensions(String name, MetadataService metadataService) { + final Set result = new TreeSet<>(); + final String[] tokens = name.split("\\."); + boolean extensionFound = false; + + int i; + for (i = 1; (i < tokens.length) && !extensionFound; i++) { + extensionFound = metadataService.getMetadata(tokens[i]) != null; + } + if (extensionFound) { + for (--i; (i < tokens.length); i++) { + result.add(tokens[i]); + } + } + + return result; + } + + /** + * Returns the list of known extensions taken from a given variant. + * + * @param variant the given variant. + * @param metadataService Service that holds the known extensions. + * @return The list of known extensions taken from the variant. + */ + public static Collection getExtensions( + Variant variant, MetadataService metadataService) { + final Set result = new TreeSet<>(); + + String extension = metadataService.getExtension(variant.getCharacterSet()); + if (extension != null) { + result.add(extension); + } + extension = metadataService.getExtension(variant.getMediaType()); + if (extension != null) { + result.add(extension); + } + for (Language language : variant.getLanguages()) { + extension = metadataService.getExtension(language); + if (extension != null) { + result.add(extension); + } + } + for (Encoding encoding : variant.getEncodings()) { + extension = metadataService.getExtension(encoding); + if (extension != null) { + result.add(extension); + } + } + + return result; + } + + /** + * Updates some variant metadata based on a given entry name with extensions. + * + * @param entryName The entry name with extensions. + * @param variant The variant to update. + * @param applyDefault Indicate if default metadata must be applied. + * @param metadataService The parent metadata service. + */ + public static void updateMetadata( + String entryName, + Variant variant, + boolean applyDefault, + MetadataService metadataService) { + if (variant != null) { + String[] tokens = entryName.split("\\."); + Metadata current; + + // We found a potential variant + for (int j = 1; j < tokens.length; j++) { + current = metadataService.getMetadata(tokens[j]); + + if (current != null) { + // Metadata extension detected + if (current instanceof MediaType) { + variant.setMediaType((MediaType) current); + } else if (current instanceof CharacterSet) { + variant.setCharacterSet((CharacterSet) current); + } else if (current instanceof Encoding) { + // Do we need to add this metadata? + boolean found = false; + for (int i = 0; !found && i < variant.getEncodings().size(); i++) { + found = current.includes(variant.getEncodings().get(i)); + } + if (!found) { + variant.getEncodings().add((Encoding) current); + } + } else if (current instanceof Language) { + // Do we need to add this metadata? + boolean found = false; + for (int i = 0; !found && i < variant.getLanguages().size(); i++) { + found = current.includes(variant.getLanguages().get(i)); + } + if (!found) { + variant.getLanguages().add((Language) current); + } + } + } + + final int dashIndex = tokens[j].indexOf('-'); + if (dashIndex != -1) { + // We found a language extension with a region area specified. + // Try to find a language matching the primary part of the extension. + final String primaryPart = tokens[j].substring(0, dashIndex); + current = metadataService.getMetadata(primaryPart); + if (current instanceof Language) { + variant.getLanguages().add((Language) current); + } + } + } + + if (applyDefault) { + // If no language is defined, take the default one + if (variant.getLanguages().isEmpty()) { + final Language defaultLanguage = metadataService.getDefaultLanguage(); + + if ((defaultLanguage != null) && !defaultLanguage.equals(Language.ALL)) { + variant.getLanguages().add(defaultLanguage); + } + } + + // If no media type is defined, take the default one + if (variant.getMediaType() == null) { + final MediaType defaultMediaType = metadataService.getDefaultMediaType(); + + if ((defaultMediaType != null) && !defaultMediaType.equals(MediaType.ALL)) { + variant.setMediaType(defaultMediaType); + } + } + + // If no encoding is defined, take the default one + if (variant.getEncodings().isEmpty()) { + final Encoding defaultEncoding = metadataService.getDefaultEncoding(); + + if ((defaultEncoding != null) + && !defaultEncoding.equals(Encoding.ALL) + && !defaultEncoding.equals(Encoding.IDENTITY)) { + variant.getEncodings().add(defaultEncoding); + } + } + + // If no character set is defined, take the default one + if (variant.getCharacterSet() == null) { + final CharacterSet defaultCharacterSet = + metadataService.getDefaultCharacterSet(); + + if ((defaultCharacterSet != null) + && !defaultCharacterSet.equals(CharacterSet.ALL)) { + variant.setCharacterSet(defaultCharacterSet); + } + } + } + } + } + + /** The metadata service to use. */ + private volatile MetadataService metadataService; + + /** + * Constructor. + * + * @param metadataService The metadata service to use. + */ + public Entity(MetadataService metadataService) { + this.metadataService = metadataService; + } + + /** + * Indicates if the entity does exist. + * + * @return True if the entity does exist. + */ + public abstract boolean exists(); + + /** + * Return the base name of this entity, meaning the longest part of the name without known + * extensions (beginning from the left). + * + * @return The base name of this entity. + */ + public String getBaseName() { + return getBaseName(getName(), getMetadataService()); + } + + /** + * Returns the list of contained entities if the current entity is a directory, null otherwise. + * + * @return The list of contained entities. + */ + public abstract List getChildren(); + + /** + * Returns the list of known extensions. + * + * @return The list of known extensions taken from the entity name. + */ + public Collection getExtensions() { + return getExtensions(getName(), getMetadataService()); + } + + /** + * Returns the metadata service to use. + * + * @return The metadata service to use. + */ + public MetadataService getMetadataService() { + return metadataService; + } + + /** + * Returns the name. + * + * @return The name. + */ + public abstract String getName(); + + /** + * Returns the parent directory (if any). + * + * @return The parent directory, null otherwise. + */ + public abstract Entity getParent(); + + /** + * Returns a representation of this local entity. + * + * @return A representation of this entity. + */ + public abstract Representation getRepresentation(MediaType defaultMediaType, int timeToLive); + + /** + * Returns a variant corresponding to the extensions of this entity. + * + * @return A variant corresponding to the extensions of this entity. + */ + public Variant getVariant() { + Variant result = new Variant(); + updateMetadata(getName(), result, true, getMetadataService()); + return result; + } + + /** + * Indicates if the entity is a directory. + * + * @return True if the entity is a directory. + */ + public abstract boolean isDirectory(); + + /** + * Indicates if the entity is a normal entity, especially if it is not a directory. + * + * @return True if the entity is a normal entity. + * @see File#isFile() + * @see File#isDirectory() + */ + public abstract boolean isNormal(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java index b23b57e479..522c92619e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java @@ -1,252 +1,269 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; +import java.util.Collection; +import java.util.Iterator; import org.restlet.Client; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Preference; +import org.restlet.data.Reference; +import org.restlet.data.ReferenceList; +import org.restlet.data.Status; import org.restlet.representation.Representation; import org.restlet.representation.Variant; -import java.util.Collection; -import java.util.Iterator; - /** - * Connector to the local entities. That connector supports the content - * negotiation feature (i.e. for GET and HEAD methods) and implements the - * response to GET/HEAD methods. - * + * Connector to the local entities. That connector supports the content negotiation feature (i.e., + * for GET and HEAD methods) and implements the response to GET/HEAD methods. + * * @author Thierry Boileau */ public abstract class EntityClientHelper extends LocalClientHelper { - /** - * Constructor. - * - * @param client The client to help. - */ - public EntityClientHelper(Client client) { - super(client); - } - - /** - * Generate a Reference for a variant name (which is URL decoded) and handle the - * translation between the incoming requested path (which is URL encoded). - * - * @param scheme The scheme of the requested resource. - * @param encodedParentDirPath The encoded path of the parent directory of the - * requested resource. - * @param encodedEntityName The encoded name of the requested resource. - * @param decodedVariantName The decoded name of a returned resource. - * @return A new Reference. - */ - public Reference createReference(String scheme, String encodedParentDirPath, String encodedEntityName, - String decodedVariantName) { - return new Reference(scheme + "://" + encodedParentDirPath + "/" - + getReencodedVariantEntityName(encodedEntityName, decodedVariantName)); - } - - /** - * Returns a local entity for the given path. - * - * @param path The path of the entity. - * @return A local entity for the given path. - */ - public abstract Entity getEntity(String path); - - /** - * Percent-encodes the given percent-decoded variant name of a resource whose - * percent-encoded name is given. Tries to match the longest common part of both - * encoded entity name and decoded variant name. - * - * @param encodedEntityName the percent-encoded name of the initial - * resource - * @param decodedVariantEntityName the percent-decoded entity name of a variant - * of the initial resource. - * @return The variant percent-encoded entity name. - */ - protected String getReencodedVariantEntityName(String encodedEntityName, String decodedVariantEntityName) { - int i = 0; - int j = 0; - boolean stop = false; - char[] encodeds = encodedEntityName.toCharArray(); - char[] decodeds = decodedVariantEntityName.toCharArray(); - - for (i = 0; (i < decodeds.length) && (j < encodeds.length) && !stop; i++) { - char decodedChar = decodeds[i]; - char encodedChar = encodeds[j]; - - if (encodedChar == '%') { - String dec = Reference.decode(encodedEntityName.substring(j, j + 3)); - if (decodedChar == dec.charAt(0)) { - j += 3; - } else { - stop = true; - } - } else if (decodedChar == encodedChar) { - j++; - } else { - String dec = Reference.decode(encodedEntityName.substring(j, j + 1)); - if (decodedChar == dec.charAt(0)) { - j++; - } else { - stop = true; - } - } - } - - if (stop) { - return encodedEntityName.substring(0, j) + decodedVariantEntityName.substring(i - 1); - } - - if (j == encodedEntityName.length()) { - return encodedEntityName.substring(0, j) + decodedVariantEntityName.substring(i); - } - - return encodedEntityName.substring(0, j); - } - - /** - * Handles a GET call. - * - * @param request The request to answer. - * @param response The response to update. - * @param entity The requested entity (normal or directory). - */ - protected void handleEntityGet(Request request, Response response, Entity entity) { - Representation output = null; - - // Get variants for a resource - boolean found = false; - Iterator> iterator = request.getClientInfo().getAcceptedMediaTypes().iterator(); - while (iterator.hasNext() && !found) { - Preference pref = iterator.next(); - found = pref.getMetadata().equals(MediaType.TEXT_URI_LIST); - } - - if (found) { - // Try to list all variants of this resource - // 1- set up base name as the longest part of the name without known - // extensions (beginning from the left) - String baseName = entity.getBaseName(); - - // 2- looking for resources with the same base name - Entity parent = entity.getParent(); - - if (parent != null) { - Collection entities = parent.getChildren(); - - if (entities != null) { - ReferenceList rl = new ReferenceList(entities.size()); - String scheme = request.getResourceRef().getScheme(); - String path = request.getResourceRef().getPath(); - String encodedParentDirectoryURI = path.substring(0, path.lastIndexOf("/")); - String encodedEntityName = path.substring(path.lastIndexOf("/") + 1); - - for (Entity entry : entities) { - if (baseName.equals(entry.getBaseName())) { - rl.add(createReference(scheme, encodedParentDirectoryURI, encodedEntityName, - entry.getName())); - } - } - - output = rl.getTextRepresentation(); - } - } - } else { - if (entity.exists()) { - if (entity.isDirectory()) { - // Return the directory listing - Collection children = entity.getChildren(); - ReferenceList rl = new ReferenceList(children.size()); - String directoryUri = request.getResourceRef().getTargetRef().toString(); - - // Ensures that the directory URI ends with a slash - if (!directoryUri.endsWith("/")) { - directoryUri += "/"; - } - - for (Entity entry : children) { - if (entry.isDirectory()) { - rl.add(directoryUri + Reference.encode(entry.getName()) + "/"); - } else { - rl.add(directoryUri + Reference.encode(entry.getName())); - } - } - - output = rl.getTextRepresentation(); - } else { - // Return the file content - output = entity.getRepresentation(getMetadataService().getDefaultMediaType(), getTimeToLive()); - output.setLocationRef(request.getResourceRef()); - Entity.updateMetadata(entity.getName(), output, true, getMetadataService()); - } - } else { - // We look for the possible variant which has the same - // metadata based on extensions (in a distinct order) and - // default metadata. - Entity uniqueVariant = null; - - // 1- set up base name as the longest part of the name without - // known extensions (beginning from the left) - String baseName = entity.getBaseName(); - Variant entityVariant = entity.getVariant(); - - // 2- looking for resources with the same base name - Entity parent = entity.getParent(); - if (parent != null) { - Collection files = parent.getChildren(); - - if (files != null) { - for (Entity entry : files) { - if (baseName.equals(entry.getBaseName())) { - Variant entryVariant = entry.getVariant(); - - if (entityVariant.isCompatible(entryVariant)) { - // The right representation has been found. - uniqueVariant = entry; - break; - } - } - } - } - } - - if (uniqueVariant != null) { - // Return the file content - output = uniqueVariant.getRepresentation(getMetadataService().getDefaultMediaType(), - getTimeToLive()); - output.setLocationRef(request.getResourceRef()); - Entity.updateMetadata(entity.getName(), output, true, getMetadataService()); - } - } - } - - if (output == null) { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } else { - output.setLocationRef(request.getResourceRef()); - response.setEntity(output); - response.setStatus(Status.SUCCESS_OK); - } - } - - @Override - protected void handleLocal(Request request, Response response, String decodedPath) { - if (Method.GET.equals(request.getMethod()) || Method.HEAD.equals(request.getMethod())) { - handleEntityGet(request, response, getEntity(decodedPath)); - } else { - response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - response.getAllowedMethods().add(Method.GET); - response.getAllowedMethods().add(Method.HEAD); - } - } + /** + * Constructor. + * + * @param client The client to help. + */ + public EntityClientHelper(Client client) { + super(client); + } + + /** + * Generate a Reference for a variant name (which is URL decoded) and handle the translation + * between the incoming requested path (which is URL encoded). + * + * @param scheme The scheme of the requested resource. + * @param encodedParentDirPath The encoded path of the parent directory of the requested + * resource. + * @param encodedEntityName The encoded name of the requested resource. + * @param decodedVariantName The decoded name of a returned resource. + * @return A new Reference. + */ + public Reference createReference( + String scheme, + String encodedParentDirPath, + String encodedEntityName, + String decodedVariantName) { + return new Reference( + scheme + + "://" + + encodedParentDirPath + + "/" + + getReencodedVariantEntityName(encodedEntityName, decodedVariantName)); + } + + /** + * Returns a local entity for the given path. + * + * @param path The path of the entity. + * @return A local entity for the given path. + */ + public abstract Entity getEntity(String path); + + /** + * Percent-encodes the given percent-decoded variant name of a resource whose percent-encoded + * name is given. Tries to match the longest common part of both encoded entity name and decoded + * variant name. + * + * @param encodedEntityName the percent-encoded name of the initial resource + * @param decodedVariantEntityName the percent-decoded entity name of a variant of the initial + * resource. + * @return The variant percent-encoded entity name. + */ + protected String getReencodedVariantEntityName( + String encodedEntityName, String decodedVariantEntityName) { + int i = 0; + int j = 0; + boolean stop = false; + char[] encodeds = encodedEntityName.toCharArray(); + char[] decodeds = decodedVariantEntityName.toCharArray(); + + for (i = 0; (i < decodeds.length) && (j < encodeds.length) && !stop; i++) { + char decodedChar = decodeds[i]; + char encodedChar = encodeds[j]; + + if (encodedChar == '%') { + String dec = Reference.decode(encodedEntityName.substring(j, j + 3)); + if (decodedChar == dec.charAt(0)) { + j += 3; + } else { + stop = true; + } + } else if (decodedChar == encodedChar) { + j++; + } else { + String dec = Reference.decode(encodedEntityName.substring(j, j + 1)); + if (decodedChar == dec.charAt(0)) { + j++; + } else { + stop = true; + } + } + } + + if (stop) { + return encodedEntityName.substring(0, j) + decodedVariantEntityName.substring(i - 1); + } + + if (j == encodedEntityName.length()) { + return encodedEntityName.substring(0, j) + decodedVariantEntityName.substring(i); + } + + return encodedEntityName.substring(0, j); + } + + /** + * Handles a GET call. + * + * @param request The request to answer. + * @param response The response to update. + * @param entity The requested entity (normal or directory). + */ + protected void handleEntityGet(Request request, Response response, Entity entity) { + Representation output = null; + + // Get variants for a resource + boolean found = false; + Iterator> iterator = + request.getClientInfo().getAcceptedMediaTypes().iterator(); + while (iterator.hasNext() && !found) { + Preference pref = iterator.next(); + found = pref.getMetadata().equals(MediaType.TEXT_URI_LIST); + } + + if (found) { + // Try to list all variants of this resource + // 1- set up the base name as the longest part of the name without known + // extensions (beginning from the left) + String baseName = entity.getBaseName(); + + // 2- looking for resources with the same base name + Entity parent = entity.getParent(); + + if (parent != null) { + Collection entities = parent.getChildren(); + + if (entities != null) { + ReferenceList rl = new ReferenceList(entities.size()); + String scheme = request.getResourceRef().getScheme(); + String path = request.getResourceRef().getPath(); + String encodedParentDirectoryURI = path.substring(0, path.lastIndexOf('/')); + String encodedEntityName = path.substring(path.lastIndexOf('/') + 1); + + for (Entity entry : entities) { + if (baseName.equals(entry.getBaseName())) { + rl.add( + createReference( + scheme, + encodedParentDirectoryURI, + encodedEntityName, + entry.getName())); + } + } + + output = rl.getTextRepresentation(); + } + } + } else { + if (entity.exists()) { + if (entity.isDirectory()) { + // Return the directory listing + Collection children = entity.getChildren(); + ReferenceList rl = new ReferenceList(children.size()); + String directoryUri = request.getResourceRef().getTargetRef().toString(); + + // Ensures that the directory URI ends with a slash + if (!directoryUri.endsWith("/")) { + directoryUri += "/"; + } + + for (Entity entry : children) { + if (entry.isDirectory()) { + rl.add(directoryUri + Reference.encode(entry.getName()) + "/"); + } else { + rl.add(directoryUri + Reference.encode(entry.getName())); + } + } + + output = rl.getTextRepresentation(); + } else { + // Return the file content + output = + entity.getRepresentation( + getMetadataService().getDefaultMediaType(), getTimeToLive()); + output.setLocationRef(request.getResourceRef()); + Entity.updateMetadata(entity.getName(), output, true, getMetadataService()); + } + } else { + // We look for the possible variant which has the same + // metadata based on extensions (in a distinct order) and + // default metadata. + Entity uniqueVariant = null; + + // 1- set up the base name as the longest part of the name without + // known extensions (beginning from the left) + String baseName = entity.getBaseName(); + Variant entityVariant = entity.getVariant(); + + // 2- looking for resources with the same base name + Entity parent = entity.getParent(); + if (parent != null) { + Collection files = parent.getChildren(); + + if (files != null) { + for (Entity entry : files) { + if (baseName.equals(entry.getBaseName())) { + Variant entryVariant = entry.getVariant(); + + if (entityVariant.isCompatible(entryVariant)) { + // The right representation has been found. + uniqueVariant = entry; + break; + } + } + } + } + } + + if (uniqueVariant != null) { + // Return the file content + output = + uniqueVariant.getRepresentation( + getMetadataService().getDefaultMediaType(), getTimeToLive()); + output.setLocationRef(request.getResourceRef()); + Entity.updateMetadata(entity.getName(), output, true, getMetadataService()); + } + } + } + + if (output == null) { + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } else { + output.setLocationRef(request.getResourceRef()); + response.setEntity(output); + response.setStatus(Status.SUCCESS_OK); + } + } + + @Override + protected void handleLocal(Request request, Response response, String decodedPath) { + if (Method.GET.equals(request.getMethod()) || Method.HEAD.equals(request.getMethod())) { + handleEntityGet(request, response, getEntity(decodedPath)); + } else { + response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + response.getAllowedMethods().add(Method.GET); + response.getAllowedMethods().add(Method.HEAD); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java index 381493d0de..355e64aea6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java @@ -1,22 +1,30 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; -import org.restlet.Client; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.*; -import org.restlet.engine.io.IoUtils; -import org.restlet.representation.Representation; -import org.restlet.representation.Variant; -import org.restlet.resource.Directory; +import static java.lang.String.format; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static java.util.logging.Level.WARNING; +import static org.restlet.data.Method.DELETE; +import static org.restlet.data.Method.GET; +import static org.restlet.data.Method.HEAD; +import static org.restlet.data.Method.PUT; +import static org.restlet.data.Protocol.FILE; +import static org.restlet.data.Range.isBytesRange; +import static org.restlet.data.Status.CLIENT_ERROR_BAD_REQUEST; +import static org.restlet.data.Status.CLIENT_ERROR_FORBIDDEN; +import static org.restlet.data.Status.CLIENT_ERROR_METHOD_NOT_ALLOWED; +import static org.restlet.data.Status.CLIENT_ERROR_NOT_ACCEPTABLE; +import static org.restlet.data.Status.SERVER_ERROR_INTERNAL; +import static org.restlet.data.Status.SUCCESS_CREATED; +import static org.restlet.data.Status.SUCCESS_NO_CONTENT; +import static org.restlet.data.Status.SUCCESS_OK; import java.io.File; import java.io.FileFilter; @@ -25,20 +33,31 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.*; - -import static java.lang.String.format; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static java.util.logging.Level.WARNING; -import static org.restlet.data.Method.*; -import static org.restlet.data.Protocol.FILE; -import static org.restlet.data.Range.isBytesRange; -import static org.restlet.data.Status.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import org.restlet.Client; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.CharacterSet; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.LocalReference; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Range; +import org.restlet.data.Status; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.Representation; +import org.restlet.representation.Variant; +import org.restlet.resource.Directory; /** - * Connector to the file resources accessible. Here is the list of parameters - * that are supported. They should be set in the Client's context before it is - * started: + * Connector to the file resources accessible. Here is the list of parameters that are supported. + * They should be set in the Client's context before it is started: + * * * * @@ -62,530 +81,543 @@ * deletion of the temporary file created. * *
list of supported parameters
- * + * * @author Jerome Louvel * @author Thierry Boileau */ public class FileClientHelper extends EntityClientHelper { - /** - * Constructor. - * - * @param client The client to help. - */ - public FileClientHelper(Client client) { - super(client); - getProtocols().add(FILE); - } - - /** - * Check that all extensions of the file correspond to a known metadata. - * - * @param file The file whose extensions are checked. - * @return True if all extensions of the file are known by the metadata service. - */ - protected boolean checkExtensionsConsistency(File file) { - boolean knownExtension = true; - - Collection set = Entity.getExtensions(file.getName(), getMetadataService()); - Iterator iterator = set.iterator(); - while (iterator.hasNext() && knownExtension) { - knownExtension = getMetadataService().getMetadata(iterator.next()) != null; - } - - return knownExtension; - } - - /** - * Checks that the URI and the representation are compatible. The whole set of - * metadata of the representation must be included in the set of those of the - * URI - * - * @param fileName The name of the resource - * @param representation The provided representation. - * @return True if the metadata of the representation are compatible with the - * metadata extracted from the filename - */ - private boolean checkMetadataConsistency(String fileName, Representation representation) { - if (representation != null) { - Variant var = new Variant(); - Entity.updateMetadata(fileName, var, false, getMetadataService()); - - // "var" contains the theoretical correct metadata - if (!var.getLanguages().isEmpty() && !representation.getLanguages().isEmpty() - && !new HashSet<>(var.getLanguages()).containsAll(representation.getLanguages())) { - return false; - } - - if ((var.getMediaType() != null) && (representation.getMediaType() != null) - && !(var.getMediaType().includes(representation.getMediaType()))) { - return false; - } - - if (!var.getEncodings().isEmpty() && !representation.getEncodings().isEmpty() - && !new HashSet<>(var.getEncodings()).containsAll(representation.getEncodings())) { - return false; - } - } - return true; - } - - @Override - public Entity getEntity(String decodedPath) { - return new FileEntity(getFileWithLocalizedPath(decodedPath), getMetadataService()); - } - - /** - * Returns a new {@link File} instance for the given path name.
- * It ensures to translate the "/" to the local supported file separator (mainly - * useful for Windows OS). - * - * @param path The Path of the file - * @return a new {@link File} instance for the given path name. - */ - private static File getFileWithLocalizedPath(final String path) { - return new File(LocalReference.localizePath(path)); - } - - /** - * Returns the name of the extension to use to store the temporary content while - * uploading content via the PUT method. Defaults to "tmp". - * - * @return The name of the extension to use to store the temporary content. - */ - public String getTemporaryExtension() { - return getHelpedParameters().getFirstValue("temporaryExtension", "tmp"); - } - - @Override - protected void handleLocal(Request request, Response response, String decodedPath) { - String scheme = request.getResourceRef().getScheme(); - - if (!FILE.getSchemeName().equalsIgnoreCase(scheme)) { - throw new IllegalArgumentException( - format("Protocol \"%s\" not supported by the connector. Only FILE is supported.", scheme)); - } - - handleFile(request, response, decodedPath); - } - - protected void handleFile(Request request, Response response, String decodedPath) { - final Directory directory = (Directory) request.getAttributes().get("org.restlet.directory"); - final File fileWithLocalizedPath = getFileWithLocalizedPath(decodedPath); - - if (!isFileInDirectory(directory, fileWithLocalizedPath)) { - response.setStatus(CLIENT_ERROR_FORBIDDEN); - } else if (GET.equals(request.getMethod()) || HEAD.equals(request.getMethod())) { - handleEntityGet(request, response, getEntity(decodedPath)); - } else if (PUT.equals(request.getMethod())) { - handleFilePut(request, response, decodedPath, fileWithLocalizedPath); - } else if (DELETE.equals(request.getMethod())) { - handleFileDelete(response, fileWithLocalizedPath); - } else { - response.setStatus(CLIENT_ERROR_METHOD_NOT_ALLOWED); - response.getAllowedMethods().add(GET); - response.getAllowedMethods().add(HEAD); - response.getAllowedMethods().add(PUT); - response.getAllowedMethods().add(DELETE); - } - } - - /** - * Indicates whether the given file is located inside the root directory. - * - * @param directory The root directory - * @param file The file. - * @return True if the path is located under the root directory, false - * otherwise. - */ - private static boolean isFileInDirectory(final Directory directory, final File file) { - boolean result = true; - - if (directory != null) { - final String fileAbsolute = directory.getRootRef().getPath(true); - final String filePath; - - if (fileAbsolute.indexOf(':') == 2 || fileAbsolute.indexOf('|') == 2) { - filePath = fileAbsolute.substring(1); - } else { - filePath = fileAbsolute; - } - - final Path rootDirectoryPath = Paths.get(filePath).normalize(); - final Path actualFilePath = file.toPath().normalize(); - result = !rootDirectoryPath.relativize(actualFilePath).toString().startsWith(".."); - } - - return result; - } - - /** - * Handles a DELETE call for the FILE protocol. - * - * @param response The response to update. - * @param file The file or directory to delete. - */ - protected void handleFileDelete(Response response, File file) { - if (file.isDirectory()) { - final File[] files = file.listFiles(); - if (files == null || files.length == 0) { - if (IoUtils.delete(file)) { - response.setStatus(SUCCESS_NO_CONTENT); - } else { - response.setStatus(SERVER_ERROR_INTERNAL, "Couldn't delete the directory"); - } - } else { - response.setStatus(CLIENT_ERROR_FORBIDDEN, "Couldn't delete the non-empty directory"); - } - } else { - if (IoUtils.delete(file)) { - response.setStatus(SUCCESS_NO_CONTENT); - } else { - response.setStatus(SERVER_ERROR_INTERNAL, "Couldn't delete the file"); - } - } - } - - /** - * Handles a PUT call for the FILE protocol. - * - * @param request The request to update. - * @param response The response to update. - * @param path The encoded path of the requested file or directory. - * @param file The requested file or directory. - */ - protected void handleFilePut(Request request, Response response, String path, File file) { - response.setStatus(doHandleFilePut(request, path, file)); - } - - private Status doHandleFilePut(Request request, String path, File file) { - // Handle directory - if (file.exists()) { - if (file.isDirectory()) { - return new Status(CLIENT_ERROR_FORBIDDEN, "Can't put a new representation of a directory"); - } - } else if (path.endsWith("/")) { - // It seems the request targets a directory - // Create a new directory and its parents if necessary - if (file.mkdirs()) { - return SUCCESS_NO_CONTENT; - } else { - getLogger().warning("Unable to create the new directory"); - return new Status(SERVER_ERROR_INTERNAL, "Unable to create the new directory"); - } - } - - // Several checks : first the consistency of the metadata and the filename - if (!checkMetadataConsistency(file.getName(), request.getEntity())) { - // Ask the client to reiterate properly its request - return new Status(CLIENT_ERROR_BAD_REQUEST, "The metadata are not consistent with the URI"); - } - - // We look for the possible variants - // Set up base name as the longest part of the name without known extensions - // (beginning from the left) - final String baseName = Entity.getBaseName(file.getName(), getMetadataService()); - - // Look for resources with the same base name - FileFilter filter = new FileFilter() { - public boolean accept(File file) { - return file.isFile() && baseName.equals(Entity.getBaseName(file.getName(), getMetadataService())); - } - }; - - File[] files = file.getParentFile().listFiles(filter); - File uniqueVariant = null; - List variantsList = new ArrayList(); - - if (files != null && files.length > 0) { - // Set the list of extensions, due to the file name and the - // default metadata. - // TODO It seems we could handle more clearly the equivalence - // between the file name space and the target resource (URI - // completed by default metadata) - Variant variant = new Variant(); - Entity.updateMetadata(file.getName(), variant, false, getMetadataService()); - Collection extensions = Entity.getExtensions(variant, getMetadataService()); - - for (File entry : files) { - Collection entryExtensions = Entity.getExtensions(entry.getName(), getMetadataService()); - - if (entryExtensions.containsAll(extensions)) { - variantsList.add(entry); - - if (extensions.containsAll(entryExtensions)) { - // The right representation has been found. - uniqueVariant = entry; - } - } - } - } - - if (uniqueVariant != null) { - file = uniqueVariant; - } else { - if (!variantsList.isEmpty()) { - // Negotiated resource (several variants, but not the right one). - // Check if the request could be completed or not. - // The request could be more precise - return new Status(CLIENT_ERROR_NOT_ACCEPTABLE, - "Unable to process properly the request. Several variants exist but none of them suits precisely."); - } - - // This resource does not exist, yet. Complete it with the - // default metadata - Entity.updateMetadata(file.getName(), request.getEntity(), true, getMetadataService()); - - // Update the URI - StringBuilder fileName = new StringBuilder(baseName); - - for (Language language : request.getEntity().getLanguages()) { - updateFileExtension(fileName, language); - } - - for (Encoding encoding : request.getEntity().getEncodings()) { - updateFileExtension(fileName, encoding); - } - - // It is important to finish with the media type as it is - // often leveraged by operating systems to detect file type - updateFileExtension(fileName, request.getEntity().getMediaType()); - - file = new File(file.getParentFile(), fileName.toString()); - } - - // Before putting the file representation, we check that all the extensions are - // known - if (!checkExtensionsConsistency(file)) { - return new Status(SERVER_ERROR_INTERNAL, - "Unable to process properly the URI. At least one extension is not known by the server."); - } - - // This helper supports only single "bytes" range. - Range range = (!request.getRanges().isEmpty() && isBytesRange(request.getRanges().get(0))) - ? request.getRanges().get(0) - : null; - - if (file.exists()) { - // The PUT call is handled in two phases: - // 1- write a temporary file - // 2- rename the target file - if (range != null) { - return updateFileWithPartialContent(request, file, range); - } - return replaceFile(request, file); - } else { - // The file does not exist yet. - File parent = file.getParentFile(); - - if ((parent != null) && !parent.exists()) { - // Create the parent directories then the new file - if (!parent.mkdirs()) { - String message = "Unable to create the parent directory"; - getLogger().warning(message); - return new Status(SERVER_ERROR_INTERNAL, message); - } - } - - // Create the new file - if (range != null) { - return createFileWithPartialContent(request, file, range); - } - return createFile(request, file); - } - } - - private Status updateFileWithPartialContent(Request request, File file, Range range) { - File tmp = null; - - // Replace the content of the file. First, create a temporary file - try { - // The temporary file used for partial PUT. - tmp = new File(file.getCanonicalPath() + "." + getTemporaryExtension()); - - cleanTemporaryFileIfUploadNotResumed(tmp); - - if (!tmp.exists()) { - // Copy the target file. - Files.copy(file.toPath(), tmp.toPath()); - } - - try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { - // Go to the desired offset. - if (range.getIndex() == Range.INDEX_LAST) { - if (raf.length() <= range.getSize()) { - raf.seek(range.getSize()); - } else { - raf.seek(raf.length() - range.getSize()); - } - } else { - raf.seek(range.getIndex()); - } - - // Write the entity to the temporary file. - if (request.isEntityAvailable()) { - IoUtils.copy(request.getEntity().getStream(), raf); - } - } catch (IOException ioe) { - getLogger().log(WARNING, "Unable to close the temporary file", ioe); - cleanTemporaryFileIfUploadNotResumed(tmp); - return new Status(SERVER_ERROR_INTERNAL, ioe); - } - - return replaceFileByTemporaryFile(request, file, tmp); - } catch (IOException ioe) { - getLogger().log(WARNING, "Unable to create the temporary file", ioe); - if (tmp != null) { - cleanTemporaryFileIfUploadNotResumed(tmp); - } - return new Status(SERVER_ERROR_INTERNAL, "Unable to create a temporary file"); - } - } - - private Status replaceFile(Request request, File file) { - File tmp = null; - try { - tmp = File.createTempFile("restlet-upload", "bin"); - if (request.isEntityAvailable()) { - Files.copy(request.getEntity().getStream(), tmp.toPath(), REPLACE_EXISTING); - } - } catch (IOException ioe) { - getLogger().log(WARNING, "Unable to create the temporary file", ioe); - cleanTemporaryFileIfUploadNotResumed(tmp); - return new Status(SERVER_ERROR_INTERNAL, "Unable to create a temporary file"); - } - return replaceFileByTemporaryFile(request, file, tmp); - } - - private Status replaceFileByTemporaryFile(Request request, File file, File tmp) { - if (!tmp.exists()) { - return new Status(SERVER_ERROR_INTERNAL, "Can't replace the existing file without new content."); - } - - // Then delete the existing file - if (!IoUtils.delete(file)) { - cleanTemporaryFileIfUploadNotResumed(tmp); - return new Status(SERVER_ERROR_INTERNAL, "Unable to delete the existing file"); - } - - // Finally move the temporary file to the existing file location - if (tmp.renameTo(file)) { - if (request.isEntityAvailable()) { - return SUCCESS_NO_CONTENT; - } - return SUCCESS_OK; - } - - // Many aspects of the behavior of the method "renameTo" are inherently - // platform-dependent. - // The rename operation might not be able to move a file from one file system to - // another. - if (!tmp.exists()) { - return new Status(SERVER_ERROR_INTERNAL, "Unable to move the temporary file to replace the existing file"); - } - try { - Files.move(tmp.toPath(), file.toPath(), REPLACE_EXISTING); - } catch (IOException e) { - return new Status(SERVER_ERROR_INTERNAL, e, - "Unable to move the temporary file to replace the existing file"); - } - return SUCCESS_OK; - } - - private Status createFileWithPartialContent(Request request, File file, Range range) { - // This is a partial PUT - try (RandomAccessFile raf = new RandomAccessFile(file, "rwd")) { - // Go to the desired offset. - if (range.getIndex() == Range.INDEX_LAST) { - if (raf.length() <= range.getSize()) { - raf.seek(range.getSize()); - } else { - raf.seek(raf.length() - range.getSize()); - } - } else { - raf.seek(range.getIndex()); - } - // Write the entity to the file. - if (request.isEntityAvailable()) { - IoUtils.copy(request.getEntity().getStream(), raf); - return SUCCESS_CREATED; - } - return SUCCESS_NO_CONTENT; - } catch (IOException ioe) { - getLogger().log(WARNING, "Unable to create the new file", ioe); - return new Status(SERVER_ERROR_INTERNAL, ioe); - } - } - - private Status createFile(Request request, File file) { - // This is simple PUT of the full entity - try { - if (request.isEntityAvailable()) { - Files.copy(request.getEntity().getStream(), file.toPath()); - return SUCCESS_CREATED; - } - if (file.createNewFile()) { - return SUCCESS_NO_CONTENT; - } - } catch (IOException ioe) { - getLogger().log(WARNING, "Unable to create the new file", ioe); - return new Status(SERVER_ERROR_INTERNAL, ioe); - } - - String message = "Unable to create the new file"; - getLogger().warning(message); - return new Status(SERVER_ERROR_INTERNAL, message); - } - - private void cleanTemporaryFileIfUploadNotResumed(File tmp) { - if (tmp.exists() && !isResumeUpload()) { - IoUtils.delete(tmp); - } - } - - /** - * Indicates if a failed upload can be resumed. This will prevent the deletion - * of the temporary file created. Defaults to "false". - * - * @return True if a failed upload can be resumed, false otherwise. - */ - public boolean isResumeUpload() { - return Boolean.parseBoolean(getHelpedParameters().getFirstValue("resumeUpload", "false")); - } - - /** - * Complete the given file name with the extension corresponding to the given - * metadata. - * - * @param fileName The file name to complete. - * @param metadata The metadata. - */ - private void updateFileExtension(StringBuilder fileName, Metadata metadata) { - boolean defaultMetadata = true; - - if (getMetadataService() != null) { - if (metadata instanceof Language language) { - defaultMetadata = language.equals(getMetadataService().getDefaultLanguage()); - } else if (metadata instanceof MediaType mediaType) { - defaultMetadata = mediaType.equals(getMetadataService().getDefaultMediaType()); - } else if (metadata instanceof CharacterSet characterSet) { - defaultMetadata = characterSet.equals(getMetadataService().getDefaultCharacterSet()); - } else if (metadata instanceof Encoding encoding) { - defaultMetadata = encoding.equals(getMetadataService().getDefaultEncoding()); - } - } - - // We only add an extension for metadata that differs from default ones - if (!defaultMetadata) { - String extension = getMetadataService().getExtension(metadata); - - if (extension != null) { - fileName.append(".").append(extension); - } else { - if (metadata.getParent() != null) { - updateFileExtension(fileName, metadata.getParent()); - } - } - } - } + /** + * Constructor. + * + * @param client The client to help. + */ + public FileClientHelper(Client client) { + super(client); + getProtocols().add(FILE); + } + + /** + * Check that all extensions of the file correspond to a known metadata. + * + * @param file The file whose extensions are checked. + * @return True if the metadata service knows all extensions of the file. + */ + protected boolean checkExtensionsConsistency(File file) { + boolean knownExtension = true; + + Collection set = Entity.getExtensions(file.getName(), getMetadataService()); + Iterator iterator = set.iterator(); + while (iterator.hasNext() && knownExtension) { + knownExtension = getMetadataService().getMetadata(iterator.next()) != null; + } + + return knownExtension; + } + + /** + * Checks that the URI and the representation are compatible. The whole set of metadata of the + * representation must be included in the set of those of the URI + * + * @param fileName The name of the resource + * @param representation The provided representation. + * @return True if the metadata of the representation is compatible with the metadata extracted + * from the filename + */ + private boolean checkMetadataConsistency(String fileName, Representation representation) { + if (representation != null) { + Variant var = new Variant(); + Entity.updateMetadata(fileName, var, false, getMetadataService()); + + // "var" contains the theoretical correct metadata + if (!var.getLanguages().isEmpty() + && !representation.getLanguages().isEmpty() + && !new HashSet<>(var.getLanguages()) + .containsAll(representation.getLanguages())) { + return false; + } + + if ((var.getMediaType() != null) + && (representation.getMediaType() != null) + && !(var.getMediaType().includes(representation.getMediaType()))) { + return false; + } + + return var.getEncodings().isEmpty() + || representation.getEncodings().isEmpty() + || new HashSet<>(var.getEncodings()).containsAll(representation.getEncodings()); + } + return true; + } + + @Override + public Entity getEntity(String decodedPath) { + return new FileEntity(getFileWithLocalizedPath(decodedPath), getMetadataService()); + } + + /** + * Returns a new {@link File} instance for the given path name.
+ * It ensures to translate the "/" to the local supported file separator (mainly useful for + * Windows OS). + * + * @param path The Path of the file + * @return a new {@link File} instance for the given path name. + */ + private static File getFileWithLocalizedPath(final String path) { + return new File(LocalReference.localizePath(path)); + } + + /** + * Returns the name of the extension to use to store the temporary content while uploading + * content via the PUT method. Defaults to "tmp". + * + * @return The name of the extension to use to store the temporary content. + */ + public String getTemporaryExtension() { + return getHelpedParameters().getFirstValue("temporaryExtension", "tmp"); + } + + @Override + protected void handleLocal(Request request, Response response, String decodedPath) { + String scheme = request.getResourceRef().getScheme(); + + if (!FILE.getSchemeName().equalsIgnoreCase(scheme)) { + throw new IllegalArgumentException( + format( + "Protocol \"%s\" not supported by the connector. Only FILE is supported.", + scheme)); + } + + handleFile(request, response, decodedPath); + } + + protected void handleFile(Request request, Response response, String decodedPath) { + final Directory directory = + (Directory) request.getAttributes().get("org.restlet.directory"); + final File fileWithLocalizedPath = getFileWithLocalizedPath(decodedPath); + + if (!isFileInDirectory(directory, fileWithLocalizedPath)) { + response.setStatus(CLIENT_ERROR_FORBIDDEN); + } else if (GET.equals(request.getMethod()) || HEAD.equals(request.getMethod())) { + handleEntityGet(request, response, getEntity(decodedPath)); + } else if (PUT.equals(request.getMethod())) { + handleFilePut(request, response, decodedPath, fileWithLocalizedPath); + } else if (DELETE.equals(request.getMethod())) { + handleFileDelete(response, fileWithLocalizedPath); + } else { + response.setStatus(CLIENT_ERROR_METHOD_NOT_ALLOWED); + response.getAllowedMethods().add(GET); + response.getAllowedMethods().add(HEAD); + response.getAllowedMethods().add(PUT); + response.getAllowedMethods().add(DELETE); + } + } + + /** + * Indicates whether the given file is located inside the root directory. + * + * @param directory The root directory + * @param file The file. + * @return True if the path is located under the root directory, false otherwise. + */ + private static boolean isFileInDirectory(final Directory directory, final File file) { + boolean result = true; + + if (directory != null) { + final String fileAbsolute = directory.getRootRef().getPath(true); + final String filePath; + + if (fileAbsolute.indexOf(':') == 2 || fileAbsolute.indexOf('|') == 2) { + filePath = fileAbsolute.substring(1); + } else { + filePath = fileAbsolute; + } + + final Path rootDirectoryPath = Paths.get(filePath).normalize(); + final Path actualFilePath = file.toPath().normalize(); + result = !rootDirectoryPath.relativize(actualFilePath).toString().startsWith(".."); + } + + return result; + } + + /** + * Handles a DELETE call for the FILE protocol. + * + * @param response The response to update. + * @param file The file or directory to delete. + */ + protected void handleFileDelete(Response response, File file) { + if (file.isDirectory()) { + final File[] files = file.listFiles(); + if (files == null || files.length == 0) { + if (IoUtils.delete(file)) { + response.setStatus(SUCCESS_NO_CONTENT); + } else { + response.setStatus(SERVER_ERROR_INTERNAL, "Couldn't delete the directory"); + } + } else { + response.setStatus( + CLIENT_ERROR_FORBIDDEN, "Couldn't delete the non-empty directory"); + } + } else { + if (IoUtils.delete(file)) { + response.setStatus(SUCCESS_NO_CONTENT); + } else { + response.setStatus(SERVER_ERROR_INTERNAL, "Couldn't delete the file"); + } + } + } + + /** + * Handles a PUT call for the FILE protocol. + * + * @param request The request to update. + * @param response The response to update. + * @param path The encoded path of the requested file or directory. + * @param file The requested file or directory. + */ + protected void handleFilePut(Request request, Response response, String path, File file) { + response.setStatus(doHandleFilePut(request, path, file)); + } + + private Status doHandleFilePut(Request request, String path, File file) { + // Handle directory + if (file.exists()) { + if (file.isDirectory()) { + return new Status( + CLIENT_ERROR_FORBIDDEN, "Can't put a new representation of a directory"); + } + } else if (path.endsWith("/")) { + // It seems the request targets a directory + // Create a new directory and its parents if necessary + if (file.mkdirs()) { + return SUCCESS_NO_CONTENT; + } else { + getLogger().warning("Unable to create the new directory"); + return new Status(SERVER_ERROR_INTERNAL, "Unable to create the new directory"); + } + } + + // Several checks: first the consistency of the metadata and the filename + if (!checkMetadataConsistency(file.getName(), request.getEntity())) { + // Ask the client to properly reiterate its request + return new Status( + CLIENT_ERROR_BAD_REQUEST, "The metadata is not consistent with the URI"); + } + + // We look for the possible variants + // Set up base name as the longest part of the name without known extensions + // (beginning from the left) + final String baseName = Entity.getBaseName(file.getName(), getMetadataService()); + + // Look for resources with the same base name + FileFilter filter = + file1 -> + file1.isFile() + && baseName.equals( + Entity.getBaseName(file1.getName(), getMetadataService())); + + File[] files = file.getParentFile().listFiles(filter); + File uniqueVariant = null; + List variantsList = new ArrayList(); + + if (files != null && files.length > 0) { + // Set the list of extensions, due to the file name and the + // default metadata. + // TODO It seems we could handle more clearly the equivalence + // between the file name space and the target resource (URI + // completed by default metadata) + Variant variant = new Variant(); + Entity.updateMetadata(file.getName(), variant, false, getMetadataService()); + Collection extensions = Entity.getExtensions(variant, getMetadataService()); + + for (File entry : files) { + Collection entryExtensions = + Entity.getExtensions(entry.getName(), getMetadataService()); + + if (entryExtensions.containsAll(extensions)) { + variantsList.add(entry); + + if (extensions.containsAll(entryExtensions)) { + // The right representation has been found. + uniqueVariant = entry; + } + } + } + } + + if (uniqueVariant != null) { + file = uniqueVariant; + } else { + if (!variantsList.isEmpty()) { + // Negotiated resource (several variants, but not the right one). + // Check if the request could be completed or not. + // The request could be more precise + return new Status( + CLIENT_ERROR_NOT_ACCEPTABLE, + "Unable to process properly the request. Several variants exist but none of them suits precisely."); + } + + // This resource does not exist, yet. Complete it with the + // default metadata + Entity.updateMetadata(file.getName(), request.getEntity(), true, getMetadataService()); + + // Update the URI + StringBuilder fileName = new StringBuilder(baseName); + + for (Language language : request.getEntity().getLanguages()) { + updateFileExtension(fileName, language); + } + + for (Encoding encoding : request.getEntity().getEncodings()) { + updateFileExtension(fileName, encoding); + } + + // It is important to finish with the media type as it is + // often leveraged by operating systems to detect a file type + updateFileExtension(fileName, request.getEntity().getMediaType()); + + file = new File(file.getParentFile(), fileName.toString()); + } + + // Before putting the file representation, we check that all the extensions are + // known + if (!checkExtensionsConsistency(file)) { + return new Status( + SERVER_ERROR_INTERNAL, + "Unable to process properly the URI. At least one extension is not known by the server."); + } + + // This helper supports only a single "bytes" range. + Range range = + (!request.getRanges().isEmpty() && isBytesRange(request.getRanges().getFirst())) + ? request.getRanges().getFirst() + : null; + + if (file.exists()) { + // The PUT call is handled in two phases: + // 1- write a temporary file + // 2- rename the target file + if (range != null) { + return updateFileWithPartialContent(request, file, range); + } + return replaceFile(request, file); + } else { + // The file does not exist yet. + File parent = file.getParentFile(); + + if ((parent != null) && !parent.exists()) { + // Create the parent directories then the new file + if (!parent.mkdirs()) { + String message = "Unable to create the parent directory"; + getLogger().warning(message); + return new Status(SERVER_ERROR_INTERNAL, message); + } + } + + // Create the new file + if (range != null) { + return createFileWithPartialContent(request, file, range); + } + return createFile(request, file); + } + } + + private Status updateFileWithPartialContent(Request request, File file, Range range) { + File tmp = null; + + // Replace the content of the file. First, create a temporary file + try { + // The temporary file used for partial PUT. + tmp = new File(file.getCanonicalPath() + "." + getTemporaryExtension()); + + cleanTemporaryFileIfUploadNotResumed(tmp); + + if (!tmp.exists()) { + // Copy the target file. + Files.copy(file.toPath(), tmp.toPath()); + } + + try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { + // Go to the desired offset. + if (range.getIndex() == Range.INDEX_LAST) { + if (raf.length() <= range.getSize()) { + raf.seek(range.getSize()); + } else { + raf.seek(raf.length() - range.getSize()); + } + } else { + raf.seek(range.getIndex()); + } + + // Write the entity to the temporary file. + if (request.isEntityAvailable()) { + IoUtils.copy(request.getEntity().getStream(), raf); + } + } catch (IOException ioe) { + getLogger().log(WARNING, "Unable to close the temporary file", ioe); + cleanTemporaryFileIfUploadNotResumed(tmp); + return new Status(SERVER_ERROR_INTERNAL, ioe); + } + + return replaceFileByTemporaryFile(request, file, tmp); + } catch (IOException ioe) { + getLogger().log(WARNING, "Unable to create the temporary file", ioe); + if (tmp != null) { + cleanTemporaryFileIfUploadNotResumed(tmp); + } + return new Status(SERVER_ERROR_INTERNAL, "Unable to create a temporary file"); + } + } + + private Status replaceFile(Request request, File file) { + File tmp = null; + try { + tmp = File.createTempFile("restlet-upload", "bin"); + if (request.isEntityAvailable()) { + Files.copy(request.getEntity().getStream(), tmp.toPath(), REPLACE_EXISTING); + } + } catch (IOException ioe) { + getLogger().log(WARNING, "Unable to create the temporary file", ioe); + cleanTemporaryFileIfUploadNotResumed(tmp); + return new Status(SERVER_ERROR_INTERNAL, "Unable to create a temporary file"); + } + return replaceFileByTemporaryFile(request, file, tmp); + } + + private Status replaceFileByTemporaryFile(Request request, File file, File tmp) { + if (!tmp.exists()) { + return new Status( + SERVER_ERROR_INTERNAL, "Can't replace the existing file without new content."); + } + + // Then delete the existing file + if (!IoUtils.delete(file)) { + cleanTemporaryFileIfUploadNotResumed(tmp); + return new Status(SERVER_ERROR_INTERNAL, "Unable to delete the existing file"); + } + + // Finally move the temporary file to the existing file location + if (tmp.renameTo(file)) { + if (request.isEntityAvailable()) { + return SUCCESS_NO_CONTENT; + } + return SUCCESS_OK; + } + + // Many aspects of the behavior of the method "renameTo" are inherently platform-dependent. + // The rename operation might not be able to move a file from one file system to another. + if (!tmp.exists()) { + return new Status( + SERVER_ERROR_INTERNAL, + "Unable to move the temporary file to replace the existing file"); + } + try { + Files.move(tmp.toPath(), file.toPath(), REPLACE_EXISTING); + } catch (IOException e) { + return new Status( + SERVER_ERROR_INTERNAL, + e, + "Unable to move the temporary file to replace the existing file"); + } + return SUCCESS_OK; + } + + private Status createFileWithPartialContent(Request request, File file, Range range) { + // This is a partial PUT + try (RandomAccessFile raf = new RandomAccessFile(file, "rwd")) { + // Go to the desired offset. + if (range.getIndex() == Range.INDEX_LAST) { + if (raf.length() <= range.getSize()) { + raf.seek(range.getSize()); + } else { + raf.seek(raf.length() - range.getSize()); + } + } else { + raf.seek(range.getIndex()); + } + // Write the entity to the file. + if (request.isEntityAvailable()) { + IoUtils.copy(request.getEntity().getStream(), raf); + return SUCCESS_CREATED; + } + return SUCCESS_NO_CONTENT; + } catch (IOException ioe) { + getLogger().log(WARNING, "Unable to create the new file", ioe); + return new Status(SERVER_ERROR_INTERNAL, ioe); + } + } + + private Status createFile(Request request, File file) { + // This is a simple PUT of the full entity + try { + if (request.isEntityAvailable()) { + Files.copy(request.getEntity().getStream(), file.toPath()); + return SUCCESS_CREATED; + } + if (file.createNewFile()) { + return SUCCESS_NO_CONTENT; + } + } catch (IOException ioe) { + getLogger().log(WARNING, "Unable to create the new file", ioe); + return new Status(SERVER_ERROR_INTERNAL, ioe); + } + + String message = "Unable to create the new file"; + getLogger().warning(message); + return new Status(SERVER_ERROR_INTERNAL, message); + } + + private void cleanTemporaryFileIfUploadNotResumed(File tmp) { + if (tmp.exists() && !isResumeUpload()) { + IoUtils.delete(tmp); + } + } + + /** + * Indicates if a failed upload can be resumed. This will prevent the deletion of the temporary + * file created. Defaults to "false". + * + * @return True if a failed upload can be resumed, false otherwise. + */ + public boolean isResumeUpload() { + return Boolean.parseBoolean(getHelpedParameters().getFirstValue("resumeUpload", "false")); + } + + /** + * Complete the given file name with the extension corresponding to the given metadata. + * + * @param fileName The file name to complete. + * @param metadata The metadata. + */ + private void updateFileExtension(StringBuilder fileName, Metadata metadata) { + boolean defaultMetadata = true; + + if (getMetadataService() != null) { + if (metadata instanceof Language language) { + defaultMetadata = language.equals(getMetadataService().getDefaultLanguage()); + } else if (metadata instanceof MediaType mediaType) { + defaultMetadata = mediaType.equals(getMetadataService().getDefaultMediaType()); + } else if (metadata instanceof CharacterSet characterSet) { + defaultMetadata = + characterSet.equals(getMetadataService().getDefaultCharacterSet()); + } else if (metadata instanceof Encoding encoding) { + defaultMetadata = encoding.equals(getMetadataService().getDefaultEncoding()); + } + } + + // We only add an extension for metadata that differs from default ones + if (!defaultMetadata) { + String extension = getMetadataService().getExtension(metadata); + + if (extension != null) { + fileName.append(".").append(extension); + } else { + if (metadata.getParent() != null) { + updateFileExtension(fileName, metadata.getParent()); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java b/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java index 43390c1f1c..ac3a2b6d72 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java @@ -1,94 +1,91 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import org.restlet.data.MediaType; import org.restlet.representation.FileRepresentation; import org.restlet.representation.Representation; import org.restlet.service.MetadataService; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * Local entity based on a regular {@link File}. - */ +/** Local entity based on a regular {@link File}. */ public class FileEntity extends Entity { - /** The underlying regular file. */ - private final File file; - - /** - * Constructor. - * - * @param file The underlying file. - * @param metadataService The metadata service to use. - */ - public FileEntity(File file, MetadataService metadataService) { - super(metadataService); - this.file = file; - } - - @Override - public boolean exists() { - return getFile().exists(); - } - - @Override - public List getChildren() { - List result = null; - - if (isDirectory()) { - result = new ArrayList(); - - for (File f : getFile().listFiles()) { - result.add(new FileEntity(f, getMetadataService())); - } - } - - return result; - } - - /** - * Returns the underlying regular file. - * - * @return The underlying regular file. - */ - public File getFile() { - return file; - } - - @Override - public String getName() { - return getFile().getName(); - } - - @Override - public Entity getParent() { - File parentFile = getFile().getParentFile(); - return (parentFile == null) ? null : new FileEntity(parentFile, getMetadataService()); - } - - @Override - public Representation getRepresentation(MediaType defaultMediaType, int timeToLive) { - return new FileRepresentation(getFile(), defaultMediaType, timeToLive); - } - - @Override - public boolean isDirectory() { - return getFile().isDirectory(); - } - - @Override - public boolean isNormal() { - return getFile().isFile(); - } + /** The underlying regular file. */ + private final File file; + + /** + * Constructor. + * + * @param file The underlying file. + * @param metadataService The metadata service to use. + */ + public FileEntity(File file, MetadataService metadataService) { + super(metadataService); + this.file = file; + } + + @Override + public boolean exists() { + return getFile().exists(); + } + + @Override + public List getChildren() { + List result = null; + + if (isDirectory()) { + result = new ArrayList<>(); + + for (File f : Objects.requireNonNull(getFile().listFiles())) { + result.add(new FileEntity(f, getMetadataService())); + } + } + + return result; + } + + /** + * Returns the underlying regular file. + * + * @return The underlying regular file. + */ + public File getFile() { + return file; + } + + @Override + public String getName() { + return getFile().getName(); + } + + @Override + public Entity getParent() { + File parentFile = getFile().getParentFile(); + return (parentFile == null) ? null : new FileEntity(parentFile, getMetadataService()); + } + + @Override + public Representation getRepresentation(MediaType defaultMediaType, int timeToLive) { + return new FileRepresentation(getFile(), defaultMediaType, timeToLive); + } + + @Override + public boolean isDirectory() { + return getFile().isDirectory(); + } + + @Override + public boolean isNormal() { + return getFile().isFile(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java index bf78dfc8d8..5be8249255 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; import org.restlet.Client; @@ -16,9 +15,10 @@ import org.restlet.engine.connector.ClientHelper; /** - * Connector to the local resources accessible via file system, class loaders - * and similar mechanisms. Here is the list of parameters that are supported. - * They should be set in the Client's context before it is started: + * Connector to the local resources accessible via file system, class loaders, and similar + * mechanisms. Here is the list of parameters that are supported. They should be set in the Client's + * context before it is started: + * * * * @@ -43,76 +43,76 @@ * default language should be set, "" can be used. * *
list of supported parameters
- * + * * @see org.restlet.data.LocalReference * @author Jerome Louvel * @author Thierry Boileau */ public abstract class LocalClientHelper extends ClientHelper { - /** - * Constructor. Note that the common list of metadata associations based on - * extensions is added, see the addCommonExtensions() method. - * - * @param client The client to help. - */ - public LocalClientHelper(Client client) { - super(client); - } + /** + * Constructor. Note that the common list of metadata associations based on extensions is added, + * see the addCommonExtensions() method. + * + * @param client The client to help. + */ + public LocalClientHelper(Client client) { + super(client); + } - /** - * Returns the default language. When no metadata service is available (simple - * client connector with no parent application), falls back on this default - * language. - * - * @return The default language. - */ - public String getDefaultLanguage() { - return getHelpedParameters().getFirstValue("defaultLanguage", ""); - } + /** + * Returns the default language. When no metadata service is available (simple client connector + * with no parent application), it falls back on this default language. + * + * @return The default language. + */ + public String getDefaultLanguage() { + return getHelpedParameters().getFirstValue("defaultLanguage", ""); + } - /** - * Returns the time to live for a file representation before it expires (in - * seconds). - * - * @return The time to live for a file representation before it expires (in - * seconds). - */ - public int getTimeToLive() { - return Integer.parseInt(getHelpedParameters().getFirstValue("timeToLive", "600")); - } + /** + * Returns the time to live for a file representation before it expires (in seconds). + * + * @return The time to live for a file representation before it expires (in seconds). + */ + public int getTimeToLive() { + return Integer.parseInt(getHelpedParameters().getFirstValue("timeToLive", "600")); + } - /** - * Handles a call. Note that this implementation will systematically normalize - * and URI-decode the resource reference. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public final void handle(Request request, Response response) { - // Ensure that all ".." and "." are normalized into the path - // to prevent unauthorized access to user directories. - request.getResourceRef().normalize(); + /** + * Handles a call. Note that this implementation will systematically normalize and URI-decode + * the resource reference. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public final void handle(Request request, Response response) { + // Ensure that all ".." and "." are normalized into the path + // to prevent unauthorized access to user directories. + request.getResourceRef().normalize(); - // As the path may be percent-encoded, it has to be percent-decoded. - // Then, all generated URIs must be encoded. - String path = request.getResourceRef().getPath(); - String decodedPath = Reference.decode(path); + // As the path may be percent-encoded, it has to be percent-decoded. + // Then, all generated URIs must be encoded. + String path = request.getResourceRef().getPath(); + String decodedPath = Reference.decode(path); - if (decodedPath != null) { - // Continue the local handling - handleLocal(request, response, decodedPath); - } else { - getLogger().warning("Unable to get the path of this local URI: " + request.getResourceRef()); - } - } + if (decodedPath != null) { + // Continue the local handling + handleLocal(request, response, decodedPath); + } else { + getLogger() + .warning( + "Unable to get the path of this local URI: " + + request.getResourceRef()); + } + } - /** - * Handles a local call. - * - * @param request The request to handle. - * @param response The response to update. - * @param decodedPath The decoded local path. - */ - protected abstract void handleLocal(Request request, Response response, String decodedPath); + /** + * Handles a local call. + * + * @param request The request to handle. + * @param response The response to update. + * @param decodedPath The decoded local path. + */ + protected abstract void handleLocal(Request request, Response response, String decodedPath); } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java index e5668a6349..5c2b476b1f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; import org.restlet.Client; @@ -18,51 +17,58 @@ /** * Client connector for RIAP calls. Only the "component" authority is supported. - * + * * @author Thierry Boileau * @see Protocol#RIAP */ public class RiapClientHelper extends ClientHelper { - /** - * Constructor. - * - * @param client The client to help. - */ - public RiapClientHelper(Client client) { - super(client); - getProtocols().add(Protocol.RIAP); - } - - /** - * Handles a call. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public void handle(Request request, Response response) { - final String scheme = request.getResourceRef().getScheme(); + /** + * Constructor. + * + * @param client The client to help. + */ + public RiapClientHelper(Client client) { + super(client); + getProtocols().add(Protocol.RIAP); + } - if (Protocol.RIAP.getSchemeName().equalsIgnoreCase(scheme)) { - // Support only the "component" authority - LocalReference ref = new LocalReference(request.getResourceRef()); + /** + * Handles a call. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public void handle(Request request, Response response) { + final String scheme = request.getResourceRef().getScheme(); - if (ref.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { - if (RiapServerHelper.instance != null && RiapServerHelper.instance.getContext() != null - && RiapServerHelper.instance.getContext().getClientDispatcher() != null) { - RiapServerHelper.instance.getContext().getClientDispatcher().handle(request, response); - } else { - super.handle(request, response); - } - } else { - throw new IllegalArgumentException("Authority \"" + ref.getAuthority() - + "\" not supported by the connector. Only \"component\" is supported."); - } - } else { - throw new IllegalArgumentException( - "Protocol \"" + scheme + "\" not supported by the connector. Only RIAP is supported."); - } + if (Protocol.RIAP.getSchemeName().equalsIgnoreCase(scheme)) { + // Support only the "component" authority + LocalReference ref = new LocalReference(request.getResourceRef()); - } + if (ref.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { + if (RiapServerHelper.instance != null + && RiapServerHelper.instance.getContext() != null + && RiapServerHelper.instance.getContext().getClientDispatcher() != null) { + RiapServerHelper.instance + .getContext() + .getClientDispatcher() + .handle(request, response); + } else { + super.handle(request, response); + } + } else { + throw new IllegalArgumentException( + "Authority \"" + + ref.getAuthority() + + "\" not supported by the connector. Only \"component\" is supported."); + } + } else { + throw new IllegalArgumentException( + "Protocol \"" + + scheme + + "\" not supported by the connector. Only RIAP is supported."); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java index cd34b0046f..3f0a059e06 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; import org.restlet.Server; @@ -14,32 +13,31 @@ import org.restlet.engine.connector.ServerHelper; /** - * Server connector handling RIAP calls. By design, there is only one instance - * by JVM. - * + * Server connector handling RIAP calls. By design, there is only one instance by JVM. + * * @author Thierry Boileau */ public class RiapServerHelper extends ServerHelper { - /** The unique registered helper. */ - public static RiapServerHelper instance = null; + /** The unique registered helper. */ + public static RiapServerHelper instance = null; - /** - * Constructor. - * - * @param server The server to help. - */ - public RiapServerHelper(Server server) { - super(server); - getProtocols().add(Protocol.RIAP); + /** + * Constructor. + * + * @param server The server to help. + */ + public RiapServerHelper(Server server) { + super(server); + getProtocols().add(Protocol.RIAP); - // Lazy initialization with double-check. - if (server != null && RiapServerHelper.instance == null) { - synchronized (this.getClass()) { - if (RiapServerHelper.instance == null) { - RiapServerHelper.instance = this; - } - } - } - } + // Lazy initialization with double-check. + if (server != null && RiapServerHelper.instance == null) { + synchronized (this.getClass()) { + if (RiapServerHelper.instance == null) { + RiapServerHelper.instance = this; + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java index da75259526..3a46c99922 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java @@ -1,319 +1,341 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; import org.restlet.Client; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; +import org.restlet.data.LocalReference; +import org.restlet.data.Method; +import org.restlet.data.Protocol; +import org.restlet.data.ReferenceList; +import org.restlet.data.Status; import org.restlet.engine.io.IoUtils; import org.restlet.representation.Representation; import org.restlet.service.MetadataService; -import java.io.*; -import java.util.Collection; -import java.util.Enumeration; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; - /** - * ZIP and JAR client connector. Only works for archives available as local - * files.
+ * ZIP and JAR client connector. Only works for archives available as local files.
*
- * Handles GET, HEAD and PUT request on resources referenced as : - * zip:file:// + * Handles GET, HEAD, and PUT request on resources referenced as: zip:file:// * * @author Remi Dewitte remi@gide.net */ public class ZipClientHelper extends LocalClientHelper { - /** - * Constructor. - * - * @param client The helped client. - */ - public ZipClientHelper(Client client) { - super(client); - getProtocols().add(Protocol.ZIP); - getProtocols().add(Protocol.JAR); - } + /** + * Constructor. + * + * @param client The helped client. + */ + public ZipClientHelper(Client client) { + super(client); + getProtocols().add(Protocol.ZIP); + getProtocols().add(Protocol.JAR); + } - /** - * Handles a call for a local entity. By default, only GET and HEAD methods are - * implemented. - * - * @param request The request to handle. - * @param response The response to update. - * @param decodedPath The URL decoded entity path. - */ - @Override - protected void handleLocal(Request request, Response response, String decodedPath) { - int spi = decodedPath.indexOf("!/"); - String fileUri; - String entryName; - if (spi != -1) { - fileUri = decodedPath.substring(0, spi); - entryName = decodedPath.substring(spi + 2); - } else { - fileUri = decodedPath; - entryName = ""; - } + /** + * Handles a call for a local entity. By default, only GET and HEAD methods are implemented. + * + * @param request The request to handle. + * @param response The response to update. + * @param decodedPath The URL decoded entity path. + */ + @Override + protected void handleLocal(Request request, Response response, String decodedPath) { + int spi = decodedPath.indexOf("!/"); + String fileUri; + String entryName; + if (spi != -1) { + fileUri = decodedPath.substring(0, spi); + entryName = decodedPath.substring(spi + 2); + } else { + fileUri = decodedPath; + entryName = ""; + } - LocalReference fileRef = new LocalReference(fileUri); - if (Protocol.FILE.equals(fileRef.getSchemeProtocol())) { - final File file = fileRef.getFile(); - if (Method.GET.equals(request.getMethod()) || Method.HEAD.equals(request.getMethod())) { - handleGet(request, response, file, entryName, getMetadataService()); - } else if (Method.PUT.equals(request.getMethod())) { - handlePut(request, response, file, entryName); - } else { - response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - response.getAllowedMethods().add(Method.GET); - response.getAllowedMethods().add(Method.HEAD); - response.getAllowedMethods().add(Method.PUT); - } - } else { - response.setStatus(Status.SERVER_ERROR_NOT_IMPLEMENTED, "Only works on local files."); - } - } + LocalReference fileRef = new LocalReference(fileUri); + if (Protocol.FILE.equals(fileRef.getSchemeProtocol())) { + final File file = fileRef.getFile(); + if (Method.GET.equals(request.getMethod()) || Method.HEAD.equals(request.getMethod())) { + handleGet(request, response, file, entryName, getMetadataService()); + } else if (Method.PUT.equals(request.getMethod())) { + handlePut(request, response, file, entryName); + } else { + response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + response.getAllowedMethods().add(Method.GET); + response.getAllowedMethods().add(Method.HEAD); + response.getAllowedMethods().add(Method.PUT); + } + } else { + response.setStatus(Status.SERVER_ERROR_NOT_IMPLEMENTED, "Only works on local files."); + } + } - /** - * Handles a GET call. - * - * @param request The request to answer. - * @param response The response to update. - * @param file The Zip archive file. - * @param entryName The Zip archive entry name. - * @param metadataService The metadata service. - */ - protected void handleGet(Request request, Response response, File file, String entryName, - final MetadataService metadataService) { + /** + * Handles a GET call. + * + * @param request The request to answer. + * @param response The response to update. + * @param file The Zip archive file. + * @param entryName The Zip archive entry name. + * @param metadataService The metadata service. + */ + protected void handleGet( + Request request, + Response response, + File file, + String entryName, + final MetadataService metadataService) { - if (!file.exists()) { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } else { - ZipFile zipFile; + if (!file.exists()) { + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } else { + ZipFile zipFile; - try { - zipFile = new ZipFile(file); - } catch (Exception e) { - response.setStatus(Status.SERVER_ERROR_INTERNAL, e); - return; - } + try { + zipFile = new ZipFile(file); + } catch (Exception e) { + response.setStatus(Status.SERVER_ERROR_INTERNAL, e); + return; + } - Entity entity = new ZipEntryEntity(zipFile, entryName, metadataService); - if (!entity.exists()) { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } else { - final Representation output; + Entity entity = new ZipEntryEntity(zipFile, entryName, metadataService); + if (!entity.exists()) { + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } else { + final Representation output; - if (entity.isDirectory()) { - // Return the directory listing - final Collection children = entity.getChildren(); - final ReferenceList rl = new ReferenceList(children.size()); - String fileUri = LocalReference.createFileReference(file).toString(); - String scheme = request.getResourceRef().getScheme(); - String baseUri = scheme + ":" + fileUri + "!/"; + if (entity.isDirectory()) { + // Return the directory listing + final Collection children = entity.getChildren(); + final ReferenceList rl = new ReferenceList(children.size()); + String fileUri = LocalReference.createFileReference(file).toString(); + String scheme = request.getResourceRef().getScheme(); + String baseUri = scheme + ":" + fileUri + "!/"; - for (final Entity entry : children) { - rl.add(baseUri + entry.getName()); - } + for (final Entity entry : children) { + rl.add(baseUri + entry.getName()); + } - output = rl.getTextRepresentation(); + output = rl.getTextRepresentation(); - try { - zipFile.close(); - } catch (IOException e) { - // Do something ??? - } - } else { - // Return the file content - output = entity.getRepresentation(metadataService.getDefaultMediaType(), getTimeToLive()); - output.setLocationRef(request.getResourceRef()); - Entity.updateMetadata(entity.getName(), output, true, getMetadataService()); - } + try { + zipFile.close(); + } catch (IOException e) { + // Do something ??? + } + } else { + // Return the file content + output = + entity.getRepresentation( + metadataService.getDefaultMediaType(), getTimeToLive()); + output.setLocationRef(request.getResourceRef()); + Entity.updateMetadata(entity.getName(), output, true, getMetadataService()); + } - response.setStatus(Status.SUCCESS_OK); - response.setEntity(output); - } - } - } + response.setStatus(Status.SUCCESS_OK); + response.setEntity(output); + } + } + } - /** - * Handles a PUT call. - * - * @param request The request to answer. - * @param response The response to update. - * @param file The Zip archive file. - * @param entryName The Zip archive entry name. - */ - protected void handlePut(Request request, Response response, File file, String entryName) { - boolean zipExists = file.exists(); - ZipOutputStream zipOut = null; + /** + * Handles a PUT call. + * + * @param request The request to answer. + * @param response The response to update. + * @param file The Zip archive file. + * @param entryName The Zip archive entry name. + */ + protected void handlePut(Request request, Response response, File file, String entryName) { + boolean zipExists = file.exists(); + ZipOutputStream zipOut = null; - if ("".equals(entryName) && request.getEntity() != null && request.getEntity().getDisposition() != null) { - entryName = request.getEntity().getDisposition().getFilename(); - } - if (entryName == null) { - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Must specify an entry name."); - return; - } - // boolean canAppend = true; - boolean canAppend = !zipExists; - boolean isDirectory = entryName.endsWith("/"); - boolean wrongReplace = false; - try { - if (zipExists) { - ZipFile zipFile = new ZipFile(file); - // Already exists ? - canAppend &= null == zipFile.getEntry(entryName); - // Directory with the same name ? - if (isDirectory) { - wrongReplace = null != zipFile.getEntry(entryName.substring(0, entryName.length() - 1)); - } else { - wrongReplace = null != zipFile.getEntry(entryName + "/"); - } + if ("".equals(entryName) + && request.getEntity() != null + && request.getEntity().getDisposition() != null) { + entryName = request.getEntity().getDisposition().getFilename(); + } + if (entryName == null) { + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Must specify an entry name."); + return; + } + // boolean canAppend = true; + boolean canAppend = !zipExists; + boolean isDirectory = entryName.endsWith("/"); + boolean wrongReplace = false; + try { + if (zipExists) { + ZipFile zipFile = new ZipFile(file); + // Already exists ? + canAppend &= null == zipFile.getEntry(entryName); + // Directory with the same name ? + if (isDirectory) { + wrongReplace = + null + != zipFile.getEntry( + entryName.substring(0, entryName.length() - 1)); + } else { + wrongReplace = null != zipFile.getEntry(entryName + "/"); + } - canAppend &= !wrongReplace; - zipFile.close(); - } + canAppend &= !wrongReplace; + zipFile.close(); + } - Representation entity; - if (isDirectory) { - entity = null; - } else { - entity = request.getEntity(); - } + Representation entity; + if (isDirectory) { + entity = null; + } else { + entity = request.getEntity(); + } - if (canAppend) { - try { - // zipOut = new ZipOutputStream(new BufferedOutputStream(new - // FileOutputStream(file, true))); - zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file))); - writeEntityStream(entity, zipOut, entryName); - zipOut.close(); - } catch (Exception e) { - response.setStatus(Status.SERVER_ERROR_INTERNAL, e); - return; - } finally { - if (zipOut != null) { - zipOut.close(); - } - } - response.setStatus(Status.SUCCESS_CREATED); - } else { - if (wrongReplace) { - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, - "Directory cannot be replaced by a file or file by a directory."); - } else { - File writeTo; - ZipFile zipFile = null; - try { - writeTo = File.createTempFile("restlet_zip_", "zip"); - zipFile = new ZipFile(file); - zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(writeTo))); - Enumeration entries = zipFile.entries(); - boolean replaced = false; - while (entries.hasMoreElements()) { - ZipEntry e = entries.nextElement(); - if (!replaced && entryName.equals(e.getName())) { - writeEntityStream(entity, zipOut, entryName); - replaced = true; - } else { - zipOut.putNextEntry(e); - try (final BufferedInputStream zipStream = new BufferedInputStream( - zipFile.getInputStream(e))) { - IoUtils.copy(zipStream, zipOut); - } - zipOut.closeEntry(); - } - } - if (!replaced) { - writeEntityStream(entity, zipOut, entryName); - } - zipFile.close(); - zipOut.close(); - } finally { - try { - if (zipFile != null) { - zipFile.close(); - } - } finally { - if (zipOut != null) { - zipOut.close(); - } - } - } + if (canAppend) { + try { + // zipOut = new ZipOutputStream(new BufferedOutputStream(new + // FileOutputStream(file, true))); + zipOut = + new ZipOutputStream( + new BufferedOutputStream(new FileOutputStream(file))); + writeEntityStream(entity, zipOut, entryName); + zipOut.close(); + } catch (Exception e) { + response.setStatus(Status.SERVER_ERROR_INTERNAL, e); + return; + } finally { + if (zipOut != null) { + zipOut.close(); + } + } + response.setStatus(Status.SUCCESS_CREATED); + } else { + if (wrongReplace) { + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, + "Directory cannot be replaced by a file or file by a directory."); + } else { + File writeTo; + ZipFile zipFile = null; + try { + writeTo = File.createTempFile("restlet_zip_", "zip"); + zipFile = new ZipFile(file); + zipOut = + new ZipOutputStream( + new BufferedOutputStream(new FileOutputStream(writeTo))); + Enumeration entries = zipFile.entries(); + boolean replaced = false; + while (entries.hasMoreElements()) { + ZipEntry e = entries.nextElement(); + if (!replaced && entryName.equals(e.getName())) { + writeEntityStream(entity, zipOut, entryName); + replaced = true; + } else { + zipOut.putNextEntry(e); + try (final BufferedInputStream zipStream = + new BufferedInputStream(zipFile.getInputStream(e))) { + IoUtils.copy(zipStream, zipOut); + } + zipOut.closeEntry(); + } + } + if (!replaced) { + writeEntityStream(entity, zipOut, entryName); + } + zipFile.close(); + zipOut.close(); + } finally { + try { + if (zipFile != null) { + zipFile.close(); + } + } finally { + if (zipOut != null) { + zipOut.close(); + } + } + } - if (!(IoUtils.delete(file) && writeTo.renameTo(file))) { - if (!file.exists()) { - file.createNewFile(); - } - FileInputStream fis = null; - FileOutputStream fos = null; - try { - fis = new FileInputStream(writeTo); - fos = new FileOutputStream(file); - // ByteUtils.write(fis.getChannel(), - // fos.getChannel()); - IoUtils.copy(fis, fos); - response.setStatus(Status.SUCCESS_OK); - } finally { - try { - if (fis != null) { - fis.close(); - } - } finally { - if (fos != null) { - fos.close(); - } - } - } - } else { - response.setStatus(Status.SUCCESS_OK); - } - } - } - } catch (Exception e) { - response.setStatus(Status.SERVER_ERROR_INTERNAL, e); - } - } + if (!(IoUtils.delete(file) && writeTo.renameTo(file))) { + if (!file.exists()) { + file.createNewFile(); + } + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(writeTo); + fos = new FileOutputStream(file); + // ByteUtils.write(fis.getChannel(), + // fos.getChannel()); + IoUtils.copy(fis, fos); + response.setStatus(Status.SUCCESS_OK); + } finally { + try { + if (fis != null) { + fis.close(); + } + } finally { + if (fos != null) { + fos.close(); + } + } + } + } else { + response.setStatus(Status.SUCCESS_OK); + } + } + } + } catch (Exception e) { + response.setStatus(Status.SERVER_ERROR_INTERNAL, e); + } + } - /** - * Writes an entity to a given ZIP output stream with a given ZIP entry name. - * - * @param entity The entity to write. - * @param out The ZIP output stream. - * @param entryName The ZIP entry name. - * @return True if the writing was successful. - * @throws IOException - */ - private boolean writeEntityStream(Representation entity, ZipOutputStream out, String entryName) throws IOException { - if (entity != null && !entryName.endsWith("/")) { - ZipEntry entry = new ZipEntry(entryName); - if (entity.getModificationDate() != null) { - entry.setTime(entity.getModificationDate().getTime()); - } else { - entry.setTime(System.currentTimeMillis()); - } - out.putNextEntry(entry); - try (final BufferedInputStream entityStream = new BufferedInputStream(entity.getStream())) { - IoUtils.copy(entityStream, out); - } - out.closeEntry(); - return true; - } + /** + * Writes an entity to a given ZIP output stream with a given ZIP entry name. + * + * @param entity The entity to write. + * @param out The ZIP output stream. + * @param entryName The ZIP entry name. + * @return True if the writing was successful. + * @throws IOException + */ + private boolean writeEntityStream(Representation entity, ZipOutputStream out, String entryName) + throws IOException { + if (entity != null && !entryName.endsWith("/")) { + ZipEntry entry = new ZipEntry(entryName); + if (entity.getModificationDate() != null) { + entry.setTime(entity.getModificationDate().getTime()); + } else { + entry.setTime(System.currentTimeMillis()); + } + out.putNextEntry(entry); + try (final BufferedInputStream entityStream = + new BufferedInputStream(entity.getStream())) { + IoUtils.copy(entityStream, out); + } + out.closeEntry(); + return true; + } - out.putNextEntry(new ZipEntry(entryName)); - out.closeEntry(); - return false; - } + out.putNextEntry(new ZipEntry(entryName)); + out.closeEntry(); + return false; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java index ac5e0a6937..c49e7b8ac7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java @@ -1,130 +1,121 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; -import org.restlet.data.MediaType; -import org.restlet.representation.Representation; -import org.restlet.service.MetadataService; - import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import org.restlet.data.MediaType; +import org.restlet.representation.Representation; +import org.restlet.service.MetadataService; /** * Local entity based on an entry in a Zip archive. - * + * * @author Remi Dewitte remi@gide.net */ public class ZipEntryEntity extends Entity { - /** The Zip entry. */ - protected final ZipEntry entry; - - /** The Zip file. */ - protected final ZipFile zipFile; - - /** - * Constructor. - * - * @param zipFile The Zip file. - * @param entryName The Zip entry name. - * @param metadataService The metadata service to use. - */ - public ZipEntryEntity(ZipFile zipFile, String entryName, MetadataService metadataService) { - super(metadataService); - this.zipFile = zipFile; - ZipEntry entry = zipFile.getEntry(entryName); - if (entry == null) - this.entry = new ZipEntry(entryName); - else { - // Checking we don't have a directory - ZipEntry entryDir = zipFile.getEntry(entryName + "/"); - if (entryDir != null) - this.entry = entryDir; - else - this.entry = entry; - } - } - - /** - * Constructor. - * - * @param zipFile The Zip file. - * @param entry The Zip entry. - * @param metadataService The metadata service to use. - */ - public ZipEntryEntity(ZipFile zipFile, ZipEntry entry, MetadataService metadataService) { - super(metadataService); - this.zipFile = zipFile; - this.entry = entry; - } - - @Override - public boolean exists() { - if ("".equals(getName())) - return true; - // ZipEntry re = zipFile.getEntry(entry.getName()); - // return re != null; - return entry.getSize() != -1; - } - - @Override - public List getChildren() { - List result = null; - - if (isDirectory()) { - result = new ArrayList(); - Enumeration entries = zipFile.entries(); - String n = entry.getName(); - while (entries.hasMoreElements()) { - ZipEntry e = entries.nextElement(); - if (e.getName().startsWith(n) && e.getName().length() != n.length()) - result.add(new ZipEntryEntity(zipFile, e, getMetadataService())); - } - } - - return result; - } - - @Override - public String getName() { - return entry.getName(); - } - - @Override - public Entity getParent() { - if (entry.getName().isEmpty()) - return null; - - String n = entry.getName(); - String pn = n.substring(0, n.lastIndexOf('/') + 1); - return new ZipEntryEntity(zipFile, zipFile.getEntry(pn), getMetadataService()); - } - - @Override - public Representation getRepresentation(MediaType defaultMediaType, int timeToLive) { - return new ZipEntryRepresentation(defaultMediaType, zipFile, entry, timeToLive); - } - - @Override - public boolean isDirectory() { - if (entry.getName().isEmpty()) - return true; - return entry.isDirectory(); - } - - @Override - public boolean isNormal() { - return !entry.isDirectory(); - } - + /** The Zip entry. */ + protected final ZipEntry entry; + + /** The Zip file. */ + protected final ZipFile zipFile; + + /** + * Constructor. + * + * @param zipFile The Zip file. + * @param entryName The Zip entry name. + * @param metadataService The metadata service to use. + */ + public ZipEntryEntity(ZipFile zipFile, String entryName, MetadataService metadataService) { + super(metadataService); + this.zipFile = zipFile; + ZipEntry entry = zipFile.getEntry(entryName); + if (entry == null) this.entry = new ZipEntry(entryName); + else { + // Checking we don't have a directory + ZipEntry entryDir = zipFile.getEntry(entryName + "/"); + if (entryDir != null) this.entry = entryDir; + else this.entry = entry; + } + } + + /** + * Constructor. + * + * @param zipFile The Zip file. + * @param entry The Zip entry. + * @param metadataService The metadata service to use. + */ + public ZipEntryEntity(ZipFile zipFile, ZipEntry entry, MetadataService metadataService) { + super(metadataService); + this.zipFile = zipFile; + this.entry = entry; + } + + @Override + public boolean exists() { + if ("".equals(getName())) return true; + // ZipEntry re = zipFile.getEntry(entry.getName()); + // return re != null; + return entry.getSize() != -1; + } + + @Override + public List getChildren() { + List result = null; + + if (isDirectory()) { + result = new ArrayList<>(); + Enumeration entries = zipFile.entries(); + String n = entry.getName(); + while (entries.hasMoreElements()) { + ZipEntry e = entries.nextElement(); + if (e.getName().startsWith(n) && e.getName().length() != n.length()) + result.add(new ZipEntryEntity(zipFile, e, getMetadataService())); + } + } + + return result; + } + + @Override + public String getName() { + return entry.getName(); + } + + @Override + public Entity getParent() { + if (entry.getName().isEmpty()) return null; + + String n = entry.getName(); + String pn = n.substring(0, n.lastIndexOf('/') + 1); + return new ZipEntryEntity(zipFile, zipFile.getEntry(pn), getMetadataService()); + } + + @Override + public Representation getRepresentation(MediaType defaultMediaType, int timeToLive) { + return new ZipEntryRepresentation(defaultMediaType, zipFile, entry, timeToLive); + } + + @Override + public boolean isDirectory() { + if (entry.getName().isEmpty()) return true; + return entry.isDirectory(); + } + + @Override + public boolean isNormal() { + return !entry.isDirectory(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java index be51e61604..4adfda06b4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java @@ -1,83 +1,80 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.local; -import org.restlet.data.Disposition; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; -import org.restlet.representation.StreamRepresentation; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Date; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import org.restlet.data.Disposition; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.StreamRepresentation; /** * An entry in a Zip/JAR file. - * - * It is very important {@link #release()} is called to close the underlying Zip - * file. - * + * + *

It is very important {@link #release()} is called to close the underlying Zip file. + * * @author Remi Dewitte remi@gide.net */ public class ZipEntryRepresentation extends StreamRepresentation { - /** The Zip entry. */ - protected final ZipEntry entry; - - /** The Zip file. */ - protected final ZipFile zipFile; + /** The Zip entry. */ + protected final ZipEntry entry; - /** - * Constructor. - * - * @param mediaType The entry media type. - * @param zipFile The parent Zip archive file. - * @param entry The Zip entry. - * @param timeToLive The time to live before it expires (in seconds). - */ - public ZipEntryRepresentation(MediaType mediaType, ZipFile zipFile, ZipEntry entry, int timeToLive) { - super(mediaType); - this.zipFile = zipFile; - this.entry = entry; - Disposition disposition = new Disposition(); - disposition.setFilename(entry.getName()); - this.setDisposition(disposition); - setSize(entry.getSize()); - setModificationDate(new Date(entry.getTime())); + /** The Zip file. */ + protected final ZipFile zipFile; - if (timeToLive == 0) { - setExpirationDate(null); - } else if (timeToLive > 0) { - setExpirationDate(new Date(System.currentTimeMillis() + (1000L * timeToLive))); - } - } + /** + * Constructor. + * + * @param mediaType The entry media type. + * @param zipFile The parent Zip archive file. + * @param entry The Zip entry. + * @param timeToLive The time to live before it expires (in seconds). + */ + public ZipEntryRepresentation( + MediaType mediaType, ZipFile zipFile, ZipEntry entry, int timeToLive) { + super(mediaType); + this.zipFile = zipFile; + this.entry = entry; + Disposition disposition = new Disposition(); + disposition.setFilename(entry.getName()); + this.setDisposition(disposition); + setSize(entry.getSize()); + setModificationDate(new Date(entry.getTime())); - @Override - public InputStream getStream() throws IOException { - return zipFile.getInputStream(entry); - } + if (timeToLive == 0) { + setExpirationDate(null); + } else if (timeToLive > 0) { + setExpirationDate(new Date(System.currentTimeMillis() + (1000L * timeToLive))); + } + } - @Override - public void release() { - try { - zipFile.close(); - } catch (IOException e) { - } - } + @Override + public InputStream getStream() throws IOException { + return zipFile.getInputStream(entry); + } - @Override - public void write(OutputStream outputStream) throws IOException { - IoUtils.copy(getStream(), outputStream); - } + @Override + public void release() { + try { + zipFile.close(); + } catch (IOException e) { + } + } + @Override + public void write(OutputStream outputStream) throws IOException { + IoUtils.copy(getStream(), outputStream); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java index f786238b5b..4e81ad7d84 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java @@ -1,95 +1,92 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; import java.io.IOException; /** - * Log file handler that uses the {@link AccessLogFormatter} by default. Also - * useful in configuration files to differentiate from the - * {@link java.util.logging.FileHandler}. - * + * Log file handler that uses the {@link AccessLogFormatter} by default. Also, useful in + * configuration files to differentiate from the {@link java.util.logging.FileHandler}. + * * @author Jerome Louvel */ public class AccessLogFileHandler extends java.util.logging.FileHandler { - /** - * Constructor. - * - * @throws IOException - * @throws SecurityException - */ - public AccessLogFileHandler() throws IOException, SecurityException { - super(); - init(); - } - - /** - * Constructor. - * - * @param pattern The name of the output file. - * @throws IOException - * @throws SecurityException - */ - public AccessLogFileHandler(String pattern) throws IOException, SecurityException { - super(pattern); - init(); - } + /** + * Constructor. + * + * @throws IOException + * @throws SecurityException + */ + public AccessLogFileHandler() throws IOException, SecurityException { + super(); + init(); + } - /** - * Constructor. - * - * @param pattern The name of the output file. - * @param append Specifies append mode. - * @throws IOException - * @throws SecurityException - */ - public AccessLogFileHandler(String pattern, boolean append) throws IOException, SecurityException { - super(pattern, append); - init(); - } + /** + * Constructor. + * + * @param pattern The name of the output file. + * @throws IOException + * @throws SecurityException + */ + public AccessLogFileHandler(String pattern) throws IOException, SecurityException { + super(pattern); + init(); + } - /** - * Constructor. - * - * @param pattern The name of the output file. - * @param limit The maximum number of bytes to write to any one file. - * @param count The number of files to use. - * @throws IOException - * @throws SecurityException - */ - public AccessLogFileHandler(String pattern, int limit, int count) throws IOException, SecurityException { - super(pattern, limit, count); - init(); - } + /** + * Constructor. + * + * @param pattern The name of the output file. + * @param append Specifies append mode. + * @throws IOException + * @throws SecurityException + */ + public AccessLogFileHandler(String pattern, boolean append) + throws IOException, SecurityException { + super(pattern, append); + init(); + } - /** - * Constructor. - * - * @param pattern The name of the output file. - * @param limit The maximum number of bytes to write to any one file. - * @param count The number of files to use. - * @param append Specifies append mode. - * @throws IOException - * @throws SecurityException - */ - public AccessLogFileHandler(String pattern, int limit, int count, boolean append) - throws IOException, SecurityException { - super(pattern, limit, count, append); - init(); - } + /** + * Constructor. + * + * @param pattern The name of the output file. + * @param limit The maximum number of bytes to write to any one file. + * @param count The number of files to use. + * @throws IOException + * @throws SecurityException + */ + public AccessLogFileHandler(String pattern, int limit, int count) + throws IOException, SecurityException { + super(pattern, limit, count); + init(); + } - /** - * Initialization code common to all constructors. - */ - protected void init() { - setFormatter(new AccessLogFormatter()); - } + /** + * Constructor. + * + * @param pattern The name of the output file. + * @param limit The maximum number of bytes to write to any one file. + * @param count The number of files to use. + * @param append Specifies append mode. + * @throws IOException + * @throws SecurityException + */ + public AccessLogFileHandler(String pattern, int limit, int count, boolean append) + throws IOException, SecurityException { + super(pattern, limit, count, append); + init(); + } + /** Initialization code common to all constructors. */ + protected void init() { + setFormatter(new AccessLogFormatter()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java index 5166e0bd21..a3216b99b5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java @@ -1,28 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; import java.util.logging.Formatter; import java.util.logging.LogRecord; /** - * Log record formatter which simply outputs the message on a new line. Useful - * for Web-style logs. - * + * Log record formatter which simply outputs the message on a new line. Useful for Web-style logs. + * * @author Jerome Louvel */ public class AccessLogFormatter extends Formatter { - @Override - public String format(LogRecord logRecord) { - return logRecord.getMessage() + '\n'; - } - + @Override + public String format(LogRecord logRecord) { + return logRecord.getMessage() + '\n'; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java index 4a77c7cf99..0f0c837489 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java @@ -1,42 +1,38 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; -import org.restlet.engine.Engine; - import java.util.logging.Handler; +import org.restlet.engine.Engine; /** - * Access log record formatter which writes a header describing the default log - * format. - * + * Access log record formatter which writes a header describing the default log format. + * * @author Jerome Louvel */ public class DefaultAccessLogFormatter extends AccessLogFormatter { - @Override - public String getHead(Handler h) { - final StringBuilder sb = new StringBuilder(); - sb.append("#Software: Restlet Framework ").append(Engine.VERSION).append('\n'); - sb.append("#Version: 1.0\n"); - sb.append("#Date: "); - final long currentTime = System.currentTimeMillis(); - sb.append(String.format("%tF", currentTime)); - sb.append(' '); - sb.append(String.format("%tT", currentTime)); - sb.append('\n'); - sb.append("#Fields: "); - sb.append("date time c-ip cs-username s-ip s-port cs-method "); - sb.append("cs-uri-stem cs-uri-query sc-status sc-bytes cs-bytes "); - sb.append("time-taken cs-host cs(User-Agent) cs(Referrer)\n"); - return sb.toString(); - } - + @Override + public String getHead(Handler h) { + final StringBuilder sb = new StringBuilder(); + sb.append("#Software: Restlet Framework ").append(Engine.VERSION).append('\n'); + sb.append("#Version: 1.0\n"); + sb.append("#Date: "); + final long currentTime = System.currentTimeMillis(); + sb.append(String.format("%tF", currentTime)); + sb.append(' '); + sb.append(String.format("%tT", currentTime)); + sb.append('\n'); + sb.append("#Fields: "); + sb.append("date time c-ip cs-username s-ip s-port cs-method "); + sb.append("cs-uri-stem cs-uri-query sc-status sc-bytes cs-bytes "); + sb.append("time-taken cs-host cs(User-Agent) cs(Referrer)\n"); + return sb.toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java b/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java index 7b42ad3c24..2aecb77cb9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java @@ -1,18 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; -import org.restlet.Context; -import org.restlet.engine.io.IoUtils; -import org.restlet.engine.util.StringUtils; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -20,101 +15,107 @@ import java.net.Socket; import java.util.StringTokenizer; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.engine.io.IoUtils; +import org.restlet.engine.util.StringUtils; /** * Simple IDENT client. Follow the RFC 1413. - * + * * @author Jerome Louvel */ public class IdentClient { - /** The timeout while attempting to connect to the Ident server. */ - private static final int CONNECT_TIMEOUT = 100; - - /** The timeout while communicating with the Ident server. */ - private static final int SO_TIMEOUT = 500; - - /** The remote host type. */ - private volatile String hostType; - - /** The user identifier. */ - private volatile String userIdentifier; - - /** - * Constructor. - * - * @param clientAddress The client IP address. - * @param clientPort The client port (remote). - * @param serverPort The server port (local). - */ - public IdentClient(String clientAddress, int clientPort, int serverPort) { - Socket socket = null; - - if ((clientAddress != null) && (clientPort != -1) && (serverPort != -1)) { - BufferedReader in = null; - try { - // Compose the IDENT request - final String request = clientPort + " , " + serverPort + "\r\n"; - - // Send the request to the remote server - socket = new Socket(); - socket.setSoTimeout(SO_TIMEOUT); - socket.connect(new InetSocketAddress(clientAddress, 113), CONNECT_TIMEOUT); - socket.getOutputStream().write(StringUtils.getAsciiBytes(request)); - - // Read the response - in = new BufferedReader(new InputStreamReader(socket.getInputStream()), IoUtils.BUFFER_SIZE); - final String response = in.readLine(); - - // Parse the response - if (response != null) { - final StringTokenizer st = new StringTokenizer(response, ":"); - - if (st.countTokens() >= 3) { - // Skip the first token - st.nextToken(); - - // Get the command - final String command = st.nextToken().trim(); - if (command.equalsIgnoreCase("USERID") && (st.countTokens() >= 2)) { - // Get the host type - this.hostType = st.nextToken().trim(); - - // Get the remaining text as a user identifier - this.userIdentifier = st.nextToken("").substring(1); - } - } - } - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.FINE, "Unable to complete the IDENT request", ioe); - } finally { - try { - // Always attempt to close the reader, therefore the socket - if (in != null) { - in.close(); - } - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.FINE, "Unable to close the socket", ioe); - } - } - } - } - - /** - * Returns the remote host type. - * - * @return The remote host type. - */ - public String getHostType() { - return this.hostType; - } - - /** - * Returns the user identifier. - * - * @return The user identifier. - */ - public String getUserIdentifier() { - return this.userIdentifier; - } - + /** The timeout while attempting to connect to the Ident server. */ + private static final int CONNECT_TIMEOUT = 100; + + /** The timeout while communicating with the Ident server. */ + private static final int SO_TIMEOUT = 500; + + /** The remote host type. */ + private volatile String hostType; + + /** The user identifier. */ + private volatile String userIdentifier; + + /** + * Constructor. + * + * @param clientAddress The client IP address. + * @param clientPort The client port (remote). + * @param serverPort The server port (local). + */ + public IdentClient(String clientAddress, int clientPort, int serverPort) { + Socket socket = null; + + if ((clientAddress != null) && (clientPort != -1) && (serverPort != -1)) { + BufferedReader in = null; + try { + // Compose the IDENT request + final String request = clientPort + " , " + serverPort + "\r\n"; + + // Send the request to the remote server + socket = new Socket(); + socket.setSoTimeout(SO_TIMEOUT); + socket.connect(new InetSocketAddress(clientAddress, 113), CONNECT_TIMEOUT); + socket.getOutputStream().write(StringUtils.getAsciiBytes(request)); + + // Read the response + in = + new BufferedReader( + new InputStreamReader(socket.getInputStream()), + IoUtils.BUFFER_SIZE); + final String response = in.readLine(); + + // Parse the response + if (response != null) { + final StringTokenizer st = new StringTokenizer(response, ":"); + + if (st.countTokens() >= 3) { + // Skip the first token + st.nextToken(); + + // Get the command + final String command = st.nextToken().trim(); + if (command.equalsIgnoreCase("USERID") && (st.countTokens() >= 2)) { + // Get the host type + this.hostType = st.nextToken().trim(); + + // Get the remaining text as a user identifier + this.userIdentifier = st.nextToken("").substring(1); + } + } + } + } catch (IOException ioe) { + Context.getCurrentLogger() + .log(Level.FINE, "Unable to complete the IDENT request", ioe); + } finally { + try { + // Always attempt to close the reader, therefore the socket + if (in != null) { + in.close(); + } + } catch (IOException ioe) { + Context.getCurrentLogger().log(Level.FINE, "Unable to close the socket", ioe); + } + } + } + } + + /** + * Returns the remote host type. + * + * @return The remote host type. + */ + public String getHostType() { + return this.hostType; + } + + /** + * Returns the user identifier. + * + * @return The user identifier. + */ + public String getUserIdentifier() { + return this.userIdentifier; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java b/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java index 61b8f7ddec..b49d7a613d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; +import java.util.logging.Level; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -16,89 +17,92 @@ import org.restlet.routing.Filter; import org.restlet.service.LogService; -import java.util.logging.Level; -import java.util.logging.Logger; - /** - * Filter logging all calls after their handling by the target Restlet. The - * current format is similar to IIS 6 logs. The logging is based on the - * java.util.logging package. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Filter logging all calls after their handling by the target Restlet. The current format is + * similar to IIS 6 logs. The logging is based on the java.util.logging package. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class LogFilter extends Filter { - /** The log service. */ - protected volatile LogService logService; - - /** The log service logger. */ - private volatile Logger logLogger; + /** The log service. */ + protected volatile LogService logService; - /** - * Constructor. - * - * @param context The context. - * @param logService The log service descriptor. - */ - public LogFilter(Context context, LogService logService) { - super(context); - this.logService = logService; + /** The log service logger. */ + private volatile Logger logLogger; - if (logService != null) { - if (logService.getLoggerName() != null) { - this.logLogger = Engine.getLogger(logService.getLoggerName()); - } else if ((context != null) && (context.getLogger().getParent() != null)) { - this.logLogger = Engine.getLogger(context.getLogger().getParent().getName() + "." - + LogUtils.getBestClassName(logService.getClass())); - } else { - this.logLogger = Engine.getLogger(LogUtils.getBestClassName(logService.getClass())); - } - } - } + /** + * Constructor. + * + * @param context The context. + * @param logService The log service descriptor. + */ + public LogFilter(Context context, LogService logService) { + super(context); + this.logService = logService; - /** - * Allows filtering after processing by the next Restlet. Logs the call. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - protected void afterHandle(Request request, Response response) { - try { - if (request.isLoggable() && this.logLogger.isLoggable(Level.INFO)) { - long startTime = (Long) request.getAttributes().get("org.restlet.startTime"); - int duration = (int) (System.currentTimeMillis() - startTime); - this.logLogger.log(Level.INFO, this.logService.getResponseLogMessage(response, duration)); - } - } catch (Throwable e) { - // Error while logging the call, cf issue #931 - getLogger().log(Level.SEVERE, "Cannot log call", e); - } - } + if (logService != null) { + if (logService.getLoggerName() != null) { + this.logLogger = Engine.getLogger(logService.getLoggerName()); + } else if ((context != null) && (context.getLogger().getParent() != null)) { + this.logLogger = + Engine.getLogger( + context.getLogger().getParent().getName() + + "." + + LogUtils.getBestClassName(logService.getClass())); + } else { + this.logLogger = Engine.getLogger(LogUtils.getBestClassName(logService.getClass())); + } + } + } - /** - * Allows filtering before processing by the next Restlet. Saves the start time. - * - * @param request The request to handle. - * @param response The response to update. - * @return The continuation status. - */ - @Override - protected int beforeHandle(Request request, Response response) { - request.getAttributes().put("org.restlet.startTime", System.currentTimeMillis()); + /** + * Allows filtering after processing by the next Restlet. Logs the call. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + protected void afterHandle(Request request, Response response) { + try { + if (request.isLoggable() && this.logLogger.isLoggable(Level.INFO)) { + long startTime = (Long) request.getAttributes().get("org.restlet.startTime"); + int duration = (int) (System.currentTimeMillis() - startTime); + this.logLogger.log( + Level.INFO, this.logService.getResponseLogMessage(response, duration)); + } + } catch (Throwable e) { + // Error while logging the call, cf issue #931 + getLogger().log(Level.SEVERE, "Cannot log call", e); + } + } - // Set the log level for the given request - request.setLoggable(this.logService.isLoggable(request)); + /** + * Allows filtering before processing by the next Restlet. Saves the start time. + * + * @param request The request to handle. + * @param response The response to update. + * @return The continuation status. + */ + @Override + protected int beforeHandle(Request request, Response response) { + request.getAttributes().put("org.restlet.startTime", System.currentTimeMillis()); - if (request.isLoggable() && this.logLogger.isLoggable(Level.FINE)) { - this.logLogger.fine("Processing request to: \"" + ((request.getResourceRef() == null) ? "Unknown URI" - : request.getResourceRef().getTargetRef().toString()) + "\""); - } + // Set the log level for the given request + request.setLoggable(this.logService.isLoggable(request)); - return CONTINUE; - } + if (request.isLoggable() && this.logLogger.isLoggable(Level.FINE)) { + this.logLogger.fine( + "Processing request to: \"" + + ((request.getResourceRef() == null) + ? "Unknown URI" + : request.getResourceRef().getTargetRef().toString()) + + "\""); + } + return CONTINUE; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java b/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java index bf13715a3f..03657723df 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java @@ -1,60 +1,54 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; /** * Logging related utility methods. - * + * * @author Jerome Louvel */ public class LogUtils { - /** - * Prevent instantiation of the class. - */ - private LogUtils() { - } - - /** - * Return the best class name. If the class is anonymous, then it returns the - * super class name. - * - * @param clazz The class to name. - * @return The class name. - */ - public static String getBestClassName(Class clazz) { - String result = clazz.getSimpleName(); - - if (result.isEmpty()) { - result = getBestClassName(clazz.getSuperclass()); - } - - return result; - } - - /** - * Returns a non-null logger name. It is composed by the canonical class name of - * the owner object suffixed by the owner's hash code. - * - * @param baseName The base logger name to prepend, without a trailing dot. - * @param owner The context owner. - * @return The logger name. - */ - public static String getLoggerName(String baseName, Object owner) { - String result = baseName; - - if ((owner != null) && (owner.getClass().getSimpleName() != null)) { - result += "." + getBestClassName(owner.getClass()); - } - - return result; - } - + /** Prevent instantiation of the class. */ + private LogUtils() {} + + /** + * Return the best class name. If the class is anonymous, then it returns the super class name. + * + * @param clazz The class to name. + * @return The class name. + */ + public static String getBestClassName(Class clazz) { + String result = clazz.getSimpleName(); + + if (result.isEmpty()) { + result = getBestClassName(clazz.getSuperclass()); + } + + return result; + } + + /** + * Returns a non-null logger name. It is composed by the canonical class name of the owner + * object suffixed by the owner's hash code. + * + * @param baseName The base logger name to prepend, without a trailing dot. + * @param owner The context owner. + * @return The logger name. + */ + public static String getLoggerName(String baseName, Object owner) { + String result = baseName; + + if ((owner != null) && (owner.getClass().getSimpleName() != null)) { + result += "." + getBestClassName(owner.getClass()); + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java b/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java index 9e5f4c64cb..d2826014e4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java @@ -1,98 +1,92 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; import java.util.logging.Logger; /** - * Logger facade to the underlying logging framework used by the Restlet - * Framework. By default, it relies on the JULI mechanism built in Java SE. You - * can provide an alternate implementation by extending this class and - * overriding the methods. - * + * Logger facade to the underlying logging framework used by the Restlet Framework. By default, it + * relies on the JULI mechanism built in Java SE. You can provide an alternate implementation by + * extending this class and overriding the methods. + * * @author Jerome Louvel */ public class LoggerFacade { - /** - * Returns an anonymous logger. By default it calls - * {@link Logger#getAnonymousLogger()}. This method should be overridden by - * subclasses. - * - * @return The logger. - */ - public Logger getAnonymousLogger() { - return Logger.getAnonymousLogger(); - } - - /** - * Returns a logger based on the class name of the given object. By default, it - * calls {@link #getLogger(Class, String)} with a null default logger name. - * - * @param clazz The parent class. - * @return The logger. - */ - public final Logger getLogger(Class clazz) { - return getLogger(clazz, null); - } + /** + * Returns an anonymous logger. By default, it calls {@link Logger#getAnonymousLogger()}. This + * method should be overridden by subclasses. + * + * @return The logger. + */ + public Logger getAnonymousLogger() { + return Logger.getAnonymousLogger(); + } - /** - * Returns a logger based on the class name of the given object. - * - * @param clazz The parent class. - * @param defaultLoggerName The default logger name to use if no one can be - * inferred from the class. - * @return The logger. - */ - public final Logger getLogger(Class clazz, String defaultLoggerName) { - String loggerName = null; + /** + * Returns a logger based on the class name of the given object. By default, it calls {@link + * #getLogger(Class, String)} with a null default logger name. + * + * @param clazz The parent class. + * @return The logger. + */ + public final Logger getLogger(Class clazz) { + return getLogger(clazz, null); + } - if (clazz != null) { - loggerName = clazz.getCanonicalName(); - } + /** + * Returns a logger based on the class name of the given object. + * + * @param clazz The parent class. + * @param defaultLoggerName The default logger name to use if no one can be inferred from the + * class. + * @return The logger. + */ + public final Logger getLogger(Class clazz, String defaultLoggerName) { + String loggerName = null; - if (loggerName == null) { - loggerName = defaultLoggerName; - } + if (clazz != null) { + loggerName = clazz.getCanonicalName(); + } - if (loggerName != null) { - return getLogger(loggerName); - } + if (loggerName == null) { + loggerName = defaultLoggerName; + } - return getAnonymousLogger(); - } + if (loggerName != null) { + return getLogger(loggerName); + } - /** - * Returns a logger based on the class name of the given object. By default, it - * calls {@link #getLogger(Class, String)} with the object's class as a first - * parameter. - * - * @param object The parent object. - * @param defaultLoggerName The default logger name to use if no one can be - * inferred from the object class. - * @return The logger. - */ - public final Logger getLogger(Object object, String defaultLoggerName) { - return getLogger(object.getClass(), defaultLoggerName); - } + return getAnonymousLogger(); + } - /** - * Returns a logger based on the given logger name. By default, it calls - * {@link Logger#getLogger(String)}. This method should be overridden by - * subclasses. - * - * @param loggerName The logger name. - * @return The logger. - */ - public Logger getLogger(String loggerName) { - return Logger.getLogger(loggerName); - } + /** + * Returns a logger based on the class name of the given object. By default, it calls {@link + * #getLogger(Class, String)} with the object's class as a first parameter. + * + * @param object The parent object. + * @param defaultLoggerName The default logger name to use if no one can be inferred from the + * object class. + * @return The logger. + */ + public final Logger getLogger(Object object, String defaultLoggerName) { + return getLogger(object.getClass(), defaultLoggerName); + } + /** + * Returns a logger based on the given logger name. By default, it calls {@link + * Logger#getLogger(String)}. This method should be overridden by subclasses. + * + * @param loggerName The logger name. + * @return The logger. + */ + public Logger getLogger(String loggerName) { + return Logger.getLogger(loggerName); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java b/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java index d87e9cdc27..b5f2f55ba7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; import java.util.concurrent.ThreadFactory; @@ -15,57 +14,58 @@ /** * Thread factory that logs uncaught exceptions thrown by the created threads. - * + * * @author Jerome Louvel */ public class LoggingThreadFactory implements ThreadFactory { - /** - * Handle uncaught thread exceptions. - */ - private class LoggingExceptionHandler implements Thread.UncaughtExceptionHandler { + /** Handle uncaught thread exceptions. */ + private class LoggingExceptionHandler implements Thread.UncaughtExceptionHandler { - public void uncaughtException(Thread t, Throwable ex) { - logger.log(Level.SEVERE, "Thread: " + t.getName() + " terminated with exception: " + ex.getMessage(), ex); - } - } + public void uncaughtException(Thread t, Throwable ex) { + logger.log( + Level.SEVERE, + "Thread: " + t.getName() + " terminated with exception: " + ex.getMessage(), + ex); + } + } - /** The associated logger. */ - private final Logger logger; + /** The associated logger. */ + private final Logger logger; - /** Indicates if threads should be created as daemons. */ - private final boolean daemon; + /** Indicates if threads should be created as daemons. */ + private final boolean daemon; - /** - * Constructor. - * - * @param logger The associated logger. - */ - public LoggingThreadFactory(Logger logger) { - this(logger, false); - } + /** + * Constructor. + * + * @param logger The associated logger. + */ + public LoggingThreadFactory(Logger logger) { + this(logger, false); + } - /** - * Constructor. - * - * @param logger The associated logger. - * @param daemon Indicates if threads should be created as daemons. - */ - public LoggingThreadFactory(Logger logger, boolean daemon) { - this.logger = logger; - this.daemon = daemon; - } + /** + * Constructor. + * + * @param logger The associated logger. + * @param daemon Indicates if threads should be created as daemons. + */ + public LoggingThreadFactory(Logger logger, boolean daemon) { + this.logger = logger; + this.daemon = daemon; + } - /** - * Creates a new thread. - * - * @param r The runnable task. - */ - public Thread newThread(Runnable r) { - Thread result = new Thread(r); - result.setName("Restlet-" + result.hashCode()); - result.setUncaughtExceptionHandler(new LoggingExceptionHandler()); - result.setDaemon(this.daemon); - return result; - } + /** + * Creates a new thread. + * + * @param r The runnable task. + */ + public Thread newThread(Runnable r) { + Thread result = new Thread(r); + result.setName("Restlet-" + result.hashCode()); + result.setUncaughtExceptionHandler(new LoggingExceptionHandler()); + result.setDaemon(this.daemon); + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java index 0a55ea3820..1cd232effe 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; import java.io.PrintWriter; @@ -15,44 +14,43 @@ import java.util.logging.LogRecord; /** - * Special log formatter that displays the level, the logger name and the actual - * message. It also displays the stack trace if available. - * - * This is particularly useful for debugging. - * + * Special log formatter that displays the level, the logger name, and the actual message. It also + * displays the stack trace if available. + * + *

This is particularly useful for debugging. + * * @author Jerome Louvel */ public class SimplerFormatter extends Formatter { - /** - * Format the given LogRecord. - * - * @param record the log record to be formatted. - * @return a formatted log record - */ - public synchronized String format(LogRecord record) { - StringBuilder sb = new StringBuilder(); - - sb.append(record.getLevel().getLocalizedName()); - sb.append(" ["); - sb.append(record.getLoggerName()); - sb.append("] - "); - sb.append(formatMessage(record)); - sb.append('\n'); - - if (record.getThrown() != null) { - try { - sb.append(System.getProperty("line.separator")); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - record.getThrown().printStackTrace(pw); - pw.close(); - sb.append(sw.toString()); - } catch (Exception ex) { - } - } - - return sb.toString(); - } - + /** + * Format the given LogRecord. + * + * @param record the log record to be formatted. + * @return a formatted log record + */ + public synchronized String format(LogRecord record) { + StringBuilder sb = new StringBuilder(); + + sb.append(record.getLevel().getLocalizedName()); + sb.append(" ["); + sb.append(record.getLoggerName()); + sb.append("] - "); + sb.append(formatMessage(record)); + sb.append('\n'); + + if (record.getThrown() != null) { + try { + sb.append(System.lineSeparator()); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + record.getThrown().printStackTrace(pw); + pw.close(); + sb.append(sw); + } catch (Exception ignored) { + } + } + + return sb.toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java index ae29c4f336..7d29c7e35f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.log; import java.io.PrintWriter; @@ -15,40 +14,39 @@ import java.util.logging.LogRecord; /** - * Special log formatter that displays the actual message only. It also displays - * the stack trace if available. - * - * This is particularly useful for debugging. - * + * Special log formatter that displays the actual message only. It also displays the stack trace if + * available. + * + *

This is particularly useful for debugging. + * * @author Jerome Louvel */ public class SimplestFormatter extends Formatter { - /** - * Format the given LogRecord. - * - * @param record the log record to be formatted. - * @return a formatted log record - */ - public synchronized String format(LogRecord record) { - StringBuilder sb = new StringBuilder(); - - sb.append(formatMessage(record)); - sb.append('\n'); - - if (record.getThrown() != null) { - try { - sb.append(System.getProperty("line.separator")); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - record.getThrown().printStackTrace(pw); - pw.close(); - sb.append(sw.toString()); - } catch (Exception ex) { - } - } - - return sb.toString(); - } - + /** + * Format the given LogRecord. + * + * @param record the log record to be formatted. + * @return a formatted log record + */ + public synchronized String format(LogRecord record) { + StringBuilder sb = new StringBuilder(); + + sb.append(formatMessage(record)); + sb.append('\n'); + + if (record.getThrown() != null) { + try { + sb.append(System.lineSeparator()); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + record.getThrown().printStackTrace(pw); + pw.close(); + sb.append(sw); + } catch (Exception ignored) { + } + } + + return sb.toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java index 6c883cab02..e63ebf6cda 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java @@ -1,238 +1,246 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.resource; -import org.restlet.Context; -import org.restlet.engine.util.SystemUtils; - -import java.lang.reflect.*; +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.Objects; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.engine.util.SystemUtils; /** * Descriptor for Restlet annotations. - * + * * @author Jerome Louvel */ public abstract class AnnotationInfo { - /** - * Returns the actual type for a given generic type name. - * - * @param currentClass The current class to walk up. - * @param genericTypeName The generic type name to resolve. - * @return The actual type. - */ - protected static Class getJavaActualType(Class currentClass, String genericTypeName) { - Class result = null; - - // Lookup in the super class - result = getJavaActualType(currentClass.getGenericSuperclass(), genericTypeName); - - if (result == null) { - // Lookup in the implemented interfaces - Type[] interfaceTypes = currentClass.getGenericInterfaces(); - - for (int i = 0; (result == null) && (i < interfaceTypes.length); i++) { - result = getJavaActualType(interfaceTypes[i], genericTypeName); - } - } - - return result; - } - - /** - * Returns the actual type for a given generic type name. - * - * @param currentType The current type to start with. - * @param genericTypeName The generic type name to resolve. - * @return The actual type. - */ - protected static Class getJavaActualType(Type currentType, String genericTypeName) { - Class result = null; - - if (currentType != null) { - if (currentType instanceof Class) { - // Look in the generic super class or the implemented interfaces - result = getJavaActualType((Class) currentType, genericTypeName); - } else if (currentType instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) currentType; - Class rawType = (Class) parameterizedType.getRawType(); - Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); - TypeVariable[] typeParameters = rawType.getTypeParameters(); - - for (int i = 0; (result == null) && (i < actualTypeArguments.length); i++) { - if (genericTypeName.equals(typeParameters[i].getName())) { - result = getTypeClass(actualTypeArguments[i]); - } - } - } - } - - return result; - } - - /** - * Returns the underlying class for a type or null. - * - * @param type The generic type. - * @return The underlying class - */ - protected static Class getTypeClass(Type type) { - Class result = null; - - if (type instanceof Class) { - result = (Class) type; - } else if (type instanceof ParameterizedType) { - result = getTypeClass(((ParameterizedType) type).getRawType()); - } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType) type).getGenericComponentType(); - Class componentClass = getTypeClass(componentType); - - if (componentClass != null) { - result = Array.newInstance(componentClass, 0).getClass(); - } - } - - return result; - } - - /** The raw annotation value. */ - protected final String annotationValue; - - /** The class that hosts the annotated Java method. */ - protected final Class javaClass; - - /** The annotated Java method. */ - protected final java.lang.reflect.Method javaMethod; - - /** The upper implementation of the annotated Java method. */ - protected final java.lang.reflect.Method javaMethodImpl; - - /** - * Constructor. - * - * @param javaClass The annotated Java class or parent Java class. - * @param javaMethod The annotated Java method. - * @param annotationValue The annotation value. - */ - public AnnotationInfo(Class javaClass, java.lang.reflect.Method javaMethod, String annotationValue) { - super(); - this.javaClass = javaClass; - this.javaMethod = javaMethod; - this.annotationValue = annotationValue; - java.lang.reflect.Method m = null; - - try { - m = javaClass.getMethod(javaMethod.getName(), javaMethod.getParameterTypes()); - } catch (Exception e) { - m = javaMethod; - } - - if (m != null) { - this.javaMethodImpl = m; - } else { - this.javaMethodImpl = javaMethod; - } - } - - /** - * Constructor. - * - * @param javaClass The annotated Java class or parent Java class. - * @param annotationValue The annotation value. - */ - public AnnotationInfo(Class javaClass, String annotationValue) { - this(javaClass, null, annotationValue); - } - - /** - * Indicates if the current variant is equal to the given object. - * - * @param other The other object. - * @return True if the current object is equal to the given object. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof AnnotationInfo)) { - return false; - } - - AnnotationInfo that = (AnnotationInfo) other; - - return Objects.equals(getJavaMethod(), that.getJavaMethod()) - && Objects.equals(getJavaClass(), that.getJavaClass()) - && Objects.equals(getAnnotationValue(), that.getAnnotationValue()); - } - - /** - * Returns the raw annotation value. - * - * @return The raw annotation value. - */ - public String getAnnotationValue() { - return annotationValue; - } - - /** - * Returns the actual type for a given generic type. - * - * @param initialType The initial type, which may be generic. - * @param genericType The generic type information if any. - * @return The actual type. - */ - protected Class getJavaActualType(Class initialType, Type genericType) { - Class result = initialType; - - try { - if (genericType instanceof TypeVariable genericTypeVariable) { - String genericTypeName = genericTypeVariable.getName(); - result = getJavaActualType(getJavaClass(), genericTypeName); - } - } catch (Throwable t) { - Context.getCurrentLogger().log(Level.WARNING, "Cannot get actual type of generic type: " + genericType, t); - } - - return result; - } - - /** - * Returns the resource interface value. - * - * @return The resource interface value. - */ - public Class getJavaClass() { - return javaClass; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(annotationValue, javaClass, javaMethod); - } - - /** - * Returns the annotated Java method. - * - * @return The annotated Java method. - */ - public java.lang.reflect.Method getJavaMethod() { - return javaMethod; - } - - @Override - public String toString() { - return "AnnotationInfo [javaMethod: " + javaMethod + ", javaClass: " + javaClass + ", value: " + annotationValue - + "]"; - } - -} \ No newline at end of file + /** + * Returns the actual type for a given generic type name. + * + * @param currentClass The current class to walk up. + * @param genericTypeName The generic type name to resolve. + * @return The actual type. + */ + protected static Class getJavaActualType(Class currentClass, String genericTypeName) { + Class result = null; + + // Lookup in the super class + result = getJavaActualType(currentClass.getGenericSuperclass(), genericTypeName); + + if (result == null) { + // Lookup at the implemented interfaces + Type[] interfaceTypes = currentClass.getGenericInterfaces(); + + for (int i = 0; (result == null) && (i < interfaceTypes.length); i++) { + result = getJavaActualType(interfaceTypes[i], genericTypeName); + } + } + + return result; + } + + /** + * Returns the actual type for a given generic type name. + * + * @param currentType The current type to start with. + * @param genericTypeName The generic type name to resolve. + * @return The actual type. + */ + protected static Class getJavaActualType(Type currentType, String genericTypeName) { + Class result = null; + + if (currentType != null) { + if (currentType instanceof Class) { + // Look in the generic super class or the implemented interfaces + result = getJavaActualType((Class) currentType, genericTypeName); + } else if (currentType instanceof final ParameterizedType parameterizedType) { + Class rawType = (Class) parameterizedType.getRawType(); + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + TypeVariable[] typeParameters = rawType.getTypeParameters(); + + for (int i = 0; (result == null) && (i < actualTypeArguments.length); i++) { + if (genericTypeName.equals(typeParameters[i].getName())) { + result = getTypeClass(actualTypeArguments[i]); + } + } + } + } + + return result; + } + + /** + * Returns the underlying class for a type or null. + * + * @param type The generic type. + * @return The underlying class + */ + protected static Class getTypeClass(Type type) { + Class result = null; + + if (type instanceof Class) { + result = (Class) type; + } else if (type instanceof ParameterizedType) { + result = getTypeClass(((ParameterizedType) type).getRawType()); + } else if (type instanceof GenericArrayType) { + Type componentType = ((GenericArrayType) type).getGenericComponentType(); + Class componentClass = getTypeClass(componentType); + + if (componentClass != null) { + result = Array.newInstance(componentClass, 0).getClass(); + } + } + + return result; + } + + /** The raw annotation value. */ + protected final String annotationValue; + + /** The class that hosts the annotated Java method. */ + protected final Class javaClass; + + /** The annotated Java method. */ + protected final java.lang.reflect.Method javaMethod; + + /** The upper implementation of the annotated Java method. */ + protected final java.lang.reflect.Method javaMethodImpl; + + /** + * Constructor. + * + * @param javaClass The annotated Java class or parent Java class. + * @param javaMethod The annotated Java method. + * @param annotationValue The annotation value. + */ + public AnnotationInfo( + Class javaClass, java.lang.reflect.Method javaMethod, String annotationValue) { + super(); + this.javaClass = javaClass; + this.javaMethod = javaMethod; + this.annotationValue = annotationValue; + java.lang.reflect.Method m = null; + + try { + m = javaClass.getMethod(javaMethod.getName(), javaMethod.getParameterTypes()); + } catch (Exception e) { + m = javaMethod; + } + + if (m != null) { + this.javaMethodImpl = m; + } else { + this.javaMethodImpl = javaMethod; + } + } + + /** + * Constructor. + * + * @param javaClass The annotated Java class or parent Java class. + * @param annotationValue The annotation value. + */ + public AnnotationInfo(Class javaClass, String annotationValue) { + this(javaClass, null, annotationValue); + } + + /** + * Indicates if the current variant is equal to the given object. + * + * @param other The other object. + * @return True if the current object is equal to the given object. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof final AnnotationInfo that)) { + return false; + } + + return Objects.equals(getJavaMethod(), that.getJavaMethod()) + && Objects.equals(getJavaClass(), that.getJavaClass()) + && Objects.equals(getAnnotationValue(), that.getAnnotationValue()); + } + + /** + * Returns the raw annotation value. + * + * @return The raw annotation value. + */ + public String getAnnotationValue() { + return annotationValue; + } + + /** + * Returns the actual type for a given generic type. + * + * @param initialType The initial type, which may be generic. + * @param genericType The generic type information, if any. + * @return The actual type. + */ + protected Class getJavaActualType(Class initialType, Type genericType) { + Class result = initialType; + + try { + if (genericType instanceof TypeVariable genericTypeVariable) { + String genericTypeName = genericTypeVariable.getName(); + result = getJavaActualType(getJavaClass(), genericTypeName); + } + } catch (Throwable t) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Cannot get actual type of generic type: " + genericType, + t); + } + + return result; + } + + /** + * Returns the resource interface value. + * + * @return The resource interface value. + */ + public Class getJavaClass() { + return javaClass; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode(annotationValue, javaClass, javaMethod); + } + + /** + * Returns the annotated Java method. + * + * @return The annotated Java method. + */ + public java.lang.reflect.Method getJavaMethod() { + return javaMethod; + } + + @Override + public String toString() { + return "AnnotationInfo [javaMethod: " + + javaMethod + + ", javaClass: " + + javaClass + + ", value: " + + annotationValue + + "]"; + } +} diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java index 5cecc99cd6..dbd830ff9e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java @@ -1,14 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.resource; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Context; import org.restlet.data.Form; import org.restlet.data.Method; @@ -17,13 +22,6 @@ import org.restlet.resource.Status; import org.restlet.service.MetadataService; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArrayList; - /** * Utilities to manipulate Restlet annotations. * @@ -31,311 +29,324 @@ */ public class AnnotationUtils { - /** Annotation info cache. */ - private static final ConcurrentMap, List> cache = new ConcurrentHashMap, List>(); - - /** Current instance. */ - private static AnnotationUtils instance = new AnnotationUtils(); - - /** Returns the current instance of AnnotationUtils. */ - public static AnnotationUtils getInstance() { - return instance; - } - - /** - * Protected constructor. - */ - protected AnnotationUtils() { - } - - /** - * Computes the annotation descriptors for the given class or interface. - * - * @param descriptors The annotation descriptors to update or null to create a - * new one. - * @param clazz The class or interface to introspect. - * @param initialClass The class or interface that runs the javaMethod. - * @return The annotation descriptors. - */ - private List addAnnotations(List descriptors, Class clazz, - Class initialClass) { - List result = descriptors; - - if (clazz != null && !ServerResource.class.equals(clazz)) { - // Add the annotation descriptor - if (result == null) { - result = new CopyOnWriteArrayList(); - } - - // Inspect the current class - addThrowableAnnotationDescriptors(result, clazz, initialClass); - - if (clazz.isInterface()) { - for (java.lang.reflect.Method javaMethod : clazz.getMethods()) { - addMethodAnnotationDescriptors(result, clazz, initialClass, javaMethod); - } - } else { - for (java.lang.reflect.Method javaMethod : clazz.getDeclaredMethods()) { - addMethodAnnotationDescriptors(result, clazz, initialClass, javaMethod); - } - } - - // Inspect the implemented interfaces for annotations - Class[] interfaces = clazz.getInterfaces(); - - if (interfaces != null) { - for (Class interfaceClass : interfaces) { - result = addAnnotations(result, interfaceClass, initialClass); - } - } - - // Add the annotations from the super class. - addAnnotations(result, clazz.getSuperclass(), initialClass); - } - - return result; - } - - /** - * Computes the annotation descriptors for the given Java method. - * - * @param descriptors The annotation descriptors to update or null to create a - * new one. - * @param clazz The class or interface that hosts the javaMethod. - * @param initialClass The class or interface that runs the javaMethod. - * @param javaMethod The Java method to inspect. - * @return The annotation descriptors. - */ - private List addMethodAnnotationDescriptors(List descriptors, Class clazz, - Class initialClass, java.lang.reflect.Method javaMethod) { - List result = descriptors; - - for (Annotation annotation : javaMethod.getAnnotations()) { - Annotation methodAnnotation = annotation.annotationType() - .getAnnotation(org.restlet.engine.connector.Method.class); - Method restletMethod = getRestletMethod(annotation, methodAnnotation); - - if (restletMethod != null) { - if (result == null) { - result = new CopyOnWriteArrayList(); - } - try { - java.lang.reflect.Method valueMethod = annotation.getClass().getDeclaredMethod("value"); - String value = (String) valueMethod.invoke(annotation); - - result.add(new MethodAnnotationInfo(initialClass, restletMethod, javaMethod, value)); - } catch (Exception exception) { - Context.getCurrentLogger().info("Cannot get value of Restlet annotation: " + annotation + " due to " - + exception.getMessage()); - } - } - } - - for (Class exceptionClass : javaMethod.getExceptionTypes()) { - for (Annotation annotation : exceptionClass.getAnnotations()) { - org.restlet.resource.Status statusAnnotation = annotation.annotationType() - .getAnnotation(org.restlet.resource.Status.class); - - if (statusAnnotation != null) { - int code = statusAnnotation.value(); - boolean serializable = statusAnnotation.serialize(); - - if (result == null) { - result = new CopyOnWriteArrayList(); - } - - result.add(new ThrowableAnnotationInfo(initialClass, code, serializable)); - } - } - } - - return result; - } - - /** - * Computes the annotation descriptors for the given Java method. - * - * @param descriptors The annotation descriptors to update or null to create a - * new one. - * @param clazz The class or interface that hosts the javaMethod. - * @param initialClass The class or interface that runs the javaMethod. - * @return The annotation descriptors. - */ - private List addThrowableAnnotationDescriptors(List descriptors, Class clazz, - Class initialClass) { - List result = descriptors; - Status status = clazz.getAnnotation(org.restlet.resource.Status.class); - - if (status != null) { - result.add(new ThrowableAnnotationInfo(initialClass, status.value(), status.serialize())); - } - - return result; - } - - /** - * Clears the annotation descriptors cache. - */ - public void clearCache() { - cache.clear(); - } - - /** - * Returns the annotation descriptors for the given resource class. - * - * @param clazz The resource class to introspect. - * @return The list of annotation descriptors. - */ - public synchronized List getAnnotations(Class clazz) { - List result = cache.get(clazz); - - if (result == null) { - // Inspect the class itself for annotations - result = addAnnotations(result, clazz, clazz); - - // Put the list in the cache if no one was previously present - List prev = cache.putIfAbsent(clazz, result); - - if (prev != null) { - // Reuse the previous entry - result = prev; - } - } - - return result; - } - - /** - * Returns the annotation descriptors for the given resource class. - * - * @param javaMethod The Java method. - * @return The list of annotation descriptors. - */ - public List getAnnotations(Class clazz, java.lang.reflect.Method javaMethod) { - return addMethodAnnotationDescriptors(null, clazz, clazz, javaMethod); - } - - /** - * Returns the first annotation descriptor matching the given Java method. - * - * @param annotations The list of annotations. - * @param javaMethod The method to match. - * @return The annotation descriptor. - */ - public MethodAnnotationInfo getMethodAnnotation(List annotations, - java.lang.reflect.Method javaMethod) { - if (annotations != null) { - for (AnnotationInfo annotationInfo : annotations) { - if (annotationInfo instanceof MethodAnnotationInfo - && annotationInfo.getJavaMethod().equals(javaMethod)) { - return (MethodAnnotationInfo) annotationInfo; - } - } - } - - return null; - } - - /** - * Returns the first annotation descriptor matching the given Restlet method. - * - * @param annotations The list of annotations. - * @param restletMethod The method to match. - * @param query The query parameters. - * @param entity The request entity to match or null if no entity is - * provided. - * @param metadataService The metadata service to use. - * @param converterService The converter service to use. - * @return The annotation descriptor. - * @throws IOException - */ - public MethodAnnotationInfo getMethodAnnotation(List annotations, Method restletMethod, Form query, - Representation entity, MetadataService metadataService, - org.restlet.service.ConverterService converterService) throws IOException { - if (annotations != null) { - for (AnnotationInfo annotationInfo : annotations) { - if (annotationInfo instanceof MethodAnnotationInfo) { - if (((MethodAnnotationInfo) annotationInfo).isCompatible(restletMethod, query, entity, - metadataService, converterService)) { - return (MethodAnnotationInfo) annotationInfo; - } - } - } - } - - return null; - } - - /** - * Returns an instance of {@link Method} according to the given annotations. - * - * @param annotation Java annotation. - * @param methodAnnotation Annotation that corresponds to a Restlet method. - * @return An instance of {@link Method} according to the given annotations. - */ - protected Method getRestletMethod(Annotation annotation, Annotation methodAnnotation) { - return (methodAnnotation == null) ? null - : Method.valueOf(((org.restlet.engine.connector.Method) methodAnnotation).value()); - } - - /** - * Returns the status annotation descriptor if present or null. - * - * @param clazz The class with the status attached. - * @return The status annotation descriptor if present or null. - */ - public ThrowableAnnotationInfo getThrowableAnnotationInfo(Class clazz) { - List annotationInfos = getAnnotations(clazz); - - for (AnnotationInfo annotationInfo : annotationInfos) { - if (annotationInfo instanceof ThrowableAnnotationInfo) { - return (ThrowableAnnotationInfo) annotationInfo; - } - } - - return null; - } - - /** - * Returns the {@link Throwable} class matching the given error code if present - * or null. - * - * @param javaMethod The method that holds {@link Throwable}. - * @param errorCode The error code to match. - * @return The {@link Throwable} class matching the given error code if present - * or null. - */ - public ThrowableAnnotationInfo getThrowableAnnotationInfo(java.lang.reflect.Method javaMethod, int errorCode) { - for (Class clazz : javaMethod.getExceptionTypes()) { - ThrowableAnnotationInfo tai = getThrowableAnnotationInfo(clazz); - - if (tai != null && tai.getStatus().getCode() == errorCode) { - return tai; - } - } - - return null; - } - - /** - * Returns the {@link Throwable} class matching the given error code if present - * or null. - * - * @param javaMethod The method that holds {@link Throwable}. - * @param errorCode The error code to match. - * @return The {@link Throwable} class matching the given error code if present - * or null. - */ - public Class getThrowableClass(java.lang.reflect.Method javaMethod, int errorCode) { - for (Class clazz : javaMethod.getExceptionTypes()) { - ThrowableAnnotationInfo tai = getThrowableAnnotationInfo(clazz); - - if (tai != null && tai.getStatus().getCode() == errorCode) { - return clazz; - } - } - - return null; - } - -} \ No newline at end of file + /** Annotation info cache. */ + private static final ConcurrentMap, List> cache = + new ConcurrentHashMap, List>(); + + /** Current instance. */ + private static final AnnotationUtils instance = new AnnotationUtils(); + + /** Returns the current instance of AnnotationUtils. */ + public static AnnotationUtils getInstance() { + return instance; + } + + /** Protected constructor. */ + protected AnnotationUtils() {} + + /** + * Computes the annotation descriptors for the given class or interface. + * + * @param descriptors The annotation descriptors to update or null to create a new one. + * @param clazz The class or interface to introspect. + * @param initialClass The class or interface that runs the javaMethod. + * @return The annotation descriptors. + */ + private List addAnnotations( + List descriptors, Class clazz, Class initialClass) { + List result = descriptors; + + if (clazz != null && !ServerResource.class.equals(clazz)) { + // Add the annotation descriptor + if (result == null) { + result = new CopyOnWriteArrayList<>(); + } + + // Inspect the current class + addThrowableAnnotationDescriptors(result, clazz, initialClass); + + if (clazz.isInterface()) { + for (java.lang.reflect.Method javaMethod : clazz.getMethods()) { + addMethodAnnotationDescriptors(result, clazz, initialClass, javaMethod); + } + } else { + for (java.lang.reflect.Method javaMethod : clazz.getDeclaredMethods()) { + addMethodAnnotationDescriptors(result, clazz, initialClass, javaMethod); + } + } + + // Inspect the implemented interfaces for annotations + Class[] interfaces = clazz.getInterfaces(); + + for (Class interfaceClass : interfaces) { + result = addAnnotations(result, interfaceClass, initialClass); + } + + // Add the annotations from the super class. + addAnnotations(result, clazz.getSuperclass(), initialClass); + } + + return result; + } + + /** + * Computes the annotation descriptors for the given Java method. + * + * @param descriptors The annotation descriptors to update or null to create a new one. + * @param clazz The class or interface that hosts the javaMethod. + * @param initialClass The class or interface that runs the javaMethod. + * @param javaMethod The Java method to inspect. + * @return The annotation descriptors. + */ + private List addMethodAnnotationDescriptors( + List descriptors, + Class clazz, + Class initialClass, + java.lang.reflect.Method javaMethod) { + List result = descriptors; + + for (Annotation annotation : javaMethod.getAnnotations()) { + Annotation methodAnnotation = + annotation + .annotationType() + .getAnnotation(org.restlet.engine.connector.Method.class); + Method restletMethod = getRestletMethod(annotation, methodAnnotation); + + if (restletMethod != null) { + if (result == null) { + result = new CopyOnWriteArrayList<>(); + } + try { + java.lang.reflect.Method valueMethod = + annotation.getClass().getDeclaredMethod("value"); + String value = (String) valueMethod.invoke(annotation); + + result.add( + new MethodAnnotationInfo( + initialClass, restletMethod, javaMethod, value)); + } catch (Exception exception) { + Context.getCurrentLogger() + .info( + "Cannot get value of Restlet annotation: " + + annotation + + " due to " + + exception.getMessage()); + } + } + } + + for (Class exceptionClass : javaMethod.getExceptionTypes()) { + for (Annotation annotation : exceptionClass.getAnnotations()) { + org.restlet.resource.Status statusAnnotation = + annotation + .annotationType() + .getAnnotation(org.restlet.resource.Status.class); + + if (statusAnnotation != null) { + int code = statusAnnotation.value(); + boolean serializable = statusAnnotation.serialize(); + + if (result == null) { + result = new CopyOnWriteArrayList<>(); + } + + result.add(new ThrowableAnnotationInfo(initialClass, code, serializable)); + } + } + } + + return result; + } + + /** + * Computes the annotation descriptors for the given Java method. + * + * @param descriptors The annotation descriptors to update or null to create a new one. + * @param clazz The class or interface that hosts the javaMethod. + * @param initialClass The class or interface that runs the javaMethod. + * @return The annotation descriptors. + */ + private List addThrowableAnnotationDescriptors( + List descriptors, Class clazz, Class initialClass) { + List result = descriptors; + Status status = clazz.getAnnotation(org.restlet.resource.Status.class); + + if (status != null) { + result.add( + new ThrowableAnnotationInfo(initialClass, status.value(), status.serialize())); + } + + return result; + } + + /** Clears the annotation descriptors cache. */ + public void clearCache() { + cache.clear(); + } + + /** + * Returns the annotation descriptors for the given resource class. + * + * @param clazz The resource class to introspect. + * @return The list of annotation descriptors. + */ + public synchronized List getAnnotations(Class clazz) { + List result = cache.get(clazz); + + if (result == null) { + // Inspect the class itself for annotations + result = addAnnotations(result, clazz, clazz); + + // Put the list in the cache if no one was previously present + List prev = cache.putIfAbsent(clazz, result); + + if (prev != null) { + // Reuse the previous entry + result = prev; + } + } + + return result; + } + + /** + * Returns the annotation descriptors for the given resource class. + * + * @param javaMethod The Java method. + * @return The list of annotation descriptors. + */ + public List getAnnotations( + Class clazz, java.lang.reflect.Method javaMethod) { + return addMethodAnnotationDescriptors(null, clazz, clazz, javaMethod); + } + + /** + * Returns the first annotation descriptor matching the given Java method. + * + * @param annotations The list of annotations. + * @param javaMethod The method to match. + * @return The annotation descriptor. + */ + public MethodAnnotationInfo getMethodAnnotation( + List annotations, java.lang.reflect.Method javaMethod) { + if (annotations != null) { + for (AnnotationInfo annotationInfo : annotations) { + if (annotationInfo instanceof MethodAnnotationInfo + && annotationInfo.getJavaMethod().equals(javaMethod)) { + return (MethodAnnotationInfo) annotationInfo; + } + } + } + + return null; + } + + /** + * Returns the first annotation descriptor matching the given Restlet method. + * + * @param annotations The list of annotations. + * @param restletMethod The method to match. + * @param query The query parameters. + * @param entity The request entity to match or null if no entity is provided. + * @param metadataService The metadata service to use. + * @param converterService The converter service to use. + * @return The annotation descriptor. + * @throws IOException + */ + public MethodAnnotationInfo getMethodAnnotation( + List annotations, + Method restletMethod, + Form query, + Representation entity, + MetadataService metadataService, + org.restlet.service.ConverterService converterService) + throws IOException { + if (annotations != null) { + for (AnnotationInfo annotationInfo : annotations) { + if (annotationInfo instanceof MethodAnnotationInfo) { + if (((MethodAnnotationInfo) annotationInfo) + .isCompatible( + restletMethod, + query, + entity, + metadataService, + converterService)) { + return (MethodAnnotationInfo) annotationInfo; + } + } + } + } + + return null; + } + + /** + * Returns an instance of {@link Method} according to the given annotations. + * + * @param annotation Java annotation. + * @param methodAnnotation Annotation that corresponds to a Restlet method. + * @return An instance of {@link Method} according to the given annotations. + */ + protected Method getRestletMethod(Annotation annotation, Annotation methodAnnotation) { + return (methodAnnotation == null) + ? null + : Method.valueOf(((org.restlet.engine.connector.Method) methodAnnotation).value()); + } + + /** + * Returns the status annotation descriptor if present or null. + * + * @param clazz The class with the status attached. + * @return The status annotation descriptor if present or null. + */ + public ThrowableAnnotationInfo getThrowableAnnotationInfo(Class clazz) { + List annotationInfos = getAnnotations(clazz); + + for (AnnotationInfo annotationInfo : annotationInfos) { + if (annotationInfo instanceof ThrowableAnnotationInfo) { + return (ThrowableAnnotationInfo) annotationInfo; + } + } + + return null; + } + + /** + * Returns the {@link Throwable} class matching the given error code if present or null. + * + * @param javaMethod The method that holds {@link Throwable}. + * @param errorCode The error code to match. + * @return The {@link Throwable} class matching the given error code if present or null. + */ + public ThrowableAnnotationInfo getThrowableAnnotationInfo( + java.lang.reflect.Method javaMethod, int errorCode) { + for (Class clazz : javaMethod.getExceptionTypes()) { + ThrowableAnnotationInfo tai = getThrowableAnnotationInfo(clazz); + + if (tai != null && tai.getStatus().getCode() == errorCode) { + return tai; + } + } + + return null; + } + + /** + * Returns the {@link Throwable} class matching the given error code if present or null. + * + * @param javaMethod The method that holds {@link Throwable}. + * @param errorCode The error code to match. + * @return The {@link Throwable} class matching the given error code if present or null. + */ + public Class getThrowableClass(java.lang.reflect.Method javaMethod, int errorCode) { + for (Class clazz : javaMethod.getExceptionTypes()) { + ThrowableAnnotationInfo tai = getThrowableAnnotationInfo(clazz); + + if (tai != null && tai.getStatus().getCode() == errorCode) { + return clazz; + } + } + + return null; + } +} diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java b/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java index f067899631..f6ccbb2cfd 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java @@ -1,14 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.resource; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -24,259 +29,289 @@ import org.restlet.resource.ResourceException; import org.restlet.resource.Result; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.List; -import java.util.logging.Level; - /** - * Reflection proxy invocation handler created for the - * {@link ClientResource#wrap(Class)} and related methods. - * + * Reflection proxy invocation handler created for the {@link ClientResource#wrap(Class)} and + * related methods. + * * @author Jerome Louvel - * * @param The annotated resource interface. */ public class ClientInvocationHandler implements InvocationHandler { - /** The annotations of the resource interface. */ - private final List annotations; - - /** The associated annotation utils. */ - private AnnotationUtils annotationUtils; - - /** The associated client resource. */ - private final ClientResource clientResource; - - /** - * Constructor. - * - * @param clientResource The associated client resource. - * @param resourceInterface The annotated resource interface. - */ - public ClientInvocationHandler(ClientResource clientResource, Class resourceInterface) { - this(clientResource, resourceInterface, AnnotationUtils.getInstance()); - } - - /** - * Constructor. - * - * @param clientResource The associated client resource. - * @param resourceInterface The annotated resource interface. - * @param annotationUtils The annotationUtils class. - */ - public ClientInvocationHandler(ClientResource clientResource, Class resourceInterface, - AnnotationUtils annotationUtils) { - this.clientResource = clientResource; - this.annotationUtils = annotationUtils; - - // Introspect the interface for Restlet annotations - this.annotations = getAnnotationUtils().getAnnotations(resourceInterface); - } - - /** - * Returns the annotations of the resource interface. - * - * @return The annotations of the resource interface. - */ - public List getAnnotations() { - return annotations; - } - - /** - * Returns the associated annotation utils. - * - * @return The associated annotation utils. - */ - public AnnotationUtils getAnnotationUtils() { - return annotationUtils; - } - - /** - * Returns the associated client resource. - * - * @return The associated client resource. - */ - public ClientResource getClientResource() { - return clientResource; - } - - /** - * Allows for child classes to modify the request. - */ - protected Request getRequest(Method javaMethod, Object[] args) throws Throwable { - return getClientResource().createRequest(); - } - - /** - * Effectively invokes a Java method on the given proxy object. - */ - @SuppressWarnings("rawtypes") - public Object invoke(Object proxy, java.lang.reflect.Method javaMethod, Object[] args) throws Throwable { - Object result = null; - - if (javaMethod.equals(Object.class.getMethod("toString"))) { - // Help debug - result = "ClientProxy for resource: " + clientResource; - } else if (javaMethod.equals(ClientProxy.class.getMethod("getClientResource"))) { - result = clientResource; - } else { - MethodAnnotationInfo annotationInfo = getAnnotationUtils().getMethodAnnotation(getAnnotations(), - javaMethod); - - if (annotationInfo != null) { - Representation requestEntity = null; - - if ((args != null) && args.length > 0) { - // Checks if the user has defined its own callback. - for (int i = 0; i < args.length; i++) { - Object o = args[i]; - - if (o == null) { - requestEntity = null; - } else if (Result.class.isAssignableFrom(o.getClass())) { - // Asynchronous mode where a callback object is to - // be called. - - // Get the kind of result expected. - final Result rCallback = (Result) o; - Type[] genericParameterTypes = javaMethod.getGenericParameterTypes(); - Type genericParameterType = genericParameterTypes[i]; - ParameterizedType parameterizedType = (genericParameterType instanceof java.lang.reflect.ParameterizedType) - ? (java.lang.reflect.ParameterizedType) genericParameterType - : null; - final Class actualType = (parameterizedType != null - && parameterizedType.getActualTypeArguments()[0] instanceof Class) - ? (Class) parameterizedType.getActualTypeArguments()[0] - : null; - - // Define the callback - Uniform callback = new Uniform() { - @SuppressWarnings("unchecked") - public void handle(Request request, Response response) { - if (response.getStatus().isError()) { - rCallback.onFailure(new ResourceException(response.getStatus())); - } else { - if (actualType != null) { - Object result = null; - boolean serializationError = false; - - try { - result = getClientResource().toObject(response.getEntity(), actualType); - } catch (Exception e) { - serializationError = true; - rCallback.onFailure(new ResourceException(e)); - } - - if (!serializationError) { - rCallback.onSuccess(result); - } - } else { - rCallback.onSuccess(null); - } - } - } - }; - - getClientResource().setOnResponse(callback); - } else { - requestEntity = getClientResource().toRepresentation(o); - } - } - } - - // Clone the prototype request - Request request = getRequest(javaMethod, args); - - // The Java method was annotated - request.setMethod(annotationInfo.getRestletMethod()); - - // Add the mandatory query parameters - String query = annotationInfo.getQuery(); - - if (query != null) { - Form queryParams = new Form(annotationInfo.getQuery()); - request.getResourceRef().addQueryParameters(queryParams); - } - - // Set the entity - request.setEntity(requestEntity); - - // Updates the client preferences if they weren't changed - if ((request.getClientInfo().getAcceptedCharacterSets().isEmpty()) - && (request.getClientInfo().getAcceptedEncodings().isEmpty()) - && (request.getClientInfo().getAcceptedLanguages().isEmpty()) - && (request.getClientInfo().getAcceptedMediaTypes().isEmpty())) { - List responseVariants = annotationInfo.getResponseVariants( - getClientResource().getMetadataService(), getClientResource().getConverterService()); - - if (responseVariants != null) { - request.setClientInfo(new ClientInfo(responseVariants)); - } - } - - // Effectively handle the call - Response response = getClientResource().handleOutbound(request); - - // Handle the response, synchronous call - if (getClientResource().getOnResponse() == null) { - if ((response != null) && response.getStatus().isError()) { - ThrowableAnnotationInfo tai = getAnnotationUtils().getThrowableAnnotationInfo(javaMethod, - response.getStatus().getCode()); - - if (tai != null) { - Class throwableClazz = tai.getJavaClass(); - Throwable t = null; - - if (tai.isSerializable() && response.isEntityAvailable()) { - t = (Throwable) getClientResource().toObject(response.getEntity(), throwableClazz); - } else { - try { - t = (Throwable) throwableClazz.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.FINE, - "Unable to instantiate the client-side exception using the default constructor."); - } - - if (response.isEntityAvailable()) { - StatusInfo si = getClientResource().toObject(response.getEntity(), - StatusInfo.class); - - if (si != null) { - response.setStatus( - new Status(si.getCode(), si.getReasonPhrase(), si.getDescription())); - } - } - } - - if (t != null) { - throw t; - } - // TODO cf issues 1004 and 1018. - // this code has been commented as the automatic - // deserialization is problematic. We may rethink a - // way to recover the status info. - // } else if (response.isEntityAvailable()) { - // StatusInfo si = getClientResource().toObject( - // response.getEntity(), StatusInfo.class); - // - // if (si != null) { - // response.setStatus(new Status(si.getCode(), si - // .getReasonPhrase(), si.getDescription())); - // } - } - - getClientResource().doError(response.getStatus()); - } else if (!annotationInfo.getJavaOutputType().equals(void.class)) { - result = getClientResource().toObject((response == null ? null : response.getEntity()), - annotationInfo.getJavaOutputType()); - } - } - } - } - - return result; - } + /** The annotations of the resource interface. */ + private final List annotations; + + /** The associated annotation utils. */ + private final AnnotationUtils annotationUtils; + + /** The associated client resource. */ + private final ClientResource clientResource; + + /** + * Constructor. + * + * @param clientResource The associated client resource. + * @param resourceInterface The annotated resource interface. + */ + public ClientInvocationHandler( + ClientResource clientResource, Class resourceInterface) { + this(clientResource, resourceInterface, AnnotationUtils.getInstance()); + } + + /** + * Constructor. + * + * @param clientResource The associated client resource. + * @param resourceInterface The annotated resource interface. + * @param annotationUtils The annotationUtils class. + */ + public ClientInvocationHandler( + ClientResource clientResource, + Class resourceInterface, + AnnotationUtils annotationUtils) { + this.clientResource = clientResource; + this.annotationUtils = annotationUtils; + + // Introspect the interface for Restlet annotations + this.annotations = getAnnotationUtils().getAnnotations(resourceInterface); + } + + /** + * Returns the annotations of the resource interface. + * + * @return The annotations of the resource interface. + */ + public List getAnnotations() { + return annotations; + } + + /** + * Returns the associated annotation utils. + * + * @return The associated annotation utils. + */ + public AnnotationUtils getAnnotationUtils() { + return annotationUtils; + } + + /** + * Returns the associated client resource. + * + * @return The associated client resource. + */ + public ClientResource getClientResource() { + return clientResource; + } + + /** Allows for child classes to modify the request. */ + protected Request getRequest(Method javaMethod, Object[] args) throws Throwable { + return getClientResource().createRequest(); + } + + /** Effectively invokes a Java method on the given proxy object. */ + @SuppressWarnings("rawtypes") + public Object invoke(Object proxy, java.lang.reflect.Method javaMethod, Object[] args) + throws Throwable { + Object result = null; + + if (javaMethod.equals(Object.class.getMethod("toString"))) { + // Help debug + result = "ClientProxy for resource: " + clientResource; + } else if (javaMethod.equals(ClientProxy.class.getMethod("getClientResource"))) { + result = clientResource; + } else { + MethodAnnotationInfo annotationInfo = + getAnnotationUtils().getMethodAnnotation(getAnnotations(), javaMethod); + + if (annotationInfo != null) { + Representation requestEntity = null; + + if ((args != null) && args.length > 0) { + // Checks if the user has defined its own callback. + for (int i = 0; i < args.length; i++) { + Object o = args[i]; + + if (o == null) { + requestEntity = null; + } else if (Result.class.isAssignableFrom(o.getClass())) { + // Asynchronous mode where a callback object is to + // be called. + + // Get the kind of result expected. + final Result rCallback = (Result) o; + Type[] genericParameterTypes = javaMethod.getGenericParameterTypes(); + Type genericParameterType = genericParameterTypes[i]; + ParameterizedType parameterizedType = + (genericParameterType + instanceof java.lang.reflect.ParameterizedType) + ? (java.lang.reflect.ParameterizedType) + genericParameterType + : null; + final Class actualType = + (parameterizedType != null + && parameterizedType.getActualTypeArguments()[0] + instanceof Class) + ? (Class) + parameterizedType.getActualTypeArguments()[0] + : null; + + // Define the callback + Uniform callback = + new Uniform() { + @SuppressWarnings("unchecked") + public void handle(Request request, Response response) { + if (response.getStatus().isError()) { + rCallback.onFailure( + new ResourceException( + response.getStatus())); + } else { + if (actualType != null) { + Object result = null; + boolean serializationError = false; + + try { + result = + getClientResource() + .toObject( + response + .getEntity(), + actualType); + } catch (Exception e) { + serializationError = true; + rCallback.onFailure( + new ResourceException(e)); + } + + if (!serializationError) { + rCallback.onSuccess(result); + } + } else { + rCallback.onSuccess(null); + } + } + } + }; + + getClientResource().setOnResponse(callback); + } else { + requestEntity = getClientResource().toRepresentation(o); + } + } + } + + // Clone the prototype request + Request request = getRequest(javaMethod, args); + + // The Java method was annotated + request.setMethod(annotationInfo.getRestletMethod()); + + // Add the mandatory query parameters + String query = annotationInfo.getQuery(); + + if (query != null) { + Form queryParams = new Form(annotationInfo.getQuery()); + request.getResourceRef().addQueryParameters(queryParams); + } + + // Set the entity + request.setEntity(requestEntity); + + // Updates the client preferences if they weren't changed + if ((request.getClientInfo().getAcceptedCharacterSets().isEmpty()) + && (request.getClientInfo().getAcceptedEncodings().isEmpty()) + && (request.getClientInfo().getAcceptedLanguages().isEmpty()) + && (request.getClientInfo().getAcceptedMediaTypes().isEmpty())) { + List responseVariants = + annotationInfo.getResponseVariants( + getClientResource().getMetadataService(), + getClientResource().getConverterService()); + + if (responseVariants != null) { + request.setClientInfo(new ClientInfo(responseVariants)); + } + } + + // Effectively handle the call + Response response = getClientResource().handleOutbound(request); + + // Handle the response, synchronous call + if (getClientResource().getOnResponse() == null) { + if ((response != null) && response.getStatus().isError()) { + ThrowableAnnotationInfo tai = + getAnnotationUtils() + .getThrowableAnnotationInfo( + javaMethod, response.getStatus().getCode()); + + if (tai != null) { + Class throwableClazz = tai.getJavaClass(); + Throwable t = null; + + if (tai.isSerializable() && response.isEntityAvailable()) { + t = + (Throwable) + getClientResource() + .toObject( + response.getEntity(), + throwableClazz); + } else { + try { + t = + (Throwable) + throwableClazz + .getDeclaredConstructor() + .newInstance(); + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.FINE, + "Unable to instantiate the client-side exception using the default constructor."); + } + + if (response.isEntityAvailable()) { + StatusInfo si = + getClientResource() + .toObject( + response.getEntity(), StatusInfo.class); + + if (si != null) { + response.setStatus( + new Status( + si.getCode(), + si.getReasonPhrase(), + si.getDescription())); + } + } + } + + if (t != null) { + throw t; + } + // TODO cf issues 1004 and 1018. + // this code has been commented as the automatic + // deserialization is problematic. We may rethink a + // way to recover the status info. + // } else if (response.isEntityAvailable()) { + // StatusInfo si = getClientResource().toObject( + // response.getEntity(), StatusInfo.class); + // + // if (si != null) { + // response.setStatus(new Status(si.getCode(), si + // .getReasonPhrase(), si.getDescription())); + // } + } + + getClientResource().doError(response.getStatus()); + } else if (!annotationInfo.getJavaOutputType().equals(void.class)) { + result = + getClientResource() + .toObject( + (response == null ? null : response.getEntity()), + annotationInfo.getJavaOutputType()); + } + } + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java index 43d91bab05..0eff3f1589 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java @@ -1,25 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.resource; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.StringTokenizer; import org.restlet.Context; -import org.restlet.data.*; +import org.restlet.data.CharacterSet; +import org.restlet.data.Form; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Method; +import org.restlet.data.Parameter; import org.restlet.engine.util.StringUtils; import org.restlet.engine.util.SystemUtils; import org.restlet.representation.Representation; import org.restlet.representation.Variant; import org.restlet.service.MetadataService; -import java.io.IOException; -import java.util.*; - /** * Descriptor for method annotations. * @@ -27,371 +35,392 @@ */ public class MethodAnnotationInfo extends AnnotationInfo { - /** The input part of the annotation value. */ - private final String input; - - /** The output part of the annotation value. */ - private final String output; - - /** The optional query part of the annotation value. */ - private final String query; - - /** The matching Restlet method. */ - private final Method restletMethod; - - /** - * Constructor. - * - * @param javaClass The class or interface that hosts the annotated Java - * method. - * @param restletMethod The matching Restlet method. - * @param javaMethod The annotated Java method. - * @param annotationValue The annotation value. - */ - public MethodAnnotationInfo(Class javaClass, Method restletMethod, java.lang.reflect.Method javaMethod, - String annotationValue) { - super(javaClass, javaMethod, annotationValue); - this.restletMethod = restletMethod; - - // Parse the main components of the annotation value - if (!StringUtils.isNullOrEmpty(annotationValue)) { - int queryIndex = annotationValue.indexOf('?'); - - if (queryIndex != -1) { - this.query = annotationValue.substring(queryIndex + 1); - annotationValue = annotationValue.substring(0, queryIndex); - } else { - this.query = null; - } - - int ioSeparatorIndex = annotationValue.indexOf(':'); - - if (ioSeparatorIndex != -1) { - this.input = annotationValue.substring(0, ioSeparatorIndex); - this.output = annotationValue.substring(ioSeparatorIndex + 1); - } else { - this.input = annotationValue; - this.output = annotationValue; - } - - } else { - this.query = null; - this.input = null; - this.output = null; - } - } - - /** - * Indicates if the current object is equal to the given object. - * - * @param other The other object. - * @return True if the current object includes the other. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof MethodAnnotationInfo)) { - return false; - } - - MethodAnnotationInfo that = (MethodAnnotationInfo) other; - - return super.equals(that) && Objects.equals(getRestletMethod(), that.getRestletMethod()); - } - - /** - * Returns the input part of the annotation value. - * - * @return The input part of the annotation value. - */ - public String getInput() { - return input; - } - - /** - * Returns the generic type for the given input parameter. - * - * @param index The input parameter index. - * @return The generic type. - */ - private Class getJavaInputType(int index) { - return getJavaActualType(javaMethodImpl.getParameterTypes()[index], - javaMethodImpl.getGenericParameterTypes()[index]); - } - - /** - * Returns the input types of the Java method. - * - * @return The input types of the Java method. - */ - public Class[] getJavaInputTypes() { - int count = getJavaMethod().getParameterTypes().length; - Class[] classes = new Class[count]; - - for (int i = 0; i < count; i++) { - classes[i] = getJavaInputType(i); - } - - return classes; - } - - /** - * Returns the output type of the Java method. - * - * @return The output type of the Java method. - */ - public Class getJavaOutputType() { - return getJavaActualType(javaMethodImpl.getReturnType(), javaMethodImpl.getGenericReturnType()); - } - - /** - * Returns the output part of the annotation value. - * - * @return The output part of the annotation value. - */ - public String getOutput() { - return output; - } - - /** - * Returns the optional query part of the annotation value. - * - * @return The optional query part of the annotation value. - */ - public String getQuery() { - return query; - } - - /** - * Returns a list of request variants based on the annotation value. - * - * @param metadataService The metadata service to use. - * @return A list of request variants. - * @throws IOException - */ - @SuppressWarnings("unchecked") - public List getRequestVariants(MetadataService metadataService, - org.restlet.service.ConverterService converterService) throws IOException { - List result = null; - Class[] classes = getJavaInputTypes(); - - if (classes != null && classes.length >= 1) { - result = getVariants(metadataService, getInput()); - - if (result == null) { - Class inputClass = classes[0]; - - if (inputClass != null) { - result = (List) converterService.getVariants(inputClass, null); - } - } - } - - return result; - } - - /** - * Returns a list of response variants based on the annotation value. - * - * @param metadataService The metadata service to use. - * @param converterService The converter service to use. - * @return A list of response variants. - * @throws IOException - */ - @SuppressWarnings("unchecked") - public List getResponseVariants(MetadataService metadataService, - org.restlet.service.ConverterService converterService) throws IOException { - List result = null; - - if ((getJavaOutputType() != null) && (getJavaOutputType() != void.class) - && (getJavaOutputType() != Void.class)) { - result = getVariants(metadataService, getOutput()); - - if (result == null) { - result = (List) converterService.getVariants(getJavaOutputType(), null); - } - } - - return result; - } - - /** - * Returns the matching Restlet method. - * - * @return The matching Restlet method. - */ - public Method getRestletMethod() { - return restletMethod; - } - - /** - * Returns the list of representation variants associated to a given annotation - * value, corresponding to either an input or output entity. - * - * @param metadataService The metadata service to use. - * @param annotationValue The entity annotation value. - * @return A list of variants. - */ - private List getVariants(MetadataService metadataService, String annotationValue) { - List result = null; - - if (annotationValue != null) { - - StringTokenizer stValue = new StringTokenizer(annotationValue, "\\|"); - while (stValue.hasMoreTokens()) { - String variantValue = stValue.nextToken().trim(); - - Variant variant = null; - List mediaTypes = null; - List languages = null; - CharacterSet characterSet = null; - - StringTokenizer stExtension = new StringTokenizer(variantValue, "\\+"); - while (stExtension.hasMoreTokens()) { - String extension = stExtension.nextToken().trim(); - if (extension.isEmpty()) { - continue; - } - - List metadataList = metadataService.getAllMetadata(extension); - - if (metadataList != null) { - for (Metadata metadata : metadataList) { - if (metadata instanceof MediaType) { - if (mediaTypes == null) { - mediaTypes = new ArrayList<>(); - } - - mediaTypes.add((MediaType) metadata); - } else if (metadata instanceof Language) { - if (languages == null) { - languages = new ArrayList<>(); - } - - languages.add((Language) metadata); - } else if (metadata instanceof CharacterSet) { - if (characterSet == null) { - characterSet = (CharacterSet) metadata; - } else { - Context.getCurrentLogger().warning( - "A representation variant can have only one character set. Please check your annotation value."); - } - } - } - } - } - - // Now build the representation variants - if (mediaTypes != null) { - for (MediaType mediaType : mediaTypes) { - if ((result == null) || (!result.contains(mediaType))) { - if (result == null) { - result = new ArrayList<>(); - } - - variant = new Variant(mediaType); - - if (languages != null) { - variant.getLanguages().addAll(languages); - } - - if (characterSet != null) { - variant.setCharacterSet(characterSet); - } - - result.add(variant); - } - } - } - } - } - - return result; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(super.hashCode(), restletMethod); - } - - /** - * Indicates if the annotated method described is compatible with the given - * parameters. - * - * @param restletMethod The Restlet method to match. - * @param requestEntity Optional request entity. - * @param metadataService The metadata service to use. - * @param converterService The converter service to use. - * @return True if the annotated method is compatible. - * @throws IOException - */ - public boolean isCompatible(Method restletMethod, Form queryParams, Representation requestEntity, - MetadataService metadataService, org.restlet.service.ConverterService converterService) throws IOException { - boolean result = true; - - // Verify query parameters - if (getQuery() != null) { - Form requiredParams = new Form(getQuery()); - - for (Iterator iter = requiredParams.iterator(); iter.hasNext() && result;) { - result = queryParams.contains(iter.next()); - } - } - - // Verify HTTP method - if (result) { - result = getRestletMethod().equals(restletMethod); - } - - // Verify request entity - if (result) { - result = isCompatibleRequestEntity(requestEntity, metadataService, converterService); - - } - - return result; - } - - /** - * Indicates if the given request entity is compatible with the annotated method - * described. - * - * @param requestEntity Optional request entity. - * @param metadataService The metadata service to use. - * @param converterService The converter service to use. - * @return True if the given request entity is compatible with the annotated - * method described. - * @throws IOException - */ - public boolean isCompatibleRequestEntity(Representation requestEntity, MetadataService metadataService, - org.restlet.service.ConverterService converterService) throws IOException { - boolean result = true; - - if ((requestEntity != null) && requestEntity.isAvailable()) { - List requestVariants = getRequestVariants(metadataService, converterService); - - if ((requestVariants != null) && !requestVariants.isEmpty()) { - // Check that the compatibility - result = false; - - for (int i = 0; (!result) && (i < requestVariants.size()); i++) { - result = (requestVariants.get(i).isCompatible(requestEntity)); - } - } else { - result = false; - } - } - - return result; - } - - @Override - public String toString() { - return "MethodAnnotationInfo [javaMethod: " + javaMethod + ", javaClass: " + getJavaClass() - + ", restletMethod: " + restletMethod + ", input: " + getInput() + ", value: " + getAnnotationValue() - + ", output: " + getOutput() + ", query: " + getQuery() + "]"; - } - + /** The input part of the annotation value. */ + private final String input; + + /** The output part of the annotation value. */ + private final String output; + + /** The optional query part of the annotation value. */ + private final String query; + + /** The matching Restlet method. */ + private final Method restletMethod; + + /** + * Constructor. + * + * @param javaClass The class or interface that hosts the annotated Java method. + * @param restletMethod The matching Restlet method. + * @param javaMethod The annotated Java method. + * @param annotationValue The annotation value. + */ + public MethodAnnotationInfo( + Class javaClass, + Method restletMethod, + java.lang.reflect.Method javaMethod, + String annotationValue) { + super(javaClass, javaMethod, annotationValue); + this.restletMethod = restletMethod; + + // Parse the main parts of the annotation value + if (!StringUtils.isNullOrEmpty(annotationValue)) { + int queryIndex = annotationValue.indexOf('?'); + + if (queryIndex != -1) { + this.query = annotationValue.substring(queryIndex + 1); + annotationValue = annotationValue.substring(0, queryIndex); + } else { + this.query = null; + } + + int ioSeparatorIndex = annotationValue.indexOf(':'); + + if (ioSeparatorIndex != -1) { + this.input = annotationValue.substring(0, ioSeparatorIndex); + this.output = annotationValue.substring(ioSeparatorIndex + 1); + } else { + this.input = annotationValue; + this.output = annotationValue; + } + + } else { + this.query = null; + this.input = null; + this.output = null; + } + } + + /** + * Indicates if the current object is equal to the given object. + * + * @param other The other object. + * @return True if the current object includes the other. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof final MethodAnnotationInfo that)) { + return false; + } + + return super.equals(that) && Objects.equals(getRestletMethod(), that.getRestletMethod()); + } + + /** + * Returns the input part of the annotation value. + * + * @return The input part of the annotation value. + */ + public String getInput() { + return input; + } + + /** + * Returns the generic type for the given input parameter. + * + * @param index The input parameter index. + * @return The generic type. + */ + private Class getJavaInputType(int index) { + return getJavaActualType( + javaMethodImpl.getParameterTypes()[index], + javaMethodImpl.getGenericParameterTypes()[index]); + } + + /** + * Returns the input types of the Java method. + * + * @return The input types of the Java method. + */ + public Class[] getJavaInputTypes() { + int count = getJavaMethod().getParameterTypes().length; + Class[] classes = new Class[count]; + + for (int i = 0; i < count; i++) { + classes[i] = getJavaInputType(i); + } + + return classes; + } + + /** + * Returns the output type of the Java method. + * + * @return The output type of the Java method. + */ + public Class getJavaOutputType() { + return getJavaActualType( + javaMethodImpl.getReturnType(), javaMethodImpl.getGenericReturnType()); + } + + /** + * Returns the output part of the annotation value. + * + * @return The output part of the annotation value. + */ + public String getOutput() { + return output; + } + + /** + * Returns the optional query part of the annotation value. + * + * @return The optional query part of the annotation value. + */ + public String getQuery() { + return query; + } + + /** + * Returns a list of request variants based on the annotation value. + * + * @param metadataService The metadata service to use. + * @return A list of request variants. + * @throws IOException + */ + @SuppressWarnings("unchecked") + public List getRequestVariants( + MetadataService metadataService, org.restlet.service.ConverterService converterService) + throws IOException { + List result = null; + Class[] classes = getJavaInputTypes(); + + if (classes != null && classes.length >= 1) { + result = getVariants(metadataService, getInput()); + + if (result == null) { + Class inputClass = classes[0]; + + if (inputClass != null) { + result = (List) converterService.getVariants(inputClass, null); + } + } + } + + return result; + } + + /** + * Returns a list of response variants based on the annotation value. + * + * @param metadataService The metadata service to use. + * @param converterService The converter service to use. + * @return A list of response variants. + * @throws IOException + */ + @SuppressWarnings("unchecked") + public List getResponseVariants( + MetadataService metadataService, org.restlet.service.ConverterService converterService) + throws IOException { + List result = null; + + if ((getJavaOutputType() != null) + && (getJavaOutputType() != void.class) + && (getJavaOutputType() != Void.class)) { + result = getVariants(metadataService, getOutput()); + + if (result == null) { + result = (List) converterService.getVariants(getJavaOutputType(), null); + } + } + + return result; + } + + /** + * Returns the matching Restlet method. + * + * @return The matching Restlet method. + */ + public Method getRestletMethod() { + return restletMethod; + } + + /** + * Returns the list of representation variants associated with a given annotation value, + * corresponding to either an input or output entity. + * + * @param metadataService The metadata service to use. + * @param annotationValue The entity annotation value. + * @return A list of variants. + */ + private List getVariants(MetadataService metadataService, String annotationValue) { + List result = null; + + if (annotationValue != null) { + + StringTokenizer stValue = new StringTokenizer(annotationValue, "\\|"); + while (stValue.hasMoreTokens()) { + String variantValue = stValue.nextToken().trim(); + + Variant variant = null; + List mediaTypes = null; + List languages = null; + CharacterSet characterSet = null; + + StringTokenizer stExtension = new StringTokenizer(variantValue, "\\+"); + while (stExtension.hasMoreTokens()) { + String extension = stExtension.nextToken().trim(); + if (extension.isEmpty()) { + continue; + } + + List metadataList = metadataService.getAllMetadata(extension); + + if (metadataList != null) { + for (Metadata metadata : metadataList) { + if (metadata instanceof MediaType) { + if (mediaTypes == null) { + mediaTypes = new ArrayList<>(); + } + + mediaTypes.add((MediaType) metadata); + } else if (metadata instanceof Language) { + if (languages == null) { + languages = new ArrayList<>(); + } + + languages.add((Language) metadata); + } else if (metadata instanceof CharacterSet) { + if (characterSet == null) { + characterSet = (CharacterSet) metadata; + } else { + Context.getCurrentLogger() + .warning( + "A representation variant can have only one character set. Please check your annotation value."); + } + } + } + } + } + + // Now build the representation variants + if (mediaTypes != null) { + for (MediaType mediaType : mediaTypes) { + if ((result == null) || (!result.contains(mediaType))) { + if (result == null) { + result = new ArrayList<>(); + } + + variant = new Variant(mediaType); + + if (languages != null) { + variant.getLanguages().addAll(languages); + } + + if (characterSet != null) { + variant.setCharacterSet(characterSet); + } + + result.add(variant); + } + } + } + } + } + + return result; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode(super.hashCode(), restletMethod); + } + + /** + * Indicates if the annotated method described is compatible with the given parameters. + * + * @param restletMethod The Restlet method to match. + * @param requestEntity Optional request entity. + * @param metadataService The metadata service to use. + * @param converterService The converter service to use. + * @return True if the annotated method is compatible. + * @throws IOException + */ + public boolean isCompatible( + Method restletMethod, + Form queryParams, + Representation requestEntity, + MetadataService metadataService, + org.restlet.service.ConverterService converterService) + throws IOException { + boolean result = true; + + // Verify query parameters + if (getQuery() != null) { + Form requiredParams = new Form(getQuery()); + + for (Iterator iter = requiredParams.iterator(); iter.hasNext() && result; ) { + result = queryParams.contains(iter.next()); + } + } + + // Verify HTTP method + if (result) { + result = getRestletMethod().equals(restletMethod); + } + + // Verify request entity + if (result) { + result = isCompatibleRequestEntity(requestEntity, metadataService, converterService); + } + + return result; + } + + /** + * Indicates if the given request entity is compatible with the annotated method described. + * + * @param requestEntity Optional request entity. + * @param metadataService The metadata service to use. + * @param converterService The converter service to use. + * @return True if the given request entity is compatible with the annotated method described. + * @throws IOException + */ + public boolean isCompatibleRequestEntity( + Representation requestEntity, + MetadataService metadataService, + org.restlet.service.ConverterService converterService) + throws IOException { + boolean result = true; + + if ((requestEntity != null) && requestEntity.isAvailable()) { + List requestVariants = getRequestVariants(metadataService, converterService); + + if ((requestVariants != null) && !requestVariants.isEmpty()) { + // Check that the compatibility + result = false; + + for (int i = 0; (!result) && (i < requestVariants.size()); i++) { + result = (requestVariants.get(i).isCompatible(requestEntity)); + } + } else { + result = false; + } + } + + return result; + } + + @Override + public String toString() { + return "MethodAnnotationInfo [javaMethod: " + + javaMethod + + ", javaClass: " + + getJavaClass() + + ", restletMethod: " + + restletMethod + + ", input: " + + getInput() + + ", value: " + + getAnnotationValue() + + ", output: " + + getOutput() + + ", query: " + + getQuery() + + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java index 53764b5b0f..562f1e4aad 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java @@ -1,95 +1,89 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.resource; +import java.util.Objects; import org.restlet.data.Status; import org.restlet.engine.util.SystemUtils; -import java.util.Objects; - /** * Descriptor for status annotations. - * + * * @author Jerome Louvel */ public class ThrowableAnnotationInfo extends AnnotationInfo { - /** Indicates if the {@link Status#getThrowable()} should be serialized. */ - private final boolean serializable; - - /** The status parsed from the annotation value. */ - private final Status status; - - /** - * Constructor. - * - * @param throwableClass The class or interface that hosts the annotated Java - * method. - * @param annotationValue The annotation value containing the HTTP error code. - * @param serializable Indicates if the {@link Throwable} should be - * serialized. - */ - public ThrowableAnnotationInfo(Class throwableClass, int annotationValue, boolean serializable) { - super(throwableClass, Integer.toString(annotationValue)); - - // Parse the main components of the annotation value - this.status = Status.valueOf(annotationValue); - this.serializable = serializable; - } - - /** - * Indicates if the current object is equal to the given object. - * - * @param other The other object. - * @return True if the current object includes the other. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof ThrowableAnnotationInfo)) { - return false; - } - - ThrowableAnnotationInfo that = (ThrowableAnnotationInfo) other; - - return super.equals(that) && Objects.equals(getStatus(), that.getStatus()); - } - - /** - * Returns the status parsed from the annotation value. - * - * @return The status parsed from the annotation value. - */ - public Status getStatus() { - return status; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(super.hashCode(), status); - } - - /** - * Returns the serialize indicator parsed from the annotation value. - * - * @return the serialize indicator parsed from the annotation value. - */ - public boolean isSerializable() { - return serializable; - } - - @Override - public String toString() { - return "ExceptionAnnotationInfo [status=" + status + ", serializable=" + serializable + "]"; - } - + /** Indicates if the {@link Status#getThrowable()} should be serialized. */ + private final boolean serializable; + + /** The status parsed from the annotation value. */ + private final Status status; + + /** + * Constructor. + * + * @param throwableClass The class or interface that hosts the annotated Java method. + * @param annotationValue The annotation value containing the HTTP error code. + * @param serializable Indicates if the {@link Throwable} should be serialized. + */ + public ThrowableAnnotationInfo( + Class throwableClass, int annotationValue, boolean serializable) { + super(throwableClass, Integer.toString(annotationValue)); + + // Parse the main parts of the annotation value + this.status = Status.valueOf(annotationValue); + this.serializable = serializable; + } + + /** + * Indicates if the current object is equal to the given object. + * + * @param other The other object. + * @return True if the current object includes the other. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof final ThrowableAnnotationInfo that)) { + return false; + } + + return super.equals(that) && Objects.equals(getStatus(), that.getStatus()); + } + + /** + * Returns the status parsed from the annotation value. + * + * @return The status parsed from the annotation value. + */ + public Status getStatus() { + return status; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode(super.hashCode(), status); + } + + /** + * Returns the serialized indicator parsed from the annotation value. + * + * @return the serialized indicator parsed from the annotation value. + */ + public boolean isSerializable() { + return serializable; + } + + @Override + public String toString() { + return "ExceptionAnnotationInfo [status=" + status + ", serializable=" + serializable + "]"; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java index a332fc9757..9e035828c0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java @@ -1,117 +1,112 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.resource; +import java.util.Objects; import org.restlet.data.MediaType; import org.restlet.engine.util.SystemUtils; import org.restlet.representation.Variant; -import java.util.Objects; - /** * Variant that is declared by an annotated Java method. - * + * * @author Jerome Louvel */ public class VariantInfo extends Variant { - /** The optional annotation descriptor. */ - private final MethodAnnotationInfo annotationInfo; - - /** Affinity between this variant and an incoming representation. */ - private float inputScore; - - /** - * Constructor. - * - * @param mediaType The media type. - */ - public VariantInfo(MediaType mediaType) { - this(mediaType, null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param annotationInfo The optional annotation descriptor. - */ - public VariantInfo(MediaType mediaType, MethodAnnotationInfo annotationInfo) { - super(mediaType); - this.annotationInfo = annotationInfo; - inputScore = 1.0f; - } - - /** - * Constructor. - * - * @param variant The variant to enrich. - * @param annotationInfo The optional annotation descriptor. - */ - public VariantInfo(Variant variant, MethodAnnotationInfo annotationInfo) { - this(variant.getMediaType(), annotationInfo); - setCharacterSet(variant.getCharacterSet()); - setEncodings(variant.getEncodings()); - setLanguages(variant.getLanguages()); - } - - /** - * Indicates if the current variant is equal to the given variant. - * - * @param other The other variant. - * @return True if the current variant includes the other. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof VariantInfo)) { - return false; - } - - VariantInfo that = (VariantInfo) other; - - return super.equals(that) && Objects.equals(getAnnotationInfo(), that.getAnnotationInfo()); - } - - /** - * Returns the optional annotation descriptor. - * - * @return The optional annotation descriptor. - */ - public MethodAnnotationInfo getAnnotationInfo() { - return annotationInfo; - } - - /** - * Returns the affinity between this variant and an incoming representation. - * - * @return The affinity between this variant and an incoming representation. - */ - public float getInputScore() { - return inputScore; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(super.hashCode(), annotationInfo); - } - - /** - * Sets the affinity between this variant and an incoming representation. - * - * @param inputScore The affinity between this variant and an incoming - * representation. - */ - public void setInputScore(float inputScore) { - this.inputScore = inputScore; - } + /** The optional annotation descriptor. */ + private final MethodAnnotationInfo annotationInfo; + + /** Affinity between this variant and an incoming representation. */ + private float inputScore; + + /** + * Constructor. + * + * @param mediaType The media type. + */ + public VariantInfo(MediaType mediaType) { + this(mediaType, null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param annotationInfo The optional annotation descriptor. + */ + public VariantInfo(MediaType mediaType, MethodAnnotationInfo annotationInfo) { + super(mediaType); + this.annotationInfo = annotationInfo; + inputScore = 1.0f; + } + + /** + * Constructor. + * + * @param variant The variant to enrich. + * @param annotationInfo The optional annotation descriptor. + */ + public VariantInfo(Variant variant, MethodAnnotationInfo annotationInfo) { + this(variant.getMediaType(), annotationInfo); + setCharacterSet(variant.getCharacterSet()); + setEncodings(variant.getEncodings()); + setLanguages(variant.getLanguages()); + } + + /** + * Indicates if the current variant is equal to the given variant. + * + * @param other The other variant. + * @return True if the current variant includes the other. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof final VariantInfo that)) { + return false; + } + + return super.equals(that) && Objects.equals(getAnnotationInfo(), that.getAnnotationInfo()); + } + + /** + * Returns the optional annotation descriptor. + * + * @return The optional annotation descriptor. + */ + public MethodAnnotationInfo getAnnotationInfo() { + return annotationInfo; + } + + /** + * Returns the affinity between this variant and an incoming representation. + * + * @return The affinity between this variant and an incoming representation. + */ + public float getInputScore() { + return inputScore; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode(super.hashCode(), annotationInfo); + } + + /** + * Sets the affinity between this variant and an incoming representation. + * + * @param inputScore The affinity between this variant and an incoming representation. + */ + public void setInputScore(float inputScore) { + this.inputScore = inputScore; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java index 38b30073ed..eacdbbdd51 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java @@ -1,177 +1,185 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.security; +import java.io.IOException; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Header; +import org.restlet.data.Reference; import org.restlet.engine.Helper; import org.restlet.engine.header.ChallengeWriter; import org.restlet.engine.header.HeaderConstants; import org.restlet.util.Series; -import java.io.IOException; -import java.util.logging.Logger; - /** * Base class for authentication helpers. - * + * * @author Jerome Louvel */ public abstract class AuthenticatorHelper extends Helper { - /** The supported challenge scheme. */ - private volatile ChallengeScheme challengeScheme; - - /** Indicates if client side authentication is supported. */ - private volatile boolean clientSide; - - /** Indicates if server side authentication is supported. */ - private volatile boolean serverSide; - - /** - * Constructor. - * - * @param challengeScheme The supported challenge scheme. - * @param clientSide Indicates if client side authentication is supported. - * @param serverSide Indicates if server side authentication is supported. - */ - public AuthenticatorHelper(ChallengeScheme challengeScheme, boolean clientSide, boolean serverSide) { - this.challengeScheme = challengeScheme; - this.clientSide = clientSide; - this.serverSide = serverSide; - } - - /** - * Formats a challenge request as raw credentials. - * - * @param cw The header writer to update. - * @param challenge The challenge request to format. - * @param response The parent response. - * @param httpHeaders The current request HTTP headers. - */ - public void formatRequest(ChallengeWriter cw, ChallengeRequest challenge, Response response, - Series

httpHeaders) throws IOException { - } - - /** - * Formats a challenge response as raw credentials. - * - * @param cw The header writer to update. - * @param challenge The challenge response to format. - * @param request The parent request. - * @param httpHeaders The current request HTTP headers. - */ - public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, Request request, - Series
httpHeaders) { - } - - /** - * Returns the supported challenge scheme. - * - * @return The supported challenge scheme. - */ - public ChallengeScheme getChallengeScheme() { - return this.challengeScheme; - } - - /** - * Returns the context's logger. - * - * @return The context's logger. - */ - public Logger getLogger() { - return Context.getCurrentLogger(); - } - - /** - * Indicates if client side authentication is supported. - * - * @return True if client side authentication is supported. - */ - public boolean isClientSide() { - return this.clientSide; - } - - /** - * Indicates if server side authentication is supported. - * - * @return True if server side authentication is supported. - */ - public boolean isServerSide() { - return this.serverSide; - } - - /** - * Parses an authenticate header into a challenge request. The header is - * {@link HeaderConstants#HEADER_WWW_AUTHENTICATE}. - * - * @param challenge The challenge request to update. - * @param response The parent response. - * @param httpHeaders The current response HTTP headers. - */ - public void parseRequest(ChallengeRequest challenge, Response response, Series
httpHeaders) { - } - - /** - * Parses an authorization header into a challenge response. The header is - * {@link HeaderConstants#HEADER_AUTHORIZATION}. - * - * @param challenge The challenge response to update. - * @param request The parent request. - * @param httpHeaders The current request HTTP headers. - */ - public void parseResponse(ChallengeResponse challenge, Request request, Series
httpHeaders) { - } - - /** - * Sets the supported challenge scheme. - * - * @param challengeScheme The supported challenge scheme. - */ - public void setChallengeScheme(ChallengeScheme challengeScheme) { - this.challengeScheme = challengeScheme; - } - - /** - * Indicates if client side authentication is supported. - * - * @param clientSide True if client side authentication is supported. - */ - public void setClientSide(boolean clientSide) { - this.clientSide = clientSide; - } - - /** - * Indicates if server side authentication is supported. - * - * @param serverSide True if server side authentication is supported. - */ - public void setServerSide(boolean serverSide) { - this.serverSide = serverSide; - } - - /** - * Optionally updates the request with a challenge response before sending it. - * This is sometimes useful for authentication schemes that aren't based on the - * Authorization header but instead on URI query parameters or other headers. By - * default it returns the resource URI reference unchanged. - * - * @param resourceRef The resource URI reference to update. - * @param challengeResponse The challenge response provided. - * @param request The request to update. - * @return The original URI reference if unchanged or a new one if updated. - */ - public Reference updateReference(Reference resourceRef, ChallengeResponse challengeResponse, Request request) { - return resourceRef; - } - + /** The supported challenge scheme. */ + private volatile ChallengeScheme challengeScheme; + + /** Indicates if client side authentication is supported. */ + private volatile boolean clientSide; + + /** Indicates if server side authentication is supported. */ + private volatile boolean serverSide; + + /** + * Constructor. + * + * @param challengeScheme The supported challenge scheme. + * @param clientSide Indicates if client side authentication is supported. + * @param serverSide Indicates if server side authentication is supported. + */ + public AuthenticatorHelper( + ChallengeScheme challengeScheme, boolean clientSide, boolean serverSide) { + this.challengeScheme = challengeScheme; + this.clientSide = clientSide; + this.serverSide = serverSide; + } + + /** + * Formats a challenge request as raw credentials. + * + * @param cw The header writer to update. + * @param challenge The challenge request to format. + * @param response The parent response. + * @param httpHeaders The current request HTTP headers. + */ + public void formatRequest( + ChallengeWriter cw, + ChallengeRequest challenge, + Response response, + Series
httpHeaders) + throws IOException {} + + /** + * Formats a challenge response as raw credentials. + * + * @param cw The header writer to update. + * @param challenge The challenge response to format. + * @param request The parent request. + * @param httpHeaders The current request HTTP headers. + */ + public void formatResponse( + ChallengeWriter cw, + ChallengeResponse challenge, + Request request, + Series
httpHeaders) {} + + /** + * Returns the supported challenge scheme. + * + * @return The supported challenge scheme. + */ + public ChallengeScheme getChallengeScheme() { + return this.challengeScheme; + } + + /** + * Returns the context's logger. + * + * @return The context's logger. + */ + public Logger getLogger() { + return Context.getCurrentLogger(); + } + + /** + * Indicates if client side authentication is supported. + * + * @return True if client side authentication is supported. + */ + public boolean isClientSide() { + return this.clientSide; + } + + /** + * Indicates if server side authentication is supported. + * + * @return True if server side authentication is supported. + */ + public boolean isServerSide() { + return this.serverSide; + } + + /** + * Parses an authenticate header into a challenge request. The header is {@link + * HeaderConstants#HEADER_WWW_AUTHENTICATE}. + * + * @param challenge The challenge request to update. + * @param response The parent response. + * @param httpHeaders The current response HTTP headers. + */ + public void parseRequest( + ChallengeRequest challenge, Response response, Series
httpHeaders) {} + + /** + * Parses an authorization header into a challenge response. The header is {@link + * HeaderConstants#HEADER_AUTHORIZATION}. + * + * @param challenge The challenge response to update. + * @param request The parent request. + * @param httpHeaders The current request HTTP headers. + */ + public void parseResponse( + ChallengeResponse challenge, Request request, Series
httpHeaders) {} + + /** + * Sets the supported challenge scheme. + * + * @param challengeScheme The supported challenge scheme. + */ + public void setChallengeScheme(ChallengeScheme challengeScheme) { + this.challengeScheme = challengeScheme; + } + + /** + * Indicates if client side authentication is supported. + * + * @param clientSide True if client side authentication is supported. + */ + public void setClientSide(boolean clientSide) { + this.clientSide = clientSide; + } + + /** + * Indicates if server side authentication is supported. + * + * @param serverSide True if server side authentication is supported. + */ + public void setServerSide(boolean serverSide) { + this.serverSide = serverSide; + } + + /** + * Optionally updates the request with a challenge response before sending it. This is sometimes + * useful for authentication schemes that aren't based on the Authorization header but instead + * on URI query parameters or other headers. By default, it returns the resource URI reference + * unchanged. + * + * @param resourceRef The resource URI reference to update. + * @param challengeResponse The challenge response provided. + * @param request The request to update. + * @return The original URI reference if unchanged or a new one if updated. + */ + public Reference updateReference( + Reference resourceRef, ChallengeResponse challengeResponse, Request request) { + return resourceRef; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java index df08c74e88..e973897061 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java @@ -1,18 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.security; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; +import org.restlet.data.AuthenticationInfo; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Header; +import org.restlet.data.Parameter; +import org.restlet.data.Reference; import org.restlet.engine.Engine; import org.restlet.engine.header.ChallengeRequestReader; import org.restlet.engine.header.ChallengeWriter; @@ -20,383 +29,411 @@ import org.restlet.engine.header.HeaderReader; import org.restlet.util.Series; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - /** * Authentication utilities. - * + * * @author Jerome Louvel * @author Ray Waldin (ray@waldin.net) */ public class AuthenticatorUtils { - /** - * Indicates if any of the objects is null. - * - * @param objects The objects to test. - * @return True if any of the objects is null. - */ - public static boolean anyNull(Object... objects) { - for (final Object o : objects) { - if (o == null) { - return true; - } - } - return false; - } - - /** - * Formats authentication information as an HTTP header value. The header is - * {@link HeaderConstants#HEADER_AUTHENTICATION_INFO}. - * - * @param info The authentication information to format. - * @return The {@link HeaderConstants#HEADER_AUTHENTICATION_INFO} header value. - */ - public static String formatAuthenticationInfo(AuthenticationInfo info) { - ChallengeWriter cw = new ChallengeWriter(); - boolean firstParameter = true; - - if (info != null) { - if (info.getNextServerNonce() != null && !info.getNextServerNonce().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendQuotedChallengeParameter("nextnonce", info.getNextServerNonce()); - firstParameter = false; - } - - if (info.getQuality() != null && !info.getQuality().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendChallengeParameter("qop", info.getQuality()); - firstParameter = false; - - if (info.getNonceCount() > 0) { - cw.appendChallengeParameter("nc", formatNonceCount(info.getNonceCount())); - } - } - - if (info.getResponseDigest() != null && !info.getResponseDigest().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendQuotedChallengeParameter("rspauth", info.getResponseDigest()); - firstParameter = false; - } - - if (info.getClientNonce() != null && !info.getClientNonce().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendChallengeParameter("cnonce", info.getClientNonce()); - firstParameter = false; - } - } - - return cw.toString(); - } - - /** - * Formats a given nonce count as an HTTP header value. The header is - * {@link HeaderConstants#HEADER_AUTHENTICATION_INFO}. - * - * @param nonceCount The given nonce count. - * @return The formatted value of the given nonce count. - */ - public static String formatNonceCount(int nonceCount) { - StringBuilder result = new StringBuilder(Integer.toHexString(nonceCount)); - while (result.length() < 8) { - result.insert(0, '0'); - } - - return result.toString(); - } - - /** - * Formats a challenge request as an HTTP header value. The header is - * {@link HeaderConstants#HEADER_WWW_AUTHENTICATE} . The default implementation - * relies on - * {@link AuthenticatorHelper#formatRequest(ChallengeWriter, ChallengeRequest, Response, Series)} - * to append all parameters from {@link ChallengeRequest#getParameters()}. - * - * @param challenge The challenge request to format. - * @param response The parent response. - * @param httpHeaders The current response HTTP headers. - * @return The {@link HeaderConstants#HEADER_WWW_AUTHENTICATE} header value. - */ - public static String formatRequest(ChallengeRequest challenge, Response response, Series

httpHeaders) { - String result = null; - - if (challenge == null) { - Context.getCurrentLogger().warning("No challenge response to format."); - } else if (challenge.getScheme() == null) { - Context.getCurrentLogger().warning("A challenge response must have a scheme defined."); - } else if (challenge.getScheme().getTechnicalName() == null) { - Context.getCurrentLogger().warning("A challenge scheme must have a technical name defined."); - } else { - ChallengeWriter cw = new ChallengeWriter(); - cw.append(challenge.getScheme().getTechnicalName()).appendSpace(); - int cwInitialLength = cw.getBuffer().length(); - - if (challenge.getRawValue() != null) { - cw.append(challenge.getRawValue()); - } else { - AuthenticatorHelper helper = Engine.getInstance().findHelper(challenge.getScheme(), false, true); - - if (helper != null) { - try { - helper.formatRequest(cw, challenge, response, httpHeaders); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, - "Unable to format the challenge request: " + challenge, e); - } - } else { - result = "?"; - Context.getCurrentLogger().warning( - "Challenge scheme " + challenge.getScheme() + " not supported by the Restlet engine."); - } - } - - result = (cw.getBuffer().length() > cwInitialLength) ? cw.toString() : null; - } - - return result; - } - - /** - * Formats a challenge response as an HTTP header value. The header is - * {@link HeaderConstants#HEADER_AUTHORIZATION}. The default implementation - * relies on - * {@link AuthenticatorHelper#formatResponse(ChallengeWriter, ChallengeResponse, Request, Series)} - * unless some custom credentials are provided via - * - * @param challenge The challenge response to format. - * @param request The parent request. - * @param httpHeaders The current request HTTP headers. - * @return The {@link HeaderConstants#HEADER_AUTHORIZATION} header value. - * @link ChallengeResponse#getCredentials()}. - */ - public static String formatResponse(ChallengeResponse challenge, Request request, Series
httpHeaders) { - String result = null; - - if (challenge == null) { - Context.getCurrentLogger().warning("No challenge response to format."); - } else if (challenge.getScheme() == null) { - Context.getCurrentLogger().warning("A challenge response must have a scheme defined."); - } else if (challenge.getScheme().getTechnicalName() == null) { - Context.getCurrentLogger().warning("A challenge scheme must have a technical name defined."); - } else { - ChallengeWriter cw = new ChallengeWriter(); - cw.append(challenge.getScheme().getTechnicalName()).appendSpace(); - int cwInitialLength = cw.getBuffer().length(); - - if (challenge.getRawValue() != null) { - cw.append(challenge.getRawValue()); - } else { - AuthenticatorHelper helper = Engine.getInstance().findHelper(challenge.getScheme(), true, false); - - if (helper != null) { - try { - helper.formatResponse(cw, challenge, request, httpHeaders); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, - "Unable to format the challenge response: " + challenge, e); - } - } else { - Context.getCurrentLogger().warning( - "Challenge scheme " + challenge.getScheme() + " not supported by the Restlet engine."); - } - } - - result = (cw.getBuffer().length() > cwInitialLength) ? cw.toString() : null; - } - - return result; - } - - /** - * Parses the "Authentication-Info" header. - * - * @param header The header value to parse. - * @return The equivalent {@link AuthenticationInfo} instance. - */ - public static AuthenticationInfo parseAuthenticationInfo(String header) { - AuthenticationInfo result = null; - HeaderReader hr = new HeaderReader(header); - - try { - String nextNonce = null; - String qop = null; - String responseAuth = null; - String cnonce = null; - int nonceCount = 0; - Parameter param = hr.readParameter(); - - while (param != null) { - try { - if ("nextnonce".equals(param.getName())) { - nextNonce = param.getValue(); - } else if ("qop".equals(param.getName())) { - qop = param.getValue(); - } else if ("rspauth".equals(param.getName())) { - responseAuth = param.getValue(); - } else if ("cnonce".equals(param.getName())) { - cnonce = param.getValue(); - } else if ("nc".equals(param.getName())) { - nonceCount = Integer.parseInt(param.getValue(), 16); - } - - if (hr.skipValueSeparator()) { - param = hr.readParameter(); - } else { - param = null; - } - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, - "Unable to parse the authentication info header parameter", e); - } - } - - result = new AuthenticationInfo(nextNonce, nonceCount, cnonce, qop, responseAuth); - } catch (IOException e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to parse the authentication info header: " + header, - e); - } - - return result; - } - - /** - * Parses an WWW-Authenticate header into a list of challenge request. The header is - * {@link HeaderConstants#HEADER_WWW_AUTHENTICATE}. - * - * @param header The HTTP header value to parse. - * @param httpHeaders The current response HTTP headers. - * @return The list of parsed challenge request. - */ - public static List parseRequest(Response response, String header, Series
httpHeaders) { - List result = new ArrayList(); - - if (header != null) { - result = new ChallengeRequestReader(header).readValues(); - for (ChallengeRequest cr : result) { - // Give a chance to the authenticator helper to do further - // parsing - AuthenticatorHelper helper = Engine.getInstance().findHelper(cr.getScheme(), true, false); - - if (helper != null) { - helper.parseRequest(cr, response, httpHeaders); - } else { - Context.getCurrentLogger() - .warning("Couldn't find any helper support the " + cr.getScheme() + " challenge scheme."); - } - } - } - - return result; - } - - /** - * Parses an authorization header into a challenge response. The header is - * {@link HeaderConstants#HEADER_AUTHORIZATION}. - * - * @param request The parent request. - * @param header The authorization header. - * @param httpHeaders The current request HTTP headers. - * @return The parsed challenge response. - */ - public static ChallengeResponse parseResponse(Request request, String header, Series
httpHeaders) { - ChallengeResponse result = null; - - if (header != null) { - int space = header.indexOf(' '); - - if (space != -1) { - String scheme = header.substring(0, space); - String rawValue = header.substring(space + 1); - - result = new ChallengeResponse(new ChallengeScheme("HTTP_" + scheme, scheme)); - result.setRawValue(rawValue); - } - } - - if (result != null) { - // Give a chance to the authenticator helper to do further parsing - AuthenticatorHelper helper = Engine.getInstance().findHelper(result.getScheme(), true, false); - - if (helper != null) { - helper.parseResponse(result, request, httpHeaders); - } else { - Context.getCurrentLogger() - .warning("Couldn't find any helper support the " + result.getScheme() + " challenge scheme."); - } - } - - return result; - - } - - /** - * Updates a {@link ChallengeResponse} object according to given request and - * response. - * - * @param challengeResponse The challengeResponse to update. - * @param request The request. - * @param response The response. - */ - public static void update(ChallengeResponse challengeResponse, Request request, Response response) { - ChallengeRequest challengeRequest = null; - - for (ChallengeRequest c : response.getChallengeRequests()) { - if (challengeResponse.getScheme().equals(c.getScheme())) { - challengeRequest = c; - break; - } - } - - String realm = null; - String nonce = null; - - if (challengeRequest != null) { - realm = challengeRequest.getRealm(); - nonce = challengeRequest.getServerNonce(); - challengeResponse.setOpaque(challengeRequest.getOpaque()); - } - - challengeResponse.setRealm(realm); - challengeResponse.setServerNonce(nonce); - - challengeResponse.setDigestRef(new Reference(request.getResourceRef().getPath())); - } - - /** - * Optionally updates the request with a challenge response before sending it. - * This is sometimes useful for authentication schemes that aren't based on the - * Authorization header but instead on URI query parameters or other headers. By - * default, it returns the resource URI reference unchanged. - * - * @param resourceRef The resource URI reference to update. - * @param challengeResponse The challenge response provided. - * @param request The request to update. - * @return The original URI reference if unchanged or a new one if updated. - */ - public static Reference updateReference(Reference resourceRef, ChallengeResponse challengeResponse, - Request request) { - if (challengeResponse != null && challengeResponse.getRawValue() == null) { - AuthenticatorHelper helper = Engine.getInstance().findHelper(challengeResponse.getScheme(), true, false); - - if (helper != null) { - resourceRef = helper.updateReference(resourceRef, challengeResponse, request); - } else { - Context.getCurrentLogger().warning( - "Challenge scheme " + challengeResponse.getScheme() + " not supported by the Restlet engine."); - } - } - - return resourceRef; - } - - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e., it isn't instantiable and extensible. - */ - private AuthenticatorUtils() { - } - + /** + * Indicates if any of the objects is null. + * + * @param objects The objects to test. + * @return True if any of the objects is null. + */ + public static boolean anyNull(Object... objects) { + for (final Object o : objects) { + if (o == null) { + return true; + } + } + return false; + } + + /** + * Formats authentication information as an HTTP header value. The header is {@link + * HeaderConstants#HEADER_AUTHENTICATION_INFO}. + * + * @param info The authentication information to format. + * @return The {@link HeaderConstants#HEADER_AUTHENTICATION_INFO} header value. + */ + public static String formatAuthenticationInfo(AuthenticationInfo info) { + ChallengeWriter cw = new ChallengeWriter(); + boolean firstParameter = true; + + if (info != null) { + if (info.getNextServerNonce() != null && !info.getNextServerNonce().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendQuotedChallengeParameter("nextnonce", info.getNextServerNonce()); + firstParameter = false; + } + + if (info.getQuality() != null && !info.getQuality().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendChallengeParameter("qop", info.getQuality()); + firstParameter = false; + + if (info.getNonceCount() > 0) { + cw.appendChallengeParameter("nc", formatNonceCount(info.getNonceCount())); + } + } + + if (info.getResponseDigest() != null && !info.getResponseDigest().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendQuotedChallengeParameter("rspauth", info.getResponseDigest()); + firstParameter = false; + } + + if (info.getClientNonce() != null && !info.getClientNonce().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendChallengeParameter("cnonce", info.getClientNonce()); + firstParameter = false; + } + } + + return cw.toString(); + } + + /** + * Formats a given nonce count as an HTTP header value. The header is {@link + * HeaderConstants#HEADER_AUTHENTICATION_INFO}. + * + * @param nonceCount The given nonce count. + * @return The formatted value of the given nonce count. + */ + public static String formatNonceCount(int nonceCount) { + StringBuilder result = new StringBuilder(Integer.toHexString(nonceCount)); + while (result.length() < 8) { + result.insert(0, '0'); + } + + return result.toString(); + } + + /** + * Formats a challenge request as an HTTP header value. The header is {@link + * HeaderConstants#HEADER_WWW_AUTHENTICATE} . The default implementation relies on {@link + * AuthenticatorHelper#formatRequest(ChallengeWriter, ChallengeRequest, Response, Series)} to + * append all parameters from {@link ChallengeRequest#getParameters()}. + * + * @param challenge The challenge request to format. + * @param response The parent response. + * @param httpHeaders The current response HTTP headers. + * @return The {@link HeaderConstants#HEADER_WWW_AUTHENTICATE} header value. + */ + public static String formatRequest( + ChallengeRequest challenge, Response response, Series
httpHeaders) { + String result = null; + + if (challenge == null) { + Context.getCurrentLogger().warning("No challenge response to format."); + } else if (challenge.getScheme() == null) { + Context.getCurrentLogger().warning("A challenge response must have a scheme defined."); + } else if (challenge.getScheme().getTechnicalName() == null) { + Context.getCurrentLogger() + .warning("A challenge scheme must have a technical name defined."); + } else { + ChallengeWriter cw = new ChallengeWriter(); + cw.append(challenge.getScheme().getTechnicalName()).appendSpace(); + int cwInitialLength = cw.getBuffer().length(); + + if (challenge.getRawValue() != null) { + cw.append(challenge.getRawValue()); + } else { + AuthenticatorHelper helper = + Engine.getInstance().findHelper(challenge.getScheme(), false, true); + + if (helper != null) { + try { + helper.formatRequest(cw, challenge, response, httpHeaders); + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to format the challenge request: " + challenge, + e); + } + } else { + result = "?"; + Context.getCurrentLogger() + .warning( + "Challenge scheme " + + challenge.getScheme() + + " not supported by the Restlet engine."); + } + } + + result = (cw.getBuffer().length() > cwInitialLength) ? cw.toString() : null; + } + + return result; + } + + /** + * Formats a challenge response as an HTTP header value. The header is {@link + * HeaderConstants#HEADER_AUTHORIZATION}. The default implementation relies on {@link + * AuthenticatorHelper#formatResponse(ChallengeWriter, ChallengeResponse, Request, Series)} + * unless some custom credentials are provided via + * + * @param challenge The challenge response to format. + * @param request The parent request. + * @param httpHeaders The current request HTTP headers. + * @return The {@link HeaderConstants#HEADER_AUTHORIZATION} header value. + * @link ChallengeResponse#getCredentials()}. + */ + public static String formatResponse( + ChallengeResponse challenge, Request request, Series
httpHeaders) { + String result = null; + + if (challenge == null) { + Context.getCurrentLogger().warning("No challenge response to format."); + } else if (challenge.getScheme() == null) { + Context.getCurrentLogger().warning("A challenge response must have a scheme defined."); + } else if (challenge.getScheme().getTechnicalName() == null) { + Context.getCurrentLogger() + .warning("A challenge scheme must have a technical name defined."); + } else { + ChallengeWriter cw = new ChallengeWriter(); + cw.append(challenge.getScheme().getTechnicalName()).appendSpace(); + int cwInitialLength = cw.getBuffer().length(); + + if (challenge.getRawValue() != null) { + cw.append(challenge.getRawValue()); + } else { + AuthenticatorHelper helper = + Engine.getInstance().findHelper(challenge.getScheme(), true, false); + + if (helper != null) { + try { + helper.formatResponse(cw, challenge, request, httpHeaders); + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to format the challenge response: " + challenge, + e); + } + } else { + Context.getCurrentLogger() + .warning( + "Challenge scheme " + + challenge.getScheme() + + " not supported by the Restlet engine."); + } + } + + result = (cw.getBuffer().length() > cwInitialLength) ? cw.toString() : null; + } + + return result; + } + + /** + * Parses the "Authentication-Info" header. + * + * @param header The header value to parse. + * @return The equivalent {@link AuthenticationInfo} instance. + */ + public static AuthenticationInfo parseAuthenticationInfo(String header) { + AuthenticationInfo result = null; + HeaderReader hr = new HeaderReader<>(header); + + try { + String nextNonce = null; + String qop = null; + String responseAuth = null; + String cnonce = null; + int nonceCount = 0; + Parameter param = hr.readParameter(); + + while (param != null) { + try { + if ("nextnonce".equals(param.getName())) { + nextNonce = param.getValue(); + } else if ("qop".equals(param.getName())) { + qop = param.getValue(); + } else if ("rspauth".equals(param.getName())) { + responseAuth = param.getValue(); + } else if ("cnonce".equals(param.getName())) { + cnonce = param.getValue(); + } else if ("nc".equals(param.getName())) { + nonceCount = Integer.parseInt(param.getValue(), 16); + } + + if (hr.skipValueSeparator()) { + param = hr.readParameter(); + } else { + param = null; + } + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to parse the authentication info header parameter", + e); + } + } + + result = new AuthenticationInfo(nextNonce, nonceCount, cnonce, qop, responseAuth); + } catch (IOException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to parse the authentication info header: " + header, + e); + } + + return result; + } + + /** + * Parses an WWW-Authenticate header into a list of challenge request. The header is {@link + * HeaderConstants#HEADER_WWW_AUTHENTICATE}. + * + * @param header The HTTP header value to parse. + * @param httpHeaders The current response HTTP headers. + * @return The list of parsed challenge request. + */ + public static List parseRequest( + Response response, String header, Series
httpHeaders) { + List result = new ArrayList(); + + if (header != null) { + result = new ChallengeRequestReader(header).readValues(); + for (ChallengeRequest cr : result) { + // Give a chance to the authenticator helper to do further + // parsing + AuthenticatorHelper helper = + Engine.getInstance().findHelper(cr.getScheme(), true, false); + + if (helper != null) { + helper.parseRequest(cr, response, httpHeaders); + } else { + Context.getCurrentLogger() + .warning( + "Couldn't find any helper support the " + + cr.getScheme() + + " challenge scheme."); + } + } + } + + return result; + } + + /** + * Parses an authorization header into a challenge response. The header is {@link + * HeaderConstants#HEADER_AUTHORIZATION}. + * + * @param request The parent request. + * @param header The authorization header. + * @param httpHeaders The current request HTTP headers. + * @return The parsed challenge response. + */ + public static ChallengeResponse parseResponse( + Request request, String header, Series
httpHeaders) { + ChallengeResponse result = null; + + if (header != null) { + int space = header.indexOf(' '); + + if (space != -1) { + String scheme = header.substring(0, space); + String rawValue = header.substring(space + 1); + + result = new ChallengeResponse(new ChallengeScheme("HTTP_" + scheme, scheme)); + result.setRawValue(rawValue); + } + } + + if (result != null) { + // Give a chance to the authenticator helper to do further parsing + AuthenticatorHelper helper = + Engine.getInstance().findHelper(result.getScheme(), true, false); + + if (helper != null) { + helper.parseResponse(result, request, httpHeaders); + } else { + Context.getCurrentLogger() + .warning( + "Couldn't find any helper support the " + + result.getScheme() + + " challenge scheme."); + } + } + + return result; + } + + /** + * Updates a {@link ChallengeResponse} object according to given request and response. + * + * @param challengeResponse The challengeResponse to update. + * @param request The request. + * @param response The response. + */ + public static void update( + ChallengeResponse challengeResponse, Request request, Response response) { + ChallengeRequest challengeRequest = null; + + for (ChallengeRequest c : response.getChallengeRequests()) { + if (challengeResponse.getScheme().equals(c.getScheme())) { + challengeRequest = c; + break; + } + } + + String realm = null; + String nonce = null; + + if (challengeRequest != null) { + realm = challengeRequest.getRealm(); + nonce = challengeRequest.getServerNonce(); + challengeResponse.setOpaque(challengeRequest.getOpaque()); + } + + challengeResponse.setRealm(realm); + challengeResponse.setServerNonce(nonce); + + challengeResponse.setDigestRef(new Reference(request.getResourceRef().getPath())); + } + + /** + * Optionally updates the request with a challenge response before sending it. This is sometimes + * useful for authentication schemes that aren't based on the Authorization header but instead + * on URI query parameters or other headers. By default, it returns the resource URI reference + * unchanged. + * + * @param resourceRef The resource URI reference to update. + * @param challengeResponse The challenge response provided. + * @param request The request to update. + * @return The original URI reference if unchanged or a new one if updated. + */ + public static Reference updateReference( + Reference resourceRef, ChallengeResponse challengeResponse, Request request) { + if (challengeResponse != null && challengeResponse.getRawValue() == null) { + AuthenticatorHelper helper = + Engine.getInstance().findHelper(challengeResponse.getScheme(), true, false); + + if (helper != null) { + resourceRef = helper.updateReference(resourceRef, challengeResponse, request); + } else { + Context.getCurrentLogger() + .warning( + "Challenge scheme " + + challengeResponse.getScheme() + + " not supported by the Restlet engine."); + } + } + + return resourceRef; + } + + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private AuthenticatorUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java index 43ce5cfa94..c908bf6263 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.security; import java.io.CharArrayWriter; @@ -14,7 +13,6 @@ import java.io.UnsupportedEncodingException; import java.util.Base64; import java.util.logging.Level; - import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -30,147 +28,168 @@ /** * Implements the HTTP BASIC authentication. - * + * * @author Jerome Louvel */ public class HttpBasicHelper extends AuthenticatorHelper { - /** - * Constructor. - */ - public HttpBasicHelper() { - super(ChallengeScheme.HTTP_BASIC, true, true); - } - - @Override - public void formatRequest(ChallengeWriter cw, ChallengeRequest challenge, Response response, - Series

httpHeaders) throws IOException { - String realm = challenge.getRealm(); - String charset = challenge.getParameters().getFirstValue("charset"); - - if (realm != null) { - cw.appendQuotedChallengeParameter("realm", realm); - } else { - getLogger() - .warning("The realm directive is required for all authentication schemes that issue a challenge."); - } - - if (charset != null) { - if ("UTF-8".equalsIgnoreCase(charset)) { - cw.appendQuotedChallengeParameter("charset", "UTF-8"); - } else { - getLogger().warning("The \"charset\" parameter must be \"UTF-8\" per RFC 7617."); - } - } - } - - @Override - public void formatResponse(ChallengeWriter cw, ChallengeResponse challenge, Request request, - Series
httpHeaders) { - try { - if (challenge == null) { - throw new RuntimeException("No challenge provided, unable to encode credentials"); - } else { - String charset = challenge.getParameters().getFirstValue("charset"); - - if (charset != null) { - if ("UTF-8".equalsIgnoreCase(charset)) { - charset = "UTF-8"; - } else { - getLogger().warning( - "The \"charset\" parameter must be \"UTF-8\" per RFC 7617. Using \"ISO-8859-1\" instead."); - charset = "ISO-8859-1"; - } - } else { - charset = "ISO-8859-1"; - } - - CharArrayWriter credentials = new CharArrayWriter(); - credentials.write(challenge.getIdentifier()); - credentials.write(":"); - credentials.write(challenge.getSecret()); - cw.append(Base64.getEncoder().encodeToString(IoUtils.toByteArray(credentials.toCharArray(), charset))); - } - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Unsupported encoding, unable to encode credentials"); - } catch (IOException e) { - throw new RuntimeException("Unexpected exception, unable to encode credentials", e); - } - } - - @Override - public void parseRequest(ChallengeRequest challenge, Response response, Series
httpHeaders) { - if (challenge.getRawValue() != null) { - HeaderReader hr = new HeaderReader(challenge.getRawValue()); - - try { - Parameter param = hr.readParameter(); - - while (param != null) { - try { - if ("realm".equals(param.getName())) { - challenge.setRealm(param.getValue()); - } else { - challenge.getParameters().add(param); - } - - if (hr.skipValueSeparator()) { - param = hr.readParameter(); - } else { - param = null; - } - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, - "Unable to parse the challenge request header parameter", e); - } - } - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to parse the challenge request header parameter", - e); - } - } - } - - @Override - public void parseResponse(ChallengeResponse challenge, Request request, Series
httpHeaders) { - if (challenge.getRawValue() == null) { - getLogger().info("Cannot decode credentials: " + challenge.getRawValue()); - return; - } - - try { - String charset = challenge.getParameters().getFirstValue("charset"); - - if (charset != null) { - if ("UTF-8".equalsIgnoreCase(charset)) { - charset = "UTF-8"; - } else { - getLogger().warning( - "The \"charset\" parameter must be \"UTF-8\" per RFC 7617. Using \"ISO-8859-1\" instead."); - charset = "ISO-8859-1"; - } - } else { - charset = "ISO-8859-1"; - } - - byte[] credentialsEncoded = Base64.getDecoder().decode(challenge.getRawValue()); - - String credentials = new String(credentialsEncoded, charset); - int separator = credentials.indexOf(':'); - - if (separator == -1) { - // Log the blocking - getLogger().info("Invalid credentials given by client with IP: " - + ((request != null) ? request.getClientInfo().getAddress() : "?")); - } else { - challenge.setIdentifier(credentials.substring(0, separator)); - challenge.setSecret(credentials.substring(separator + 1)); - } - } catch (UnsupportedEncodingException e) { - getLogger().log(Level.INFO, "Unsupported HTTP Basic encoding error", e); - } catch (IllegalArgumentException e) { - getLogger().log(Level.INFO, "Unable to decode the HTTP Basic credential", e); - } - } - + /** Constructor. */ + public HttpBasicHelper() { + super(ChallengeScheme.HTTP_BASIC, true, true); + } + + @Override + public void formatRequest( + ChallengeWriter cw, + ChallengeRequest challenge, + Response response, + Series
httpHeaders) { + String realm = challenge.getRealm(); + String charset = challenge.getParameters().getFirstValue("charset"); + + if (realm != null) { + cw.appendQuotedChallengeParameter("realm", realm); + } else { + getLogger() + .warning( + "The realm directive is required for all authentication schemes that issue a challenge."); + } + + if (charset != null) { + if ("UTF-8".equalsIgnoreCase(charset)) { + cw.appendQuotedChallengeParameter("charset", "UTF-8"); + } else { + getLogger().warning("The \"charset\" parameter must be \"UTF-8\" per RFC 7617."); + } + } + } + + @Override + public void formatResponse( + ChallengeWriter cw, + ChallengeResponse challenge, + Request request, + Series
httpHeaders) { + try { + if (challenge == null) { + throw new RuntimeException("No challenge provided, unable to encode credentials"); + } else { + String charset = challenge.getParameters().getFirstValue("charset"); + + if (charset != null) { + if ("UTF-8".equalsIgnoreCase(charset)) { + charset = "UTF-8"; + } else { + getLogger() + .warning( + "The \"charset\" parameter must be \"UTF-8\" per RFC 7617. Using \"ISO-8859-1\" instead."); + charset = "ISO-8859-1"; + } + } else { + charset = "ISO-8859-1"; + } + + CharArrayWriter credentials = new CharArrayWriter(); + credentials.write(challenge.getIdentifier()); + credentials.write(":"); + credentials.write(challenge.getSecret()); + cw.append( + Base64.getEncoder() + .encodeToString( + IoUtils.toByteArray(credentials.toCharArray(), charset))); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unsupported encoding, unable to encode credentials"); + } catch (IOException e) { + throw new RuntimeException("Unexpected exception, unable to encode credentials", e); + } + } + + @Override + public void parseRequest( + ChallengeRequest challenge, Response response, Series
httpHeaders) { + if (challenge.getRawValue() != null) { + HeaderReader hr = new HeaderReader(challenge.getRawValue()); + + try { + Parameter param = hr.readParameter(); + + while (param != null) { + try { + if ("realm".equals(param.getName())) { + challenge.setRealm(param.getValue()); + } else { + challenge.getParameters().add(param); + } + + if (hr.skipValueSeparator()) { + param = hr.readParameter(); + } else { + param = null; + } + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to parse the challenge request header parameter", + e); + } + } + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to parse the challenge request header parameter", + e); + } + } + } + + @Override + public void parseResponse( + ChallengeResponse challenge, Request request, Series
httpHeaders) { + if (challenge.getRawValue() == null) { + getLogger().info("Cannot decode credentials: " + challenge.getRawValue()); + return; + } + + try { + String charset = challenge.getParameters().getFirstValue("charset"); + + if (charset != null) { + if ("UTF-8".equalsIgnoreCase(charset)) { + charset = "UTF-8"; + } else { + getLogger() + .warning( + "The \"charset\" parameter must be \"UTF-8\" per RFC 7617. Using \"ISO-8859-1\" instead."); + charset = "ISO-8859-1"; + } + } else { + charset = "ISO-8859-1"; + } + + byte[] credentialsEncoded = Base64.getDecoder().decode(challenge.getRawValue()); + + String credentials = new String(credentialsEncoded, charset); + int separator = credentials.indexOf(':'); + + if (separator == -1) { + // Log the blocking + getLogger() + .info( + "Invalid credentials given by client with IP: " + + ((request != null) + ? request.getClientInfo().getAddress() + : "?")); + } else { + challenge.setIdentifier(credentials.substring(0, separator)); + challenge.setSecret(credentials.substring(separator + 1)); + } + } catch (UnsupportedEncodingException e) { + getLogger().log(Level.INFO, "Unsupported HTTP Basic encoding error", e); + } catch (IllegalArgumentException e) { + getLogger().log(Level.INFO, "Unable to decode the HTTP Basic credential", e); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java index 83bbba53cd..dfa4ae52eb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java @@ -1,41 +1,36 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.security; import java.io.IOException; import java.net.InetAddress; - import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; - import org.eclipse.jetty.util.ssl.SslContextFactory; /** * Jetty SSL context factory based on a Restlet SSL context one. - * + * * @author Jerome Louvel */ public class RestletSslContextFactoryClient extends SslContextFactory.Client { /** * Constructor. - * - * @param restletSslContextFactory - * The Restlet SSL context factory to leverage. + * + * @param restletSslContextFactory The Restlet SSL context factory to leverage. * @throws Exception */ public RestletSslContextFactoryClient( - org.restlet.engine.ssl.SslContextFactory restletSslContextFactory) - throws Exception { + org.restlet.engine.ssl.SslContextFactory restletSslContextFactory) throws Exception { setSslContext(restletSslContextFactory.createSslContext()); } @@ -53,9 +48,10 @@ public SSLEngine newSSLEngine(String host, int port) { public SSLServerSocket newSslServerSocket(String host, int port, int backlog) throws IOException { SSLServerSocketFactory factory = getSslContext().getServerSocketFactory(); - return (SSLServerSocket) ((host == null) - ? factory.createServerSocket(port, backlog) : - factory.createServerSocket(port, backlog, InetAddress.getByName(host))); + return (SSLServerSocket) + ((host == null) + ? factory.createServerSocket(port, backlog) + : factory.createServerSocket(port, backlog, InetAddress.getByName(host))); } @Override diff --git a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java index ac4155a644..6f680d276c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java @@ -1,42 +1,37 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.security; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; - import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; - import org.eclipse.jetty.util.ssl.SslContextFactory; /** * Jetty SSL context factory based on a Restlet SSL context one. - * + * * @author Jerome Louvel */ public class RestletSslContextFactoryServer extends SslContextFactory.Server { /** * Constructor. - * - * @param restletSslContextFactory - * The Restlet SSL context factory to leverage. + * + * @param restletSslContextFactory The Restlet SSL context factory to leverage. * @throws Exception */ public RestletSslContextFactoryServer( - org.restlet.engine.ssl.SslContextFactory restletSslContextFactory) - throws Exception { + org.restlet.engine.ssl.SslContextFactory restletSslContextFactory) throws Exception { setSslContext(restletSslContextFactory.createSslContext()); } @@ -54,9 +49,10 @@ public SSLEngine newSSLEngine(String host, int port) { public SSLServerSocket newSslServerSocket(String host, int port, int backlog) throws IOException { final SSLServerSocketFactory factory = getSslContext().getServerSocketFactory(); - final ServerSocket serverSocket = (host == null) - ? factory.createServerSocket(port, backlog) - : factory.createServerSocket(port, backlog, InetAddress.getByName(host)); + final ServerSocket serverSocket = + (host == null) + ? factory.createServerSocket(port, backlog) + : factory.createServerSocket(port, backlog, InetAddress.getByName(host)); return (SSLServerSocket) serverSocket; } diff --git a/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java b/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java index d7e9d28c03..58896ba541 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.security; import org.restlet.security.Group; @@ -15,53 +14,50 @@ /** * Mapping from an organization or a user or a group to a role. - * + * * @author Jerome Louvel */ public class RoleMapping { - /** - * The source of the mapping. It must be an instance of one of these classes: - * {@link User} or {@link Group}. - */ - private volatile Object source; - - /** The target role of the mapping. */ - private volatile Role target; - - /** - * Default constructor. - */ - public RoleMapping() { - this(null, null); - } - - /** - * Constructor. - * - * @param source - * @param target - */ - public RoleMapping(Object source, Role target) { - super(); - this.source = source; - this.target = target; - } - - public Object getSource() { - return source; - } - - public Role getTarget() { - return target; - } - - public void setSource(Object source) { - this.source = source; - } - - public void setTarget(Role target) { - this.target = target; - } - + /** + * The source of the mapping. It must be an instance of one of these classes: {@link User} or + * {@link Group}. + */ + private volatile Object source; + + /** The target role of the mapping. */ + private volatile Role target; + + /** Default constructor. */ + public RoleMapping() { + this(null, null); + } + + /** + * Constructor. + * + * @param source + * @param target + */ + public RoleMapping(Object source, Role target) { + super(); + this.source = source; + this.target = target; + } + + public Object getSource() { + return source; + } + + public Role getTarget() { + return target; + } + + public void setSource(Object source) { + this.source = source; + } + + public void setTarget(Role target) { + this.target = target; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java index 7163606422..bedb344a62 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java @@ -1,46 +1,45 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.ssl; import javax.net.ssl.SSLContext; /** * Default SSL context that delegates calls to {@link WrapperSslContextSpi} - * + * * @author Jerome Louvel */ public class DefaultSslContext extends SSLContext { - /** - * Creates a SSL context SPI capable or setting additional properties on the - * created SSL engines and socket factories. - * - * @param contextFactory The parent SSL context factory. - * @param wrappedContext The wrapped SSL context. - * @return The created SSL context SPI. - */ - private static WrapperSslContextSpi createContextSpi(DefaultSslContextFactory contextFactory, - SSLContext wrappedContext) { - return new WrapperSslContextSpi(contextFactory, wrappedContext); - } - - /** - * Constructor. - * - * @param contextFactory The parent SSL context factory. - * @param wrappedContext The wrapped SSL context. - * - */ - public DefaultSslContext(DefaultSslContextFactory contextFactory, SSLContext wrappedContext) { - super(createContextSpi(contextFactory, wrappedContext), wrappedContext.getProvider(), - wrappedContext.getProtocol()); - } + /** + * Creates a SSL context SPI capable or setting additional properties on the created SSL engines + * and socket factories. + * + * @param contextFactory The parent SSL context factory. + * @param wrappedContext The wrapped SSL context. + * @return The created SSL context SPI. + */ + private static WrapperSslContextSpi createContextSpi( + DefaultSslContextFactory contextFactory, SSLContext wrappedContext) { + return new WrapperSslContextSpi(contextFactory, wrappedContext); + } + /** + * Constructor. + * + * @param contextFactory The parent SSL context factory. + * @param wrappedContext The wrapped SSL context. + */ + public DefaultSslContext(DefaultSslContextFactory contextFactory, SSLContext wrappedContext) { + super( + createContextSpi(contextFactory, wrappedContext), + wrappedContext.getProvider(), + wrappedContext.getProtocol()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java index ba760acbc3..527518c482 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java @@ -1,33 +1,32 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.ssl; -import org.restlet.data.Parameter; -import org.restlet.util.Series; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSocketFactory; import java.io.FileInputStream; -import java.security.*; +import java.security.KeyStore; +import java.security.SecureRandom; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSocketFactory; +import org.restlet.data.Parameter; +import org.restlet.util.Series; /** - * This {@link SslContextFactory} makes it possible to configure most basic - * options when building an SSLContext. See the {@link #init(Series)} method for - * the list of parameters supported by this factory when configuring your HTTP - * client or server connector. Here is the list of SSL related parameters that - * are also supported: + * This {@link SslContextFactory} makes it possible to configure most basic options when building an + * SSLContext. See the {@link #init(Series)} method for the list of parameters supported by this + * factory when configuring your HTTP client or server connector. Here is the list of SSL related + * parameters that are also supported: + * * * * @@ -146,395 +145,425 @@ * into account if the "needClientAuthentication" parameter is 'false'. * *
list of supported parameters
- *

- * In short, two instances of KeyStore are used when configuring an SSLContext: - * the key store (which contains the public and private keys and certificates to - * be used locally) and the trust store (which generally holds the CA - * certificates to be trusted when connecting to a remote host). Both keystore - * and trust store are KeyStores. When not explicitly set using the setters of - * this class, the values will default to the default system properties, - * following the behavior described in the JSSE reference guide. - *

- *

- * There is more information in the JSSE Reference Guide. - *

- * + * + *

In short, two instances of KeyStore are used when configuring an SSLContext: the key store + * (which contains the public and private keys and certificates to be used locally) and the trust + * store (which generally holds the CA certificates to be trusted when connecting to a remote host). + * Both keystore and trust store are KeyStores. When not explicitly set using the setters of this + * class, the values will default to the default system properties, following the behavior described + * in the JSSE reference guide. + * + *

There is more information in the JSSE + * Reference Guide. + * * @author Bruno Harbulot * @see javax.net.ssl.SSLContext * @see java.security.KeyStore * @see JSSE - * Reference - Standard names + * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#AppA">JSSE + * Reference - Standard names */ public class DefaultSslContextFactory extends SslContextFactory { - /** The whitespace-separated list of disabled cipher suites. */ - private volatile String[] disabledCipherSuites = null; - - /** The whitespace-separated list of disabled SSL protocols. */ - private volatile String[] disabledProtocols = null; - - /** The whitespace-separated list of enabled cipher suites. */ - private volatile String[] enabledCipherSuites = null; - - /** The whitespace-separated list of enabled SSL protocols. */ - private volatile String[] enabledProtocols = null; - - /** The name of the KeyManager algorithm. */ - private volatile String keyManagerAlgorithm = System.getProperty("ssl.KeyManagerFactory.algorithm", "SunX509"); - - /** The password for the key in the keystore (as a String). */ - private volatile char[] keyStoreKeyPassword = (System.getProperty("javax.net.ssl.keyPassword", - System.getProperty("javax.net.ssl.keyStorePassword")) != null) ? System - .getProperty("javax.net.ssl.keyPassword", System.getProperty("javax.net.ssl.keyStorePassword")) - .toCharArray() : null; - - /** The password for the keystore (as a String). */ - private volatile char[] keyStorePassword = (System.getProperty("javax.net.ssl.keyStorePassword") != null) - ? System.getProperty("javax.net.ssl.keyStorePassword").toCharArray() - : null; - - /** The path to the KeyStore file. */ - private volatile String keyStorePath = System - .getProperty("javax.net.ssl.keyStore", - (System.getProperty("user.home") != null) ? ((System.getProperty("user.home").endsWith("/")) - ? System.getProperty("user.home") + ".keystore" - : System.getProperty("user.home") + "/.keystore") : null); - - /** The name of the keystore provider. */ - private volatile String keyStoreProvider = System.getProperty("javax.net.ssl.keyStoreProvider"); - - /** The keyStore type of the keystore. */ - private volatile String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", "JKS"); - - /** Indicates if we require client certificate authentication. */ - private volatile boolean needClientAuthentication = false; - - /** The standard name of the protocol to use when creating the SSLContext. */ - private volatile String protocol = "TLS"; - - /** The name of the SecureRandom algorithm. */ - private volatile String secureRandomAlgorithm = null; - - /** The name of the TrustManager algorithm. */ - private volatile String trustManagerAlgorithm = System.getProperty("ssl.TrustManagerFactory.algorithm", "SunX509"); - - /** The password for the trust store keystore. */ - private volatile char[] trustStorePassword = (System.getProperty("javax.net.ssl.trustStorePassword") != null) - ? System.getProperty("javax.net.ssl.trustStorePassword").toCharArray() - : null; - - /** The path to the trust store (keystore) file. */ - private volatile String trustStorePath = System.getProperty("javax.net.ssl.trustStore"); - - /** The name of the trust store (keystore) provider. */ - private volatile String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider"); - - /** The KeyStore type of the trust store. */ - private volatile String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType"); - - /** Indicates if we would like client certificate authentication. */ - private volatile boolean wantClientAuthentication = false; - - /** - * This class is likely to contain sensitive information; cloning is therefore - * not allowed. - */ - @Override - protected final DefaultSslContextFactory clone() throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } - - /** - * Creates a configured and initialized SSLContext from the values set via the - * various setters of this class. If keyStorePath, - * keyStoreProvider, keyStoreType are all - * null, the SSLContext will be initialized with a - * null array of KeyManagers. Similarly, if - * trustStorePath, trustStoreProvider, - * trustStoreType are all null, a null - * array of TrustManagers will be used. - * - * @see SSLContext#init(javax.net.ssl.KeyManager[], - * javax.net.ssl.TrustManager[], SecureRandom) - */ - @Override - public javax.net.ssl.SSLContext createSslContext() throws Exception { - final javax.net.ssl.SSLContext result; - - final javax.net.ssl.KeyManagerFactory kmf; - if ((this.keyStorePath != null) || (this.keyStoreProvider != null) || (this.keyStoreType != null)) { - final KeyStore keyStore = loadKeyStore(this.keyStorePath, this.keyStoreProvider, this.keyStoreType, this.keyStorePassword); - - // Creates the key-manager factory. - kmf = javax.net.ssl.KeyManagerFactory.getInstance(this.keyManagerAlgorithm); - kmf.init(keyStore, this.keyStoreKeyPassword); - } else { - kmf = null; - } - - final javax.net.ssl.TrustManagerFactory tmf; - if ((this.trustStorePath != null) || (this.trustStoreProvider != null) || (this.trustStoreType != null)) { - final KeyStore trustStore = loadKeyStore(this.trustStorePath, this.trustStoreProvider, this.trustStoreType, this.trustStorePassword); - - // Creates the trust-manager factory. - tmf = javax.net.ssl.TrustManagerFactory.getInstance(this.trustManagerAlgorithm); - tmf.init(trustStore); - } else { - tmf = null; - } - - final SecureRandom sr; - if (this.secureRandomAlgorithm != null) { - sr = SecureRandom.getInstance(this.secureRandomAlgorithm); - } else { - sr = null; - } - - // Creates the SSL context - final javax.net.ssl.SSLContext sslContext = javax.net.ssl.SSLContext.getInstance(this.protocol); - sslContext.init(kmf != null ? kmf.getKeyManagers() : null, tmf != null ? tmf.getTrustManagers() : null, sr); - - // Wraps the SSL context to be able to set cipher suites and other - // properties after SSL engine creation, for example - result = createWrapper(sslContext); - return result; - } - - /** - * Creates a new {@link SSLContext} wrapper. Necessary to properly initialize - * the {@link SSLEngine} or {@link SSLSocketFactory} or - * {@link javax.net.ssl.SSLServerSocketFactory} created. - * - * @param sslContext The SSL context to wrap. - * @return The SSL context wrapper. - */ - protected javax.net.ssl.SSLContext createWrapper(javax.net.ssl.SSLContext sslContext) { - return new DefaultSslContext(this, sslContext); - } - - /** - * Returns the whitespace-separated list of disabled cipher suites. - * - * @return The whitespace-separated list of disabled cipher suites. - */ - public String[] getDisabledCipherSuites() { - return disabledCipherSuites; - } - - /** - * Returns the whitespace-separated list of disabled SSL protocols. - * - * @return The whitespace-separated list of disabled SSL protocols. - */ - public String[] getDisabledProtocols() { - return disabledProtocols; - } - - /** - * Returns the whitespace-separated list of enabled cipher suites. - * - * @return The whitespace-separated list of enabled cipher suites. - */ - public String[] getEnabledCipherSuites() { - return enabledCipherSuites; - } - - /** - * Returns the whitespace-separated list of enabled SSL protocols. - * - * @return The whitespace-separated list of enabled SSL protocols. - */ - public String[] getEnabledProtocols() { - return enabledProtocols; - } - - /** - * Returns the name of the KeyManager algorithm. - * - * @return The name of the KeyManager algorithm. - */ - public String getKeyManagerAlgorithm() { - return keyManagerAlgorithm; - } - - /** - * Returns the password for the key in the keystore (as a String). - * - * @return The password for the key in the keystore (as a String). - */ - public char[] getKeyStoreKeyPassword() { - return keyStoreKeyPassword; - } - - /** - * Returns the password for the keystore (as a String). - * - * @return The password for the keystore (as a String). - */ - public char[] getKeyStorePassword() { - return keyStorePassword; - } - - /** - * Returns the path to the KeyStore file. - * - * @return The path to the KeyStore file. - */ - public String getKeyStorePath() { - return keyStorePath; - } - - /** - * Returns the name of the keystore provider. - * - * @return The name of the keystore provider. - */ - public String getKeyStoreProvider() { - return keyStoreProvider; - } - - /** - * Returns the keyStore type of the keystore. - * - * @return The keyStore type of the keystore. - */ - public String getKeyStoreType() { - return keyStoreType; - } - - /** - * Returns the secure socket protocol name, "TLS" by default. - * - * @return The secure socket protocol. - */ - public String getProtocol() { - return this.protocol; - } - - /** - * Returns the name of the SecureRandom algorithm. - * - * @return The name of the SecureRandom algorithm. - */ - public String getSecureRandomAlgorithm() { - return secureRandomAlgorithm; - } - - /** - * Returns the selected cipher suites. The selection is the subset of supported - * suites that are both in the enabled suites and out of the disabled suites. - * - * @param supportedCipherSuites The initial cipher suites to restrict. - * @return The selected cipher suites. - */ - public String[] getSelectedCipherSuites(String[] supportedCipherSuites) { - Set resultSet = new HashSet(); - - if (supportedCipherSuites != null) { - for (String supportedCipherSuite : supportedCipherSuites) { - if (((getEnabledCipherSuites() == null) - || Arrays.asList(getEnabledCipherSuites()).contains(supportedCipherSuite)) - && ((getDisabledCipherSuites() == null) - || !Arrays.asList(getDisabledCipherSuites()).contains(supportedCipherSuite))) { - resultSet.add(supportedCipherSuite); - } - } - } - - String[] result = new String[resultSet.size()]; - return resultSet.toArray(result); - } - - /** - * Returns the selected SSL protocols. The selection is the subset of supported - * protocols whose name starts with the name of {@link #getEnabledProtocols()} name. - * - * @param supportedProtocols The selected SSL protocols. - * @return The selected SSL protocols. - */ - public String[] getSelectedSslProtocols(String[] supportedProtocols) { - Set resultSet = new HashSet(); - - if (supportedProtocols != null) { - for (String supportedProtocol : supportedProtocols) { - if (((getEnabledProtocols() == null) - || Arrays.asList(getEnabledProtocols()).contains(supportedProtocol)) - && ((getDisabledProtocols() == null) - || !Arrays.asList(getDisabledProtocols()).contains(supportedProtocol))) { - resultSet.add(supportedProtocol); - } - } - } - - String[] result = new String[resultSet.size()]; - return resultSet.toArray(result); - } - - /** - * Returns the name of the TrustManager algorithm. - * - * @return The name of the TrustManager algorithm. - */ - public String getTrustManagerAlgorithm() { - return trustManagerAlgorithm; - } - - /** - * Returns the password for the trust store keystore. - * - * @return The password for the trust store keystore. - */ - public char[] getTrustStorePassword() { - return trustStorePassword; - } - - /** - * Returns the path to the trust store (keystore) file. - * - * @return The path to the trust store (keystore) file. - */ - public String getTrustStorePath() { - return trustStorePath; - } - - /** - * Returns the name of the trust store (keystore) provider. - * - * @return The name of the trust store (keystore) provider. - */ - public String getTrustStoreProvider() { - return trustStoreProvider; - } - - /** - * Returns the KeyStore type of the trust store. - * - * @return The KeyStore type of the trust store. - */ - public String getTrustStoreType() { - return trustStoreType; - } - - /** - * Sets the following options according to parameters that may have been set up - * directly in the HttpsClientHelper or HttpsServerHelper parameters. See class - * Javadocs for the list of parameters supported. - * - * @param helperParameters Typically, the parameters that would have been - * obtained from HttpsServerHelper.getParameters() - */ - @Override - public void init(Series helperParameters) { - // Parses and set the disabled cipher suites - String[] disabledCipherSuitesArray = helperParameters.getValuesArray("disabledCipherSuites"); - Set disabledCipherSuites = new HashSet<>(); - - for (String disabledCipherSuiteSeries : disabledCipherSuitesArray) { + /** The whitespace-separated list of disabled cipher suites. */ + private volatile String[] disabledCipherSuites = null; + + /** The whitespace-separated list of disabled SSL protocols. */ + private volatile String[] disabledProtocols = null; + + /** The whitespace-separated list of enabled cipher suites. */ + private volatile String[] enabledCipherSuites = null; + + /** The whitespace-separated list of enabled SSL protocols. */ + private volatile String[] enabledProtocols = null; + + /** The name of the KeyManager algorithm. */ + private volatile String keyManagerAlgorithm = + System.getProperty("ssl.KeyManagerFactory.algorithm", "SunX509"); + + /** The password for the key in the keystore (as a String). */ + private volatile char[] keyStoreKeyPassword = + (System.getProperty( + "javax.net.ssl.keyPassword", + System.getProperty("javax.net.ssl.keyStorePassword")) + != null) + ? System.getProperty( + "javax.net.ssl.keyPassword", + System.getProperty("javax.net.ssl.keyStorePassword")) + .toCharArray() + : null; + + /** The password for the keystore (as a String). */ + private volatile char[] keyStorePassword = + (System.getProperty("javax.net.ssl.keyStorePassword") != null) + ? System.getProperty("javax.net.ssl.keyStorePassword").toCharArray() + : null; + + /** The path to the KeyStore file. */ + private volatile String keyStorePath = + System.getProperty( + "javax.net.ssl.keyStore", + (System.getProperty("user.home") != null) + ? ((System.getProperty("user.home").endsWith("/")) + ? System.getProperty("user.home") + ".keystore" + : System.getProperty("user.home") + "/.keystore") + : null); + + /** The name of the keystore provider. */ + private volatile String keyStoreProvider = System.getProperty("javax.net.ssl.keyStoreProvider"); + + /** The keyStore type of the keystore. */ + private volatile String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", "JKS"); + + /** Indicates if we require client certificate authentication. */ + private volatile boolean needClientAuthentication = false; + + /** The standard name of the protocol to use when creating the SSLContext. */ + private volatile String protocol = "TLS"; + + /** The name of the SecureRandom algorithm. */ + private volatile String secureRandomAlgorithm = null; + + /** The name of the TrustManager algorithm. */ + private volatile String trustManagerAlgorithm = + System.getProperty("ssl.TrustManagerFactory.algorithm", "SunX509"); + + /** The password for the trust store keystore. */ + private volatile char[] trustStorePassword = + (System.getProperty("javax.net.ssl.trustStorePassword") != null) + ? System.getProperty("javax.net.ssl.trustStorePassword").toCharArray() + : null; + + /** The path to the trust store (keystore) file. */ + private volatile String trustStorePath = System.getProperty("javax.net.ssl.trustStore"); + + /** The name of the trust store (keystore) provider. */ + private volatile String trustStoreProvider = + System.getProperty("javax.net.ssl.trustStoreProvider"); + + /** The KeyStore type of the trust store. */ + private volatile String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType"); + + /** Indicates if we would like client certificate authentication. */ + private volatile boolean wantClientAuthentication = false; + + /** This class is likely to contain sensitive information; cloning is therefore not allowed. */ + @Override + protected final DefaultSslContextFactory clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + /** + * Creates a configured and initialized SSLContext from the values set via the + * various setters of this class. If keyStorePath, + * keyStoreProvider, keyStoreType are all + * null, the SSLContext will be initialized with a + * null array of KeyManagers. Similarly, if + * trustStorePath, trustStoreProvider, + * trustStoreType are all null, a null + * array of TrustManagers will be used. + * + * @see SSLContext#init(javax.net.ssl.KeyManager[], + * javax.net.ssl.TrustManager[], SecureRandom) + */ + @Override + public javax.net.ssl.SSLContext createSslContext() throws Exception { + final javax.net.ssl.SSLContext result; + + final javax.net.ssl.KeyManagerFactory kmf; + if ((this.keyStorePath != null) + || (this.keyStoreProvider != null) + || (this.keyStoreType != null)) { + final KeyStore keyStore = + loadKeyStore( + this.keyStorePath, + this.keyStoreProvider, + this.keyStoreType, + this.keyStorePassword); + + // Creates the key-manager factory. + kmf = javax.net.ssl.KeyManagerFactory.getInstance(this.keyManagerAlgorithm); + kmf.init(keyStore, this.keyStoreKeyPassword); + } else { + kmf = null; + } + + final javax.net.ssl.TrustManagerFactory tmf; + if ((this.trustStorePath != null) + || (this.trustStoreProvider != null) + || (this.trustStoreType != null)) { + final KeyStore trustStore = + loadKeyStore( + this.trustStorePath, + this.trustStoreProvider, + this.trustStoreType, + this.trustStorePassword); + + // Creates the trust-manager factory. + tmf = javax.net.ssl.TrustManagerFactory.getInstance(this.trustManagerAlgorithm); + tmf.init(trustStore); + } else { + tmf = null; + } + + final SecureRandom sr; + if (this.secureRandomAlgorithm != null) { + sr = SecureRandom.getInstance(this.secureRandomAlgorithm); + } else { + sr = null; + } + + // Creates the SSL context + final javax.net.ssl.SSLContext sslContext = + javax.net.ssl.SSLContext.getInstance(this.protocol); + sslContext.init( + kmf != null ? kmf.getKeyManagers() : null, + tmf != null ? tmf.getTrustManagers() : null, + sr); + + // Wraps the SSL context to be able to set cipher suites and other + // properties after SSL engine creation, for example + result = createWrapper(sslContext); + return result; + } + + /** + * Creates a new {@link SSLContext} wrapper. Necessary to properly initialize the {@link + * SSLEngine} or {@link SSLSocketFactory} or {@link javax.net.ssl.SSLServerSocketFactory} + * created. + * + * @param sslContext The SSL context to wrap. + * @return The SSL context wrapper. + */ + protected javax.net.ssl.SSLContext createWrapper(javax.net.ssl.SSLContext sslContext) { + return new DefaultSslContext(this, sslContext); + } + + /** + * Returns the whitespace-separated list of disabled cipher suites. + * + * @return The whitespace-separated list of disabled cipher suites. + */ + public String[] getDisabledCipherSuites() { + return disabledCipherSuites; + } + + /** + * Returns the whitespace-separated list of disabled SSL protocols. + * + * @return The whitespace-separated list of disabled SSL protocols. + */ + public String[] getDisabledProtocols() { + return disabledProtocols; + } + + /** + * Returns the whitespace-separated list of enabled cipher suites. + * + * @return The whitespace-separated list of enabled cipher suites. + */ + public String[] getEnabledCipherSuites() { + return enabledCipherSuites; + } + + /** + * Returns the whitespace-separated list of enabled SSL protocols. + * + * @return The whitespace-separated list of enabled SSL protocols. + */ + public String[] getEnabledProtocols() { + return enabledProtocols; + } + + /** + * Returns the name of the KeyManager algorithm. + * + * @return The name of the KeyManager algorithm. + */ + public String getKeyManagerAlgorithm() { + return keyManagerAlgorithm; + } + + /** + * Returns the password for the key in the keystore (as a String). + * + * @return The password for the key in the keystore (as a String). + */ + public char[] getKeyStoreKeyPassword() { + return keyStoreKeyPassword; + } + + /** + * Returns the password for the keystore (as a String). + * + * @return The password for the keystore (as a String). + */ + public char[] getKeyStorePassword() { + return keyStorePassword; + } + + /** + * Returns the path to the KeyStore file. + * + * @return The path to the KeyStore file. + */ + public String getKeyStorePath() { + return keyStorePath; + } + + /** + * Returns the name of the keystore provider. + * + * @return The name of the keystore provider. + */ + public String getKeyStoreProvider() { + return keyStoreProvider; + } + + /** + * Returns the keyStore type of the keystore. + * + * @return The keyStore type of the keystore. + */ + public String getKeyStoreType() { + return keyStoreType; + } + + /** + * Returns the secure socket protocol name, "TLS" by default. + * + * @return The secure socket protocol. + */ + public String getProtocol() { + return this.protocol; + } + + /** + * Returns the name of the SecureRandom algorithm. + * + * @return The name of the SecureRandom algorithm. + */ + public String getSecureRandomAlgorithm() { + return secureRandomAlgorithm; + } + + /** + * Returns the selected cipher suites. The selection is the subset of supported suites that are + * both in the enabled suites and out of the disabled suites. + * + * @param supportedCipherSuites The initial cipher suites to restrict. + * @return The selected cipher suites. + */ + public String[] getSelectedCipherSuites(String[] supportedCipherSuites) { + Set resultSet = new HashSet(); + + if (supportedCipherSuites != null) { + for (String supportedCipherSuite : supportedCipherSuites) { + if (((getEnabledCipherSuites() == null) + || Arrays.asList(getEnabledCipherSuites()) + .contains(supportedCipherSuite)) + && ((getDisabledCipherSuites() == null) + || !Arrays.asList(getDisabledCipherSuites()) + .contains(supportedCipherSuite))) { + resultSet.add(supportedCipherSuite); + } + } + } + + String[] result = new String[resultSet.size()]; + return resultSet.toArray(result); + } + + /** + * Returns the selected SSL protocols. The selection is the subset of supported protocols whose + * name starts with the name of {@link #getEnabledProtocols()} name. + * + * @param supportedProtocols The selected SSL protocols. + * @return The selected SSL protocols. + */ + public String[] getSelectedSslProtocols(String[] supportedProtocols) { + Set resultSet = new HashSet(); + + if (supportedProtocols != null) { + for (String supportedProtocol : supportedProtocols) { + if (((getEnabledProtocols() == null) + || Arrays.asList(getEnabledProtocols()).contains(supportedProtocol)) + && ((getDisabledProtocols() == null) + || !Arrays.asList(getDisabledProtocols()) + .contains(supportedProtocol))) { + resultSet.add(supportedProtocol); + } + } + } + + String[] result = new String[resultSet.size()]; + return resultSet.toArray(result); + } + + /** + * Returns the name of the TrustManager algorithm. + * + * @return The name of the TrustManager algorithm. + */ + public String getTrustManagerAlgorithm() { + return trustManagerAlgorithm; + } + + /** + * Returns the password for the trust store keystore. + * + * @return The password for the trust store keystore. + */ + public char[] getTrustStorePassword() { + return trustStorePassword; + } + + /** + * Returns the path to the trust store (keystore) file. + * + * @return The path to the trust store (keystore) file. + */ + public String getTrustStorePath() { + return trustStorePath; + } + + /** + * Returns the name of the trust store (keystore) provider. + * + * @return The name of the trust store (keystore) provider. + */ + public String getTrustStoreProvider() { + return trustStoreProvider; + } + + /** + * Returns the KeyStore type of the trust store. + * + * @return The KeyStore type of the trust store. + */ + public String getTrustStoreType() { + return trustStoreType; + } + + /** + * Sets the following options according to parameters that may have been set up directly in the + * HttpsClientHelper or HttpsServerHelper parameters. See class Javadocs for the list of + * parameters supported. + * + * @param helperParameters Typically, the parameters that would have been obtained from + * HttpsServerHelper.getParameters() + */ + @Override + public void init(Series helperParameters) { + // Parses and set the disabled cipher suites + String[] disabledCipherSuitesArray = + helperParameters.getValuesArray("disabledCipherSuites"); + Set disabledCipherSuites = new HashSet<>(); + + for (String disabledCipherSuiteSeries : disabledCipherSuitesArray) { Collections.addAll(disabledCipherSuites, disabledCipherSuiteSeries.split(" ")); - } + } if (disabledCipherSuites.isEmpty()) { setDisabledCipherSuites(null); @@ -545,12 +574,12 @@ public void init(Series helperParameters) { } // Parses and set the disabled protocols - String[] disabledProtocolsArray = helperParameters.getValuesArray("disabledProtocols"); - Set disabledProtocols = new HashSet<>(); + String[] disabledProtocolsArray = helperParameters.getValuesArray("disabledProtocols"); + Set disabledProtocols = new HashSet<>(); - for (String disabledProtocolsSeries : disabledProtocolsArray) { + for (String disabledProtocolsSeries : disabledProtocolsArray) { Collections.addAll(disabledProtocols, disabledProtocolsSeries.split(" ")); - } + } if (disabledProtocols.isEmpty()) { setDisabledProtocols(null); @@ -561,12 +590,12 @@ public void init(Series helperParameters) { } // Parses and set the enabled cipher suites - String[] enabledCipherSuitesArray = helperParameters.getValuesArray("enabledCipherSuites"); - Set enabledCipherSuites = new HashSet<>(); + String[] enabledCipherSuitesArray = helperParameters.getValuesArray("enabledCipherSuites"); + Set enabledCipherSuites = new HashSet<>(); - for (String enabledCipherSuiteSeries : enabledCipherSuitesArray) { + for (String enabledCipherSuiteSeries : enabledCipherSuitesArray) { Collections.addAll(enabledCipherSuites, enabledCipherSuiteSeries.split(" ")); - } + } if (enabledCipherSuites.isEmpty()) { setEnabledCipherSuites(null); @@ -577,12 +606,12 @@ public void init(Series helperParameters) { } // Parses and set the enabled protocols - String[] enabledProtocolsArray = helperParameters.getValuesArray("enabledProtocols"); - Set enabledProtocols = new HashSet<>(); + String[] enabledProtocolsArray = helperParameters.getValuesArray("enabledProtocols"); + Set enabledProtocols = new HashSet<>(); - for (String enabledProtocolSeries : enabledProtocolsArray) { + for (String enabledProtocolSeries : enabledProtocolsArray) { Collections.addAll(enabledProtocols, enabledProtocolSeries.split(" ")); - } + } if (enabledProtocols.isEmpty()) { setEnabledProtocols(null); @@ -592,304 +621,319 @@ public void init(Series helperParameters) { setEnabledProtocols(enabledProtocolsArray); } - setKeyManagerAlgorithm(helperParameters.getFirstValue("keyManagerAlgorithm", true, - System.getProperty("ssl.KeyManagerFactory.algorithm", "SunX509"))); - setKeyStorePassword(helperParameters.getFirstValue("keyStorePassword", true, - System.getProperty("javax.net.ssl.keyStorePassword", ""))); - setKeyStoreKeyPassword( - helperParameters.getFirstValue("keyPassword", true, System.getProperty("javax.net.ssl.keyPassword"))); - - if (this.keyStoreKeyPassword == null) { - this.keyStoreKeyPassword = this.keyStorePassword; - } - - setKeyStorePath( - helperParameters.getFirstValue("keyStorePath", true, System.getProperty("javax.net.ssl.keyStore"))); - setKeyStoreType( - helperParameters.getFirstValue("keyStoreType", true, System.getProperty("javax.net.ssl.keyStoreType"))); - setNeedClientAuthentication( - Boolean.parseBoolean(helperParameters.getFirstValue("needClientAuthentication", true, "false"))); - setProtocol(helperParameters.getFirstValue("protocol", true, "TLS")); - setSecureRandomAlgorithm(helperParameters.getFirstValue("secureRandomAlgorithm", true)); - setTrustManagerAlgorithm(helperParameters.getFirstValue("trustManagerAlgorithm", true, - System.getProperty("ssl.TrustManagerFactory.algorithm", "SunX509"))); - setTrustStorePassword(helperParameters.getFirstValue("trustStorePassword", true, - System.getProperty("javax.net.ssl.trustStorePassword"))); - setTrustStorePath( - helperParameters.getFirstValue("trustStorePath", true, System.getProperty("javax.net.ssl.trustStore"))); - setTrustStoreType(helperParameters.getFirstValue("trustStoreType", true, - System.getProperty("javax.net.ssl.trustStoreType"))); - setWantClientAuthentication( - Boolean.parseBoolean(helperParameters.getFirstValue("wantClientAuthentication", true, "false"))); - } - - /** - * Indicates if we require client certificate authentication. - * - * @return True if we require client certificate authentication. - */ - public boolean isNeedClientAuthentication() { - return needClientAuthentication; - } - - /** - * Indicates if we would like client certificate authentication. - * - * @return True if we would like client certificate authentication. - */ - public boolean isWantClientAuthentication() { - return wantClientAuthentication; - } - - /** - * Loads a keystore according to its file path, type and password. - * @param path The file path of the keystore. - * @param provider The name of the keystore provider. - * @param type The keystore type of the keystore. - * @param password the optional password of the keystore. - * @return a keystore. - * @throws Exception - */ - protected KeyStore loadKeyStore(String path, String provider, String type, char[] password) throws Exception { - final String nonNullKeyStoreType = (type != null) ? type : KeyStore.getDefaultType(); - final KeyStore keyStore = (provider != null) - ? KeyStore.getInstance(nonNullKeyStoreType, provider) - : KeyStore.getInstance(nonNullKeyStoreType); - - try (FileInputStream keyStoreInputStream = ((path != null) && (!"NONE".equals(path))) - ? new FileInputStream(path) - : null) { - keyStore.load(keyStoreInputStream, password); - } - - return keyStore; - } - - /** - * Sets the whitespace-separated list of disabled cipher suites. - * - * @param disabledCipherSuites The whitespace-separated list of disabled cipher - * suites. - */ - public void setDisabledCipherSuites(String[] disabledCipherSuites) { - this.disabledCipherSuites = disabledCipherSuites; - } - - /** - * Sets the whitespace-separated list of disabled SSL protocols. - * - * @param disabledProtocols The whitespace-separated list of disabled SSL - * protocols. - */ - public void setDisabledProtocols(String[] disabledProtocols) { - this.disabledProtocols = disabledProtocols; - } - - /** - * Sets the whitespace-separated list of enabled cipher suites. - * - * @param enabledCipherSuites The whitespace-separated list of enabled cipher - * suites. - */ - public void setEnabledCipherSuites(String[] enabledCipherSuites) { - this.enabledCipherSuites = enabledCipherSuites; - } - - /** - * Sets the standard name of the protocols to use when creating the SSL sockets - * or engines. - * - * @param enabledProtocols The standard name of the protocols to use when - * creating the SSL sockets or engines. - */ - public void setEnabledProtocols(String[] enabledProtocols) { - this.enabledProtocols = enabledProtocols; - } - - /** - * Sets the KeyManager algorithm. The default value is that of the - * ssl.KeyManagerFactory.algorithm system property, or "SunX509" - * if the system property has not been set up. - * - * @param keyManagerAlgorithm The KeyManager algorithm. - */ - public void setKeyManagerAlgorithm(String keyManagerAlgorithm) { - this.keyManagerAlgorithm = keyManagerAlgorithm; - } - - /** - * Sets the password of the key in the keystore. The default value is that of - * the javax.net.ssl.keyPassword system property, falling back to - * javax.net.ssl.keyStorePassword. This system property name is not - * standard. - * - * @param keyStoreKeyPassword The password of the key in the keystore. - */ - public void setKeyStoreKeyPassword(char[] keyStoreKeyPassword) { - this.keyStoreKeyPassword = keyStoreKeyPassword; - } - - /** - * Sets the password of the key in the keystore. The default value is that of - * the javax.net.ssl.keyPassword system property, falling back to - * javax.net.ssl.keyStorePassword. This system property name is not - * standard. - * - * @param keyStoreKeyPassword The password of the key in the keystore. - */ - public void setKeyStoreKeyPassword(String keyStoreKeyPassword) { - this.keyStoreKeyPassword = (keyStoreKeyPassword != null) ? keyStoreKeyPassword.toCharArray() : null; - } - - /** - * Sets the keystore password. The default value is that of the - * javax.net.ssl.keyStorePassword system property. - * - * @param keyStorePassword Sets the keystore password. - */ - public void setKeyStorePassword(char[] keyStorePassword) { - this.keyStorePassword = keyStorePassword; - } - - /** - * Sets the keystore password. The default value is that of the - * javax.net.ssl.keyStorePassword system property. - * - * @param keyStorePassword Sets the keystore password. - */ - public void setKeyStorePassword(String keyStorePassword) { - this.keyStorePassword = (keyStorePassword != null) ? keyStorePassword.toCharArray() : null; - } - - /** - * Sets the path to the keystore file. The default value is that of the - * javax.net.ssl.keyStore system property. - * - * @param keyStorePath The path to the keystore file. - */ - public void setKeyStorePath(String keyStorePath) { - this.keyStorePath = keyStorePath; - } - - /** - * Sets the name of the keystore provider. The default value is that of the - * javax.net.ssl.keyStoreProvider system property. - * - * @param keyStoreProvider The name of the keystore provider. - */ - public void setKeyStoreProvider(String keyStoreProvider) { - this.keyStoreProvider = keyStoreProvider; - } - - /** - * Sets the KeyStore type of the keystore. The default value is that of the - * javax.net.ssl.keyStoreType system property. - * - * @param keyStoreType The KeyStore type of the keystore. - */ - public void setKeyStoreType(String keyStoreType) { - this.keyStoreType = keyStoreType; - } - - /** - * Indicates if we require client certificate authentication. The default value - * is false. - * - * @param needClientAuthentication True if we require client certificate - * authentication. - */ - public void setNeedClientAuthentication(boolean needClientAuthentication) { - this.needClientAuthentication = needClientAuthentication; - } - - /** - * Sets the secure socket protocol name, "TLS" by default. - * - * @param protocol Name of the secure socket protocol to use. - */ - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - /** - * Sets the SecureRandom algorithm. The default value is null, in which - * case the default SecureRandom would be used. - * - * @param secureRandomAlgorithm The SecureRandom algorithm. - */ - public void setSecureRandomAlgorithm(String secureRandomAlgorithm) { - this.secureRandomAlgorithm = secureRandomAlgorithm; - } - - /** - * Sets the TrustManager algorithm. The default value is that of the - * ssl.TrustManagerFactory.algorithm system property, or "SunX509" - * if the system property has not been set up. - * - * @param trustManagerAlgorithm The TrustManager algorithm. - */ - public void setTrustManagerAlgorithm(String trustManagerAlgorithm) { - this.trustManagerAlgorithm = trustManagerAlgorithm; - } - - /** - * Sets the password of the trust store KeyStore. The default value is that of - * the javax.net.ssl.trustStorePassword system property. - * - * @param trustStorePassword The password of the trust store KeyStore. - */ - public void setTrustStorePassword(char[] trustStorePassword) { - this.trustStorePassword = trustStorePassword; - } - - /** - * Sets the password of the trust store KeyStore. The default value is that of - * the javax.net.ssl.trustStorePassword system property. - * - * @param trustStorePassword The password of the trust store KeyStore. - */ - public void setTrustStorePassword(String trustStorePassword) { - this.trustStorePassword = (trustStorePassword != null) ? trustStorePassword.toCharArray() : null; - } - - /** - * Sets the path to the trust store KeyStore. The default value is that of the - * javax.net.ssl.trustStore system property. - * - * @param trustStorePath The trustStorePath to set - */ - public void setTrustStorePath(String trustStorePath) { - this.trustStorePath = trustStorePath; - } - - /** - * Sets the name of the trust store provider. The default value is that of the - * javax.net.ssl.trustStoreProvider system property. - * - * @param trustStoreProvider The name of the trust store provider. - */ - public void setTrustStoreProvider(String trustStoreProvider) { - this.trustStoreProvider = trustStoreProvider; - } - - /** - * Sets the KeyStore type of the trust store. The default value is that of the - * javax.net.ssl.trustStoreType system property. - * - * @param trustStoreType The KeyStore type of the trust store. - */ - public void setTrustStoreType(String trustStoreType) { - this.trustStoreType = trustStoreType; - } - - /** - * Indicates if we would like client certificate authentication. The default - * value is false. - * - * @param wantClientAuthentication True if we would like client certificate - * authentication. - */ - public void setWantClientAuthentication(boolean wantClientAuthentication) { - this.wantClientAuthentication = wantClientAuthentication; - } + setKeyManagerAlgorithm( + helperParameters.getFirstValue( + "keyManagerAlgorithm", + true, + System.getProperty("ssl.KeyManagerFactory.algorithm", "SunX509"))); + setKeyStorePassword( + helperParameters.getFirstValue( + "keyStorePassword", + true, + System.getProperty("javax.net.ssl.keyStorePassword", ""))); + setKeyStoreKeyPassword( + helperParameters.getFirstValue( + "keyPassword", true, System.getProperty("javax.net.ssl.keyPassword"))); + + if (this.keyStoreKeyPassword == null) { + this.keyStoreKeyPassword = this.keyStorePassword; + } + + setKeyStorePath( + helperParameters.getFirstValue( + "keyStorePath", true, System.getProperty("javax.net.ssl.keyStore"))); + setKeyStoreType( + helperParameters.getFirstValue( + "keyStoreType", true, System.getProperty("javax.net.ssl.keyStoreType"))); + setNeedClientAuthentication( + Boolean.parseBoolean( + helperParameters.getFirstValue("needClientAuthentication", true, "false"))); + setProtocol(helperParameters.getFirstValue("protocol", true, "TLS")); + setSecureRandomAlgorithm(helperParameters.getFirstValue("secureRandomAlgorithm", true)); + setTrustManagerAlgorithm( + helperParameters.getFirstValue( + "trustManagerAlgorithm", + true, + System.getProperty("ssl.TrustManagerFactory.algorithm", "SunX509"))); + setTrustStorePassword( + helperParameters.getFirstValue( + "trustStorePassword", + true, + System.getProperty("javax.net.ssl.trustStorePassword"))); + setTrustStorePath( + helperParameters.getFirstValue( + "trustStorePath", true, System.getProperty("javax.net.ssl.trustStore"))); + setTrustStoreType( + helperParameters.getFirstValue( + "trustStoreType", + true, + System.getProperty("javax.net.ssl.trustStoreType"))); + setWantClientAuthentication( + Boolean.parseBoolean( + helperParameters.getFirstValue("wantClientAuthentication", true, "false"))); + } + + /** + * Indicates if we require client certificate authentication. + * + * @return True if we require client certificate authentication. + */ + public boolean isNeedClientAuthentication() { + return needClientAuthentication; + } + + /** + * Indicates if we would like client certificate authentication. + * + * @return True if we would like client certificate authentication. + */ + public boolean isWantClientAuthentication() { + return wantClientAuthentication; + } + + /** + * Loads a keystore according to its file path, type and password. + * + * @param path The file path of the keystore. + * @param provider The name of the keystore provider. + * @param type The keystore type of the keystore. + * @param password the optional password of the keystore. + * @return a keystore. + * @throws Exception + */ + protected KeyStore loadKeyStore(String path, String provider, String type, char[] password) + throws Exception { + final String nonNullKeyStoreType = (type != null) ? type : KeyStore.getDefaultType(); + final KeyStore keyStore = + (provider != null) + ? KeyStore.getInstance(nonNullKeyStoreType, provider) + : KeyStore.getInstance(nonNullKeyStoreType); + + try (FileInputStream keyStoreInputStream = + ((path != null) && (!"NONE".equals(path))) ? new FileInputStream(path) : null) { + keyStore.load(keyStoreInputStream, password); + } + + return keyStore; + } + + /** + * Sets the whitespace-separated list of disabled cipher suites. + * + * @param disabledCipherSuites The whitespace-separated list of disabled cipher suites. + */ + public void setDisabledCipherSuites(String[] disabledCipherSuites) { + this.disabledCipherSuites = disabledCipherSuites; + } + + /** + * Sets the whitespace-separated list of disabled SSL protocols. + * + * @param disabledProtocols The whitespace-separated list of disabled SSL protocols. + */ + public void setDisabledProtocols(String[] disabledProtocols) { + this.disabledProtocols = disabledProtocols; + } + + /** + * Sets the whitespace-separated list of enabled cipher suites. + * + * @param enabledCipherSuites The whitespace-separated list of enabled cipher suites. + */ + public void setEnabledCipherSuites(String[] enabledCipherSuites) { + this.enabledCipherSuites = enabledCipherSuites; + } + + /** + * Sets the standard name of the protocols to use when creating the SSL sockets or engines. + * + * @param enabledProtocols The standard name of the protocols to use when creating the SSL + * sockets or engines. + */ + public void setEnabledProtocols(String[] enabledProtocols) { + this.enabledProtocols = enabledProtocols; + } + + /** + * Sets the KeyManager algorithm. The default value is that of the + * ssl.KeyManagerFactory.algorithm system property, or "SunX509" if the system + * property has not been set up. + * + * @param keyManagerAlgorithm The KeyManager algorithm. + */ + public void setKeyManagerAlgorithm(String keyManagerAlgorithm) { + this.keyManagerAlgorithm = keyManagerAlgorithm; + } + + /** + * Sets the password of the key in the keystore. The default value is that of the + * javax.net.ssl.keyPassword system property, falling back to + * javax.net.ssl.keyStorePassword. This system property name is not standard. + * + * @param keyStoreKeyPassword The password of the key in the keystore. + */ + public void setKeyStoreKeyPassword(char[] keyStoreKeyPassword) { + this.keyStoreKeyPassword = keyStoreKeyPassword; + } + + /** + * Sets the password of the key in the keystore. The default value is that of the + * javax.net.ssl.keyPassword system property, falling back to + * javax.net.ssl.keyStorePassword. This system property name is not standard. + * + * @param keyStoreKeyPassword The password of the key in the keystore. + */ + public void setKeyStoreKeyPassword(String keyStoreKeyPassword) { + this.keyStoreKeyPassword = + (keyStoreKeyPassword != null) ? keyStoreKeyPassword.toCharArray() : null; + } + + /** + * Sets the keystore password. The default value is that of the + * javax.net.ssl.keyStorePassword system property. + * + * @param keyStorePassword Sets the keystore password. + */ + public void setKeyStorePassword(char[] keyStorePassword) { + this.keyStorePassword = keyStorePassword; + } + + /** + * Sets the keystore password. The default value is that of the + * javax.net.ssl.keyStorePassword system property. + * + * @param keyStorePassword Sets the keystore password. + */ + public void setKeyStorePassword(String keyStorePassword) { + this.keyStorePassword = (keyStorePassword != null) ? keyStorePassword.toCharArray() : null; + } + + /** + * Sets the path to the keystore file. The default value is that of the + * javax.net.ssl.keyStore system property. + * + * @param keyStorePath The path to the keystore file. + */ + public void setKeyStorePath(String keyStorePath) { + this.keyStorePath = keyStorePath; + } + + /** + * Sets the name of the keystore provider. The default value is that of the + * javax.net.ssl.keyStoreProvider system property. + * + * @param keyStoreProvider The name of the keystore provider. + */ + public void setKeyStoreProvider(String keyStoreProvider) { + this.keyStoreProvider = keyStoreProvider; + } + + /** + * Sets the KeyStore type of the keystore. The default value is that of the + * javax.net.ssl.keyStoreType system property. + * + * @param keyStoreType The KeyStore type of the keystore. + */ + public void setKeyStoreType(String keyStoreType) { + this.keyStoreType = keyStoreType; + } + + /** + * Indicates if we require client certificate authentication. The default value is false. + * + * @param needClientAuthentication True if we require client certificate authentication. + */ + public void setNeedClientAuthentication(boolean needClientAuthentication) { + this.needClientAuthentication = needClientAuthentication; + } + + /** + * Sets the secure socket protocol name, "TLS" by default. + * + * @param protocol Name of the secure socket protocol to use. + */ + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + /** + * Sets the SecureRandom algorithm. The default value is null, in which case the default + * SecureRandom would be used. + * + * @param secureRandomAlgorithm The SecureRandom algorithm. + */ + public void setSecureRandomAlgorithm(String secureRandomAlgorithm) { + this.secureRandomAlgorithm = secureRandomAlgorithm; + } + + /** + * Sets the TrustManager algorithm. The default value is that of the + * ssl.TrustManagerFactory.algorithm system property, or "SunX509" if the system + * property has not been set up. + * + * @param trustManagerAlgorithm The TrustManager algorithm. + */ + public void setTrustManagerAlgorithm(String trustManagerAlgorithm) { + this.trustManagerAlgorithm = trustManagerAlgorithm; + } + + /** + * Sets the password of the trust store KeyStore. The default value is that of the + * javax.net.ssl.trustStorePassword system property. + * + * @param trustStorePassword The password of the trust store KeyStore. + */ + public void setTrustStorePassword(char[] trustStorePassword) { + this.trustStorePassword = trustStorePassword; + } + + /** + * Sets the password of the trust store KeyStore. The default value is that of the + * javax.net.ssl.trustStorePassword system property. + * + * @param trustStorePassword The password of the trust store KeyStore. + */ + public void setTrustStorePassword(String trustStorePassword) { + this.trustStorePassword = + (trustStorePassword != null) ? trustStorePassword.toCharArray() : null; + } + + /** + * Sets the path to the trust store KeyStore. The default value is that of the + * javax.net.ssl.trustStore system property. + * + * @param trustStorePath The trustStorePath to set + */ + public void setTrustStorePath(String trustStorePath) { + this.trustStorePath = trustStorePath; + } + + /** + * Sets the name of the trust store provider. The default value is that of the + * javax.net.ssl.trustStoreProvider system property. + * + * @param trustStoreProvider The name of the trust store provider. + */ + public void setTrustStoreProvider(String trustStoreProvider) { + this.trustStoreProvider = trustStoreProvider; + } + + /** + * Sets the KeyStore type of the trust store. The default value is that of the + * javax.net.ssl.trustStoreType system property. + * + * @param trustStoreType The KeyStore type of the trust store. + */ + public void setTrustStoreType(String trustStoreType) { + this.trustStoreType = trustStoreType; + } + + /** + * Indicates if we would like client certificate authentication. The default value is false. + * + * @param wantClientAuthentication True if we would like client certificate authentication. + */ + public void setWantClientAuthentication(boolean wantClientAuthentication) { + this.wantClientAuthentication = wantClientAuthentication; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java index 01406d317e..737726c3b5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java @@ -1,48 +1,46 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.ssl; +import javax.net.ssl.SSLContext; import org.restlet.data.Parameter; import org.restlet.util.Series; -import javax.net.ssl.SSLContext; - /** - * This is an abstract factory that produces configured and initialized - * instances of SSLContext. Concrete implementations of SslContextFactory must - * implement {@link #createSslContext()}, which should typically consist of: - * + * This is an abstract factory that produces configured and initialized instances of SSLContext. + * Concrete implementations of SslContextFactory must implement {@link #createSslContext()}, which + * should typically consist of: + * *

  *    SSLContext sslContext = SSLContext.getInstance(...);
  *    ...
  *    sslContext.init(..., ..., ...);
  *    return sslContext;
  * 
- * + * * @author Bruno Harbulot * @see SSLContext */ public abstract class SslContextFactory { - /** - * Creates a configured and initialized SSLContext. - * - * @return A configured and initialized SSLContext. - * @throws Exception - */ - public abstract SSLContext createSslContext() throws Exception; + /** + * Creates a configured and initialized SSLContext. + * + * @return A configured and initialized SSLContext. + * @throws Exception + */ + public abstract SSLContext createSslContext() throws Exception; - /** - * Initialize the factory with the given connector parameters. - * - * @param parameters The connector parameters. - */ - public abstract void init(Series parameters); + /** + * Initialize the factory with the given connector parameters. + * + * @param parameters The connector parameters. + */ + public abstract void init(Series parameters); } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java index 14474389e3..2d941fff0b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java @@ -1,57 +1,56 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.ssl; -import org.restlet.Context; -import org.restlet.engine.RestletHelper; - import java.lang.reflect.InvocationTargetException; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.engine.RestletHelper; /** * Various HTTPS utilities. - * + * * @author Jerome Louvel */ public class SslUtils { - /** Cache of SSL key sizes for various cipher suites. */ - private final static ConcurrentMap keySizesCache = new ConcurrentHashMap(); - - /** - * Extract the SSL key size of a given cipher suite. - * - * @param sslCipherSuite The SSL cipher suite. - * @return The SSL key size. - */ - public static Integer extractKeySize(String sslCipherSuite) { - Integer keySize = keySizesCache.get(sslCipherSuite); - - if (keySize == null) { - final int encAlgorithmIndex = sslCipherSuite.indexOf("WITH_"); - if (encAlgorithmIndex >= 0) { - final String encAlgorithm = sslCipherSuite.substring(encAlgorithmIndex + 5); - - /* - * (Encryption algorithms and key sizes, quoted from RFC 2246) - * - * Key Expanded Effective IV Block Cipher Type Material Key Material Key Bits - * Size Size - * - * NULL Stream 0 0 0 0 N/A IDEA_CBC Block 16 16 128 8 8 RC2_CBC_40 Block 5 16 40 - * 8 8 RC4_40 Stream 5 16 40 0 N/A RC4_128 Stream 16 16 128 0 N/A DES40_CBC - * Block 5 8 40 8 8 DES_CBC Block 8 8 56 8 8 3DES_EDE_CBC Block 24 24 168 8 8 - */ + /** Cache of SSL key sizes for various cipher suites. */ + private static final ConcurrentMap keySizesCache = + new ConcurrentHashMap(); + + /** + * Extract the SSL key size of a given cipher suite. + * + * @param sslCipherSuite The SSL cipher suite. + * @return The SSL key size. + */ + public static Integer extractKeySize(String sslCipherSuite) { + Integer keySize = keySizesCache.get(sslCipherSuite); + + if (keySize == null) { + final int encAlgorithmIndex = sslCipherSuite.indexOf("WITH_"); + if (encAlgorithmIndex >= 0) { + final String encAlgorithm = sslCipherSuite.substring(encAlgorithmIndex + 5); + + /* + * (Encryption algorithms and key sizes, quoted from RFC 2246) + * + * Key Expanded Effective IV Block Cipher Type Material Key Material Key Bits + * Size Size + * + * NULL Stream 0 0 0 0 N/A IDEA_CBC Block 16 16 128 8 8 RC2_CBC_40 Block 5 16 40 + * 8 8 RC4_40 Stream 5 16 40 0 N/A RC4_128 Stream 16 16 128 0 N/A DES40_CBC + * Block 5 8 40 8 8 DES_CBC Block 8 8 56 8 8 3DES_EDE_CBC Block 24 24 168 8 8 + */ if (encAlgorithm.startsWith("NULL_")) { keySize = 0; } else if (encAlgorithm.startsWith("IDEA_CBC_")) { @@ -76,7 +75,7 @@ public static Integer extractKeySize(String sslCipherSuite) { keySize = Integer.valueOf(st.nextToken()); break; } catch (NumberFormatException e) { -// Tokens that are not integers are ignored. + // Tokens that are not integers are ignored. } } } @@ -85,69 +84,88 @@ public static Integer extractKeySize(String sslCipherSuite) { keySizesCache.put(sslCipherSuite, keySize); } } - } - - return keySize; - } - - /** - * Returns the SSL context factory. It first looks for a "sslContextFactory" - * attribute (instance), then for a "sslContextFactory" parameter (class name to - * instantiate). - * - * @param helper The helper to use. - * - * @return The SSL context factory. - */ - public static SslContextFactory getSslContextFactory(RestletHelper helper) { - - SslContextFactory result = (SslContextFactory) ((helper.getContext() == null) - ? null - : helper.getContext().getAttributes().get("sslContextFactory")); - - if (result == null) { - String[] sslContextFactoryNames = helper.getHelpedParameters().getValuesArray("sslContextFactory"); - - if (sslContextFactoryNames != null) { - for (String sslContextFactoryName : sslContextFactoryNames) { - if ((result == null) && (sslContextFactoryName != null)) { - try { - Class sslContextFactoryClass = Class - .forName(sslContextFactoryName).asSubclass(SslContextFactory.class); - result = sslContextFactoryClass.getDeclaredConstructor().newInstance(); - result.init(helper.getHelpedParameters()); - } catch (ClassNotFoundException e) { - Context.getCurrentLogger().log(Level.WARNING, - "Unable to find SslContextFactory class: " + sslContextFactoryName, e); - } catch (ClassCastException e) { - Context.getCurrentLogger().log(Level.WARNING, - "Class " + sslContextFactoryName + " does not implement SslContextFactory", e); - } catch (InstantiationException | NoSuchMethodException e) { - Context.getCurrentLogger().log(Level.WARNING, "Could not instantiate class " - + sslContextFactoryName + " with default constructor", e); - } catch (IllegalAccessException e) { - Context.getCurrentLogger().log(Level.WARNING, - "Illegal access when instantiating class " + sslContextFactoryName, e); - } catch (InvocationTargetException e) { + } + + return keySize; + } + + /** + * Returns the SSL context factory. It first looks for a "sslContextFactory" attribute + * (instance), then for a "sslContextFactory" parameter (class name to instantiate). + * + * @param helper The helper to use. + * @return The SSL context factory. + */ + public static SslContextFactory getSslContextFactory(RestletHelper helper) { + + SslContextFactory result = + (SslContextFactory) + ((helper.getContext() == null) + ? null + : helper.getContext().getAttributes().get("sslContextFactory")); + + if (result == null) { + String[] sslContextFactoryNames = + helper.getHelpedParameters().getValuesArray("sslContextFactory"); + + if (sslContextFactoryNames != null) { + for (String sslContextFactoryName : sslContextFactoryNames) { + if ((result == null) && (sslContextFactoryName != null)) { + try { + Class sslContextFactoryClass = + Class.forName(sslContextFactoryName) + .asSubclass(SslContextFactory.class); + result = sslContextFactoryClass.getDeclaredConstructor().newInstance(); + result.init(helper.getHelpedParameters()); + } catch (ClassNotFoundException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to find SslContextFactory class: " + + sslContextFactoryName, + e); + } catch (ClassCastException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Class " + + sslContextFactoryName + + " does not implement SslContextFactory", + e); + } catch (InstantiationException | NoSuchMethodException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Could not instantiate class " + + sslContextFactoryName + + " with default constructor", + e); + } catch (IllegalAccessException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Illegal access when instantiating class " + + sslContextFactoryName, + e); + } catch (InvocationTargetException e) { throw new RuntimeException(e); } } - } - } - } - - if (result == null) { - result = new DefaultSslContextFactory(); - result.init(helper.getHelpedParameters()); - } - - return result; - } - - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private SslUtils() { - } + } + } + } + + if (result == null) { + result = new DefaultSslContextFactory(); + result.init(helper.getHelpedParameters()); + } + + return result; + } + + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private SslUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java index 074eabbff9..3505316b24 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java @@ -1,124 +1,133 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.ssl; -import javax.net.ssl.*; import java.security.KeyManagementException; import java.security.SecureRandom; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLContextSpi; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSessionContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; /** - * Default SSL context SPI capable or setting additional properties on the - * created SSL engines and socket factories. - * + * Default SSL context SPI capable or setting additional properties on the created SSL engines and + * socket factories. + * * @author Jerome Louvel */ public class WrapperSslContextSpi extends SSLContextSpi { - /** The parent SSL context factory. */ - private final DefaultSslContextFactory contextFactory; - - /** The wrapped SSL context. */ - private final SSLContext wrappedContext; - - /** - * Constructor. - * - * @param contextFactory The parent SSL context factory. - * @param wrappedContext The wrapped SSL context. - */ - public WrapperSslContextSpi(DefaultSslContextFactory contextFactory, SSLContext wrappedContext) { - this.contextFactory = contextFactory; - this.wrappedContext = wrappedContext; - } - - @Override - protected SSLEngine engineCreateSSLEngine() { - SSLEngine result = getWrappedContext().createSSLEngine(); - initEngine(result); - return result; - } - - @Override - protected SSLEngine engineCreateSSLEngine(String peerHost, int peerPort) { - SSLEngine result = getWrappedContext().createSSLEngine(peerHost, peerPort); - initEngine(result); - return result; - } - - @Override - protected SSLSessionContext engineGetClientSessionContext() { - return getWrappedContext().getClientSessionContext(); - } - - @Override - protected SSLSessionContext engineGetServerSessionContext() { - return getWrappedContext().getServerSessionContext(); - } - - @Override - protected SSLServerSocketFactory engineGetServerSocketFactory() { - return new WrapperSslServerSocketFactory(getContextFactory(), getWrappedContext().getServerSocketFactory()); - } - - @Override - protected SSLSocketFactory engineGetSocketFactory() { - return new WrapperSslSocketFactory(getContextFactory(), getWrappedContext().getSocketFactory()); - } - - @Override - protected void engineInit(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws KeyManagementException { - getWrappedContext().init(km, tm, random); - } - - /** - * Returns the parent SSL context factory. - * - * @return The parent SSL context factory. - */ - protected DefaultSslContextFactory getContextFactory() { - return contextFactory; - } - - /** - * Returns the wrapped SSL context. - * - * @return The wrapped SSL context. - */ - protected SSLContext getWrappedContext() { - return wrappedContext; - } - - /** - * Initializes the SSL engine with additional parameters from the SSL context - * factory. - * - * @param sslEngine The SSL engine to initialize. - */ - protected void initEngine(SSLEngine sslEngine) { - if (getContextFactory().isNeedClientAuthentication()) { - sslEngine.setNeedClientAuth(true); - } else if (getContextFactory().isWantClientAuthentication()) { - sslEngine.setWantClientAuth(true); - } - - if ((getContextFactory().getEnabledCipherSuites() != null) - || (getContextFactory().getDisabledCipherSuites() != null)) { - sslEngine.setEnabledCipherSuites( - getContextFactory().getSelectedCipherSuites(sslEngine.getSupportedCipherSuites())); - } - - if ((getContextFactory().getEnabledProtocols() != null) - || (getContextFactory().getDisabledProtocols() != null)) { - sslEngine.setEnabledProtocols( - getContextFactory().getSelectedSslProtocols(sslEngine.getSupportedProtocols())); - } - } - + /** The parent SSL context factory. */ + private final DefaultSslContextFactory contextFactory; + + /** The wrapped SSL context. */ + private final SSLContext wrappedContext; + + /** + * Constructor. + * + * @param contextFactory The parent SSL context factory. + * @param wrappedContext The wrapped SSL context. + */ + public WrapperSslContextSpi( + DefaultSslContextFactory contextFactory, SSLContext wrappedContext) { + this.contextFactory = contextFactory; + this.wrappedContext = wrappedContext; + } + + @Override + protected SSLEngine engineCreateSSLEngine() { + SSLEngine result = getWrappedContext().createSSLEngine(); + initEngine(result); + return result; + } + + @Override + protected SSLEngine engineCreateSSLEngine(String peerHost, int peerPort) { + SSLEngine result = getWrappedContext().createSSLEngine(peerHost, peerPort); + initEngine(result); + return result; + } + + @Override + protected SSLSessionContext engineGetClientSessionContext() { + return getWrappedContext().getClientSessionContext(); + } + + @Override + protected SSLSessionContext engineGetServerSessionContext() { + return getWrappedContext().getServerSessionContext(); + } + + @Override + protected SSLServerSocketFactory engineGetServerSocketFactory() { + return new WrapperSslServerSocketFactory( + getContextFactory(), getWrappedContext().getServerSocketFactory()); + } + + @Override + protected SSLSocketFactory engineGetSocketFactory() { + return new WrapperSslSocketFactory( + getContextFactory(), getWrappedContext().getSocketFactory()); + } + + @Override + protected void engineInit(KeyManager[] km, TrustManager[] tm, SecureRandom random) + throws KeyManagementException { + getWrappedContext().init(km, tm, random); + } + + /** + * Returns the parent SSL context factory. + * + * @return The parent SSL context factory. + */ + protected DefaultSslContextFactory getContextFactory() { + return contextFactory; + } + + /** + * Returns the wrapped SSL context. + * + * @return The wrapped SSL context. + */ + protected SSLContext getWrappedContext() { + return wrappedContext; + } + + /** + * Initializes the SSL engine with additional parameters from the SSL context factory. + * + * @param sslEngine The SSL engine to initialize. + */ + protected void initEngine(SSLEngine sslEngine) { + if (getContextFactory().isNeedClientAuthentication()) { + sslEngine.setNeedClientAuth(true); + } else if (getContextFactory().isWantClientAuthentication()) { + sslEngine.setWantClientAuth(true); + } + + if ((getContextFactory().getEnabledCipherSuites() != null) + || (getContextFactory().getDisabledCipherSuites() != null)) { + sslEngine.setEnabledCipherSuites( + getContextFactory() + .getSelectedCipherSuites(sslEngine.getSupportedCipherSuites())); + } + + if ((getContextFactory().getEnabledProtocols() != null) + || (getContextFactory().getDisabledProtocols() != null)) { + sslEngine.setEnabledProtocols( + getContextFactory().getSelectedSslProtocols(sslEngine.getSupportedProtocols())); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java index 10846de58b..8fd97f163b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java @@ -1,126 +1,130 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.ssl; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; /** - * SSL server socket factory that wraps the default one to do extra - * initialization. Configures the cipher suites and the SSL certificate request. - * + * SSL server socket factory that wraps the default one to do extra initialization. Configures the + * cipher suites and the SSL certificate request. + * * @author Jerome Louvel */ public class WrapperSslServerSocketFactory extends SSLServerSocketFactory { - /** The parent SSL context factory. */ - private final DefaultSslContextFactory contextFactory; - - /** The wrapped SSL server socket factory. */ - private final SSLServerSocketFactory wrappedSocketFactory; - - /** - * Constructor. - * - * @param contextFactory The parent SSL context factory. - * @param wrappedSocketFactory The wrapped SSL server socket factory. - */ - public WrapperSslServerSocketFactory(DefaultSslContextFactory contextFactory, - SSLServerSocketFactory wrappedSocketFactory) { - this.wrappedSocketFactory = wrappedSocketFactory; - this.contextFactory = contextFactory; - } - - @Override - public ServerSocket createServerSocket() throws IOException { - SSLServerSocket result = (SSLServerSocket) getWrappedSocketFactory().createServerSocket(); - return initSslServerSocket(result); - } - - @Override - public ServerSocket createServerSocket(int port) throws IOException { - SSLServerSocket result = (SSLServerSocket) getWrappedSocketFactory().createServerSocket(port); - return initSslServerSocket(result); - } - - @Override - public ServerSocket createServerSocket(int port, int backLog) throws IOException { - SSLServerSocket result = (SSLServerSocket) getWrappedSocketFactory().createServerSocket(port, backLog); - return initSslServerSocket(result); - } - - @Override - public ServerSocket createServerSocket(int port, int backLog, InetAddress ifAddress) throws IOException { - SSLServerSocket result = (SSLServerSocket) getWrappedSocketFactory().createServerSocket(port, backLog, - ifAddress); - return initSslServerSocket(result); - } - - /** - * Returns the parent SSL context factory. - * - * @return The parent SSL context factory. - */ - public DefaultSslContextFactory getContextFactory() { - return contextFactory; - } - - @Override - public String[] getDefaultCipherSuites() { - return getWrappedSocketFactory().getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return getWrappedSocketFactory().getSupportedCipherSuites(); - } - - /** - * Returns the wrapped SSL server socket factory. - * - * @return The wrapped SSL server socket factory. - */ - public SSLServerSocketFactory getWrappedSocketFactory() { - return wrappedSocketFactory; - } - - /** - * Initializes the SSL server socket. Configures the certificate request (need - * or want) and the enabled cipher suites. - * - * @param sslServerSocket The server socket to initialize. - * @return The initialized server socket. - */ - protected SSLServerSocket initSslServerSocket(SSLServerSocket sslServerSocket) { - if (getContextFactory().isNeedClientAuthentication()) { - sslServerSocket.setNeedClientAuth(true); - } else if (getContextFactory().isWantClientAuthentication()) { - sslServerSocket.setWantClientAuth(true); - } - - if ((getContextFactory().getEnabledCipherSuites() != null) - || (getContextFactory().getDisabledCipherSuites() != null)) { - sslServerSocket.setEnabledCipherSuites( - getContextFactory().getSelectedCipherSuites(sslServerSocket.getSupportedCipherSuites())); - } - - if ((getContextFactory().getEnabledProtocols() != null) - || (getContextFactory().getDisabledProtocols() != null)) { - sslServerSocket.setEnabledProtocols( - getContextFactory().getSelectedSslProtocols(sslServerSocket.getSupportedProtocols())); - } - - return sslServerSocket; - } - + /** The parent SSL context factory. */ + private final DefaultSslContextFactory contextFactory; + + /** The wrapped SSL server socket factory. */ + private final SSLServerSocketFactory wrappedSocketFactory; + + /** + * Constructor. + * + * @param contextFactory The parent SSL context factory. + * @param wrappedSocketFactory The wrapped SSL server socket factory. + */ + public WrapperSslServerSocketFactory( + DefaultSslContextFactory contextFactory, SSLServerSocketFactory wrappedSocketFactory) { + this.wrappedSocketFactory = wrappedSocketFactory; + this.contextFactory = contextFactory; + } + + @Override + public ServerSocket createServerSocket() throws IOException { + SSLServerSocket result = (SSLServerSocket) getWrappedSocketFactory().createServerSocket(); + return initSslServerSocket(result); + } + + @Override + public ServerSocket createServerSocket(int port) throws IOException { + SSLServerSocket result = + (SSLServerSocket) getWrappedSocketFactory().createServerSocket(port); + return initSslServerSocket(result); + } + + @Override + public ServerSocket createServerSocket(int port, int backLog) throws IOException { + SSLServerSocket result = + (SSLServerSocket) getWrappedSocketFactory().createServerSocket(port, backLog); + return initSslServerSocket(result); + } + + @Override + public ServerSocket createServerSocket(int port, int backLog, InetAddress ifAddress) + throws IOException { + SSLServerSocket result = + (SSLServerSocket) + getWrappedSocketFactory().createServerSocket(port, backLog, ifAddress); + return initSslServerSocket(result); + } + + /** + * Returns the parent SSL context factory. + * + * @return The parent SSL context factory. + */ + public DefaultSslContextFactory getContextFactory() { + return contextFactory; + } + + @Override + public String[] getDefaultCipherSuites() { + return getWrappedSocketFactory().getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return getWrappedSocketFactory().getSupportedCipherSuites(); + } + + /** + * Returns the wrapped SSL server socket factory. + * + * @return The wrapped SSL server socket factory. + */ + public SSLServerSocketFactory getWrappedSocketFactory() { + return wrappedSocketFactory; + } + + /** + * Initializes the SSL server socket. Configures the certificate request (need or want) and the + * enabled cipher suites. + * + * @param sslServerSocket The server socket to initialize. + * @return The initialized server socket. + */ + protected SSLServerSocket initSslServerSocket(SSLServerSocket sslServerSocket) { + if (getContextFactory().isNeedClientAuthentication()) { + sslServerSocket.setNeedClientAuth(true); + } else if (getContextFactory().isWantClientAuthentication()) { + sslServerSocket.setWantClientAuth(true); + } + + if ((getContextFactory().getEnabledCipherSuites() != null) + || (getContextFactory().getDisabledCipherSuites() != null)) { + sslServerSocket.setEnabledCipherSuites( + getContextFactory() + .getSelectedCipherSuites(sslServerSocket.getSupportedCipherSuites())); + } + + if ((getContextFactory().getEnabledProtocols() != null) + || (getContextFactory().getDisabledProtocols() != null)) { + sslServerSocket.setEnabledProtocols( + getContextFactory() + .getSelectedSslProtocols(sslServerSocket.getSupportedProtocols())); + } + + return sslServerSocket; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java index 8e457268d2..0ecaa0d00b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java @@ -1,138 +1,145 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.ssl; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; /** - * SSL socket factory that wraps the default one to do extra initialization. - * Configures the cipher suites and the SSL certificate request. - * + * SSL socket factory that wraps the default one to do extra initialization. Configures the cipher + * suites and the SSL certificate request. + * * @author Jerome Louvel */ public class WrapperSslSocketFactory extends SSLSocketFactory { - /** The parent SSL context factory. */ - private final DefaultSslContextFactory contextFactory; - - /** The wrapped SSL server socket factory. */ - private final SSLSocketFactory wrappedSocketFactory; - - /** - * Constructor. - * - * @param contextFactory The parent SSL context factory. - * @param wrappedSocketFactory The wrapped SSL server socket factory. - */ - public WrapperSslSocketFactory(DefaultSslContextFactory contextFactory, SSLSocketFactory wrappedSocketFactory) { - this.wrappedSocketFactory = wrappedSocketFactory; - this.contextFactory = contextFactory; - } - - @Override - public Socket createSocket() throws IOException { - SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(); - return initSslSocket(result); - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port); - return initSslSocket(result); - } - - @Override - public Socket createSocket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException { - SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port, localAddress, localPort); - return initSslSocket(result); - } - - @Override - public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(s, host, port, autoClose); - return initSslSocket(result); - } - - @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port); - return initSslSocket(result); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) - throws IOException, UnknownHostException { - SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port, localAddress, localPort); - return initSslSocket(result); - } - - /** - * Returns the parent SSL context factory. - * - * @return The parent SSL context factory. - */ - public DefaultSslContextFactory getContextFactory() { - return contextFactory; - } - - @Override - public String[] getDefaultCipherSuites() { - return getWrappedSocketFactory().getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return getWrappedSocketFactory().getSupportedCipherSuites(); - } - - /** - * Returns the wrapped SSL socket factory. - * - * @return The wrapped SSL socket factory. - */ - public SSLSocketFactory getWrappedSocketFactory() { - return wrappedSocketFactory; - } - - /** - * Initializes the SSL socket. Configures the certificate request (need or want) - * and the enabled cipher suites. - * - * @param sslSocket The socket to initialize. - * @return The initialized socket. - */ - protected SSLSocket initSslSocket(SSLSocket sslSocket) { - if (getContextFactory().isNeedClientAuthentication()) { - sslSocket.setNeedClientAuth(true); - } else if (getContextFactory().isWantClientAuthentication()) { - sslSocket.setWantClientAuth(true); - } - - if ((getContextFactory().getEnabledCipherSuites() != null) - || (getContextFactory().getDisabledCipherSuites() != null)) { - sslSocket.setEnabledCipherSuites( - getContextFactory().getSelectedCipherSuites(sslSocket.getSupportedCipherSuites())); - } - - if ((getContextFactory().getEnabledProtocols() != null) - || (getContextFactory().getDisabledProtocols() != null)) { - sslSocket.setEnabledProtocols( - getContextFactory().getSelectedSslProtocols(sslSocket.getSupportedProtocols())); - } - - return sslSocket; - } - + /** The parent SSL context factory. */ + private final DefaultSslContextFactory contextFactory; + + /** The wrapped SSL server socket factory. */ + private final SSLSocketFactory wrappedSocketFactory; + + /** + * Constructor. + * + * @param contextFactory The parent SSL context factory. + * @param wrappedSocketFactory The wrapped SSL server socket factory. + */ + public WrapperSslSocketFactory( + DefaultSslContextFactory contextFactory, SSLSocketFactory wrappedSocketFactory) { + this.wrappedSocketFactory = wrappedSocketFactory; + this.contextFactory = contextFactory; + } + + @Override + public Socket createSocket() throws IOException { + SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(); + return initSslSocket(result); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port); + return initSslSocket(result); + } + + @Override + public Socket createSocket(InetAddress host, int port, InetAddress localAddress, int localPort) + throws IOException { + SSLSocket result = + (SSLSocket) + getWrappedSocketFactory().createSocket(host, port, localAddress, localPort); + return initSslSocket(result); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + SSLSocket result = + (SSLSocket) getWrappedSocketFactory().createSocket(s, host, port, autoClose); + return initSslSocket(result); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port); + return initSslSocket(result); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) + throws IOException, UnknownHostException { + SSLSocket result = + (SSLSocket) + getWrappedSocketFactory().createSocket(host, port, localAddress, localPort); + return initSslSocket(result); + } + + /** + * Returns the parent SSL context factory. + * + * @return The parent SSL context factory. + */ + public DefaultSslContextFactory getContextFactory() { + return contextFactory; + } + + @Override + public String[] getDefaultCipherSuites() { + return getWrappedSocketFactory().getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return getWrappedSocketFactory().getSupportedCipherSuites(); + } + + /** + * Returns the wrapped SSL socket factory. + * + * @return The wrapped SSL socket factory. + */ + public SSLSocketFactory getWrappedSocketFactory() { + return wrappedSocketFactory; + } + + /** + * Initializes the SSL socket. Configures the certificate request (need or want) and the enabled + * cipher suites. + * + * @param sslSocket The socket to initialize. + * @return The initialized socket. + */ + protected SSLSocket initSslSocket(SSLSocket sslSocket) { + if (getContextFactory().isNeedClientAuthentication()) { + sslSocket.setNeedClientAuth(true); + } else if (getContextFactory().isWantClientAuthentication()) { + sslSocket.setWantClientAuth(true); + } + + if ((getContextFactory().getEnabledCipherSuites() != null) + || (getContextFactory().getDisabledCipherSuites() != null)) { + sslSocket.setEnabledCipherSuites( + getContextFactory() + .getSelectedCipherSuites(sslSocket.getSupportedCipherSuites())); + } + + if ((getContextFactory().getEnabledProtocols() != null) + || (getContextFactory().getDisabledProtocols() != null)) { + sslSocket.setEnabledProtocols( + getContextFactory().getSelectedSslProtocols(sslSocket.getSupportedProtocols())); + } + + return sslSocket; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java b/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java index fa12353c24..bd0fb6a39a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java @@ -1,123 +1,111 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; /** * Optimized public-domain implementation of a Java alphanumeric sort. - *

* - * This implementation uses a single comparison pass over the characters in a - * CharSequence and returns as soon as a differing character is found, unless - * the difference occurs in a series of numeric characters, in which case that - * series is followed to its end. Numeric series of equal length are compared - * numerically, that is, according to the most significant (leftmost) differing + *

This implementation uses a single comparison pass over the characters in a CharSequence and + * returns as soon as a differing character is found, unless the difference occurs in a series of + * numeric characters, in which case that series is followed to its end. Numeric series of equal + * length are compared numerically, that is, according to the most significant (leftmost) differing * digit. Series of unequal length are compared by their length. - *

* - * This implementation appears to be 2-5 times faster than alphanumeric - * comparators based on substring analysis, with a lighter memory - * footprint. - *

+ *

This implementation appears to be 2-5 times faster than alphanumeric comparators based on + * substring analysis, with a lighter memory footprint. * - * This alphanumeric comparator has approximately 20%-50% the performance of the - * lexical String.compareTo() operation. Character sequences without numeric - * data are compared more quickly. - *

+ *

This alphanumeric comparator has approximately 20%-50% the performance of the lexical + * String.compareTo() operation. Character sequences without numeric data are compared more quickly. * - * Dedicated to the public domain by the original author: - * Public Domain List + *

Dedicated to the public domain by the original author: Public Domain List * - * @author Rob Heittman, Solertium - * Corporation + * @author Rob Heittman, Solertium Corporation */ public class AlphaNumericComparator extends AlphabeticalComparator { - private static final long serialVersionUID = 1L; - - @Override - public int compare(final String uri0, final String uri1) { - int ptr = 0; - int msd = 0; - int diff = 0; - char a, b; - - final int llength = uri0.length(); - final int rlength = uri1.length(); - final int min = Math.min(rlength, llength); - - boolean rAtEnd, rHasNoMoreDigits; - - while (ptr < min) { - a = uri0.charAt(ptr); - b = uri1.charAt(ptr); - diff = a - b; - - if ((a >= '9') || (b >= '9') || (a <= '0') || (b <= '0')) { - if (diff != 0) { - return diff; - } - - msd = 0; - } else { - if (msd == 0) { - msd = diff; - } - - rAtEnd = rlength - ptr < 2; - - if (llength - ptr < 2) { - if (rAtEnd) { - return msd; - } - - if (!isNotDigit(a) && !isNotDigit(b)) - return diff; - - return -1; - } - - if (rAtEnd) { - if (!isNotDigit(a) && !isNotDigit(b)) - return diff; - - return -1; - } - - rHasNoMoreDigits = isNotDigit(uri1.charAt(ptr + 1)); - - if (isNotDigit(uri0.charAt(ptr + 1))) { - if (rHasNoMoreDigits && (msd != 0)) { - return msd; - } - - if (!rHasNoMoreDigits) { - return -1; - } - } else { - if (rHasNoMoreDigits) { - return 1; - } - } - } - ptr++; - } - return llength - rlength; - } - - /** - * Indicates if the character is a digit. - * - * @param x The character to test. - * @return True if the character is a digit. - */ - protected boolean isNotDigit(final char x) { - return (x > '9') || (x < '0'); - } - + private static final long serialVersionUID = 1L; + + @Override + public int compare(final String uri0, final String uri1) { + int ptr = 0; + int msd = 0; + int diff = 0; + char a, b; + + final int llength = uri0.length(); + final int rlength = uri1.length(); + final int min = Math.min(rlength, llength); + + boolean rAtEnd, rHasNoMoreDigits; + + while (ptr < min) { + a = uri0.charAt(ptr); + b = uri1.charAt(ptr); + diff = a - b; + + if ((a >= '9') || (b >= '9') || (a <= '0') || (b <= '0')) { + if (diff != 0) { + return diff; + } + + msd = 0; + } else { + if (msd == 0) { + msd = diff; + } + + rAtEnd = rlength - ptr < 2; + + if (llength - ptr < 2) { + if (rAtEnd) { + return msd; + } + + if (!isNotDigit(a) && !isNotDigit(b)) return diff; + + return -1; + } + + if (rAtEnd) { + if (!isNotDigit(a) && !isNotDigit(b)) return diff; + + return -1; + } + + rHasNoMoreDigits = isNotDigit(uri1.charAt(ptr + 1)); + + if (isNotDigit(uri0.charAt(ptr + 1))) { + if (rHasNoMoreDigits && (msd != 0)) { + return msd; + } + + if (!rHasNoMoreDigits) { + return -1; + } + } else { + if (rHasNoMoreDigits) { + return 1; + } + } + } + ptr++; + } + return llength - rlength; + } + + /** + * Indicates if the character is a digit. + * + * @param x The character to test. + * @return True if the character is a digit. + */ + protected boolean isNotDigit(final char x) { + return (x > '9') || (x < '0'); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java b/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java index 760e574637..53b956017a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java @@ -1,60 +1,58 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; -import org.restlet.data.Reference; - import java.io.Serializable; import java.util.Comparator; +import org.restlet.data.Reference; /** * Allows sorting the list of references set by the resource. - * + * * @author Jerome Louvel */ public class AlphabeticalComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - /** - * Compares two references. - * - * @param ref0 The first reference. - * @param ref1 The second reference. - * @return The comparison result. - * @see Comparator - */ - public int compare(Reference ref0, Reference ref1) { - final boolean bRep0Null = (ref0.getIdentifier() == null); - final boolean bRep1Null = (ref1.getIdentifier() == null); + /** + * Compares two references. + * + * @param ref0 The first reference. + * @param ref1 The second reference. + * @return The comparison result. + * @see Comparator + */ + public int compare(Reference ref0, Reference ref1) { + final boolean bRep0Null = (ref0.getIdentifier() == null); + final boolean bRep1Null = (ref1.getIdentifier() == null); - if (bRep0Null && bRep1Null) { - return 0; - } - if (bRep0Null) { - return -1; - } - if (bRep1Null) { - return 1; - } - return compare(ref0.toString(false, false), ref1.toString(false, false)); - } + if (bRep0Null && bRep1Null) { + return 0; + } + if (bRep0Null) { + return -1; + } + if (bRep1Null) { + return 1; + } + return compare(ref0.toString(false, false), ref1.toString(false, false)); + } - /** - * Compares two strings. - * - * @param str0 The first string. - * @param str1 The second string. - * @return The comparison result. - * @see Comparator - */ - public int compare(final String str0, final String str1) { - return str0.compareTo(str1); - } + /** + * Compares two strings. + * + * @param str0 The first string. + * @param str1 The second string. + * @return The comparison result. + * @see Comparator + */ + public int compare(final String str0, final String str1) { + return str0.compareTo(str1); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java index 5906558b73..ffb92a6787 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.beans.BeanInfo; @@ -17,43 +16,47 @@ /** * Utilities to get the {@link BeanInfo} of a class. - * + * * @author Manuel Boillod */ public class BeanInfoUtils { - /** BeanInfo cache. */ - private static final ConcurrentMap, BeanInfo> cache = new ConcurrentHashMap, BeanInfo>(); - - /** - * Get a BeanInfo from cache or create it. Stop introspection to {@link Object} - * or {@link Throwable} if the class is a subtype of {@link Throwable} - * - * @param clazz The class - * @return BeanInfo of the class - */ - public static BeanInfo getBeanInfo(Class clazz) { - BeanInfo result = cache.get(clazz); - - if (result == null) { - // Inspect the class itself for annotations - - Class stopClass = Throwable.class.isAssignableFrom(clazz) ? Throwable.class : Object.class; - try { - result = Introspector.getBeanInfo(clazz, stopClass, Introspector.IGNORE_ALL_BEANINFO); - } catch (IntrospectionException e) { - throw new RuntimeException("Could not get BeanInfo of class " + clazz.getName(), e); - } - - // Put the list in the cache if no one was previously present - BeanInfo prev = cache.putIfAbsent(clazz, result); - - if (prev != null) { - // Reuse the previous entry - result = prev; - } - } - - return result; - } + /** BeanInfo cache. */ + private static final ConcurrentMap, BeanInfo> cache = + new ConcurrentHashMap, BeanInfo>(); + + /** + * Get a BeanInfo from the cache or create it. Stop introspection to {@link Object} or {@link + * Throwable} if the class is a subtype of {@link Throwable} + * + * @param clazz The class + * @return BeanInfo of the class + */ + public static BeanInfo getBeanInfo(Class clazz) { + BeanInfo result = cache.get(clazz); + + if (result == null) { + // Inspect the class itself for annotations + + Class stopClass = + Throwable.class.isAssignableFrom(clazz) ? Throwable.class : Object.class; + try { + result = + Introspector.getBeanInfo( + clazz, stopClass, Introspector.IGNORE_ALL_BEANINFO); + } catch (IntrospectionException e) { + throw new RuntimeException("Could not get BeanInfo of class " + clazz.getName(), e); + } + + // Put the list in the cache if no one was previously present + BeanInfo prev = cache.putIfAbsent(clazz, result); + + if (prev != null) { + // Reuse the previous entry + result = prev; + } + } + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java b/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java index 090288c982..57802d34b8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; +import static org.restlet.engine.util.DateUtils.FORMAT_RFC_1123; + +import java.util.Date; import org.restlet.Request; import org.restlet.Response; import org.restlet.data.ChallengeResponse; @@ -18,226 +20,252 @@ import org.restlet.representation.Representation; import org.restlet.util.Resolver; -import java.util.Date; - -import static org.restlet.engine.util.DateUtils.FORMAT_RFC_1123; - /** * Resolves variable values based on a request and a response. - * + * * @author Jerome Louvel */ public class CallResolver extends Resolver { - /** The request to use as a model. */ - private final Request request; - - /** The response to use as a model. */ - private final Response response; - - /** - * Constructor. - * - * @param request The request to use as a model. - * @param response The response to use as a model. - */ - public CallResolver(Request request, Response response) { - this.request = request; - this.response = response; - } - - @Override - public Object resolve(String variableName) { - // Check for a matching response attribute - Object result = (this.response != null) ? this.response.getAttributes().get(variableName) : null; - if (result != null) { - return result; - } - - // Check for a matching request attribute - result = (this.request != null) ? this.request.getAttributes().get(variableName) : null; - if (result != null) { - return result; - } - - if (variableName == null) { - return null; - } - - // Check for a matching request or response property - if (this.request != null) { - ChallengeResponse cr = this.request.getChallengeResponse(); - Representation entity = this.request.getEntity(); - - switch (variableName) { - case "c": - return Boolean.toString(this.request.isConfidential()); - case "cia": - return this.request.getClientInfo().getAddress(); - case "ciua": - return this.request.getClientInfo().getUpstreamAddress(); - case "cig": - return this.request.getClientInfo().getAgent(); - case "cri": - return (cr != null) ? cr.getIdentifier() : null; - case "crs": - return (cr != null && cr.getScheme() != null) ? cr.getScheme().getTechnicalName() : null; - case "d": - return DateUtils.format(new Date(), FORMAT_RFC_1123.get(0)); - case "ecs": - return (entity != null && entity.getCharacterSet() != null) ? entity.getCharacterSet().getName() : null; - case "ee": - return getEncodingsAsString(entity); - case "eed": - return getExpirationDateAsString(entity); - case "el": - return getLanguagesAsString(entity); - case "emd": - return getModificationDateAsString(entity); - case "emt": - return (entity != null && entity.getMediaType() != null) ? entity.getMediaType().getName() : null; - case "es": - return (entity != null && entity.getSize() != -1) ? Long.toString(entity.getSize()) : null; - case "et": - return (entity != null && entity.getTag() != null) ? entity.getTag().getName() : null; - case "m": - return (this.request.getMethod() != null) ? this.request.getMethod().getName() : null; - case "p": - return (this.request.getProtocol() != null) ? this.request.getProtocol().getName() : null; - default: - if (variableName.startsWith("o")) { - return getReferenceContent(variableName.substring(1), this.request.getRootRef()); - } else if (variableName.startsWith("f")) { - return getReferenceContent(variableName.substring(1), this.request.getReferrerRef()); - } else if (variableName.startsWith("h")) { - return getReferenceContent(variableName.substring(1), this.request.getHostRef()); - } else if (variableName.startsWith("r")) { - return getReferenceContent(variableName.substring(1), this.request.getResourceRef()); - } - break; - } - } - - if (this.response != null) { - Representation entity = this.response.getEntity(); - Status status = this.response.getStatus(); - ServerInfo serverInfo = this.response.getServerInfo(); - - switch (variableName) { - case "ECS": - return (entity != null && entity.getCharacterSet() != null) ? entity.getCharacterSet().getName() : null; - case "EE": - return getEncodingsAsString(entity); - case "EED": - return getExpirationDateAsString(entity); - case "EL": - return getLanguagesAsString(entity); - case "EMD": - return getModificationDateAsString(entity); - case "EMT": - return (entity != null && entity.getMediaType() != null) ? entity.getMediaType().getName() : null; - case "ES": - return (entity != null && entity.getSize() != -1) ? Long.toString(entity.getSize()) : null; - case "ET": - return (entity != null && entity.getTag() != null) ? entity.getTag().getName() : null; - case "S": - return (status != null) ? Integer.toString(status.getCode()) : null; - case "SIA": - return serverInfo.getAddress(); - case "SIG": - return serverInfo.getAgent(); - case "SIP": - return (serverInfo.getPort() != -1) ? Integer.toString(serverInfo.getPort()) : null; - default: - if (variableName.startsWith("R")) { - return getReferenceContent(variableName.substring(1), this.response.getLocationRef()); - } - } - } - - return null; - } - - /** - * Returns the content corresponding to a reference property. - * - * @param partName The variable sub-part name. - * @param reference The reference to use as a model. - * @return The content corresponding to a reference property. - */ - private String getReferenceContent(String partName, Reference reference) { - if (reference == null || partName == null) { - return null; - } - - switch (partName) { - case "a": - return reference.getAuthority(); - case "e": - return reference.getRelativePart(); - case "f": - return reference.getFragment(); - case "h": - return reference.getHostIdentifier(); - case "i": - return reference.getIdentifier(); - case "p": - return reference.getPath(); - case "q": - return reference.getQuery(); - case "r": - return reference.getRemainingPart(); - default: - if (partName.startsWith("b")) { - return getReferenceContent(partName.substring(1), reference.getBaseRef()); - } else if (partName.startsWith("t")) { - return getReferenceContent(partName.substring(1), reference.getTargetRef()); - } else if (partName.isEmpty()) { - return reference.toString(false, false); - } - break; - } - - return null; - } - - private Object getModificationDateAsString(Representation entity) { - return (entity != null && (entity.getModificationDate() != null)) - ? DateUtils.format(entity.getModificationDate(), FORMAT_RFC_1123.get(0)) - : null; - } - - private Object getExpirationDateAsString(Representation entity) { - return (entity != null && entity.getExpirationDate() != null) - ? DateUtils.format(entity.getExpirationDate(), FORMAT_RFC_1123.get(0)) - : null; - } - - private Object getLanguagesAsString(Representation entity) { - if (entity != null && !entity.getLanguages().isEmpty()) { - final StringBuilder value = new StringBuilder(); - for (int i = 0; i < entity.getLanguages().size(); i++) { - if (i > 0) { - value.append(", "); - } - value.append(entity.getLanguages().get(i).getName()); - } - return value.toString(); - } - return null; - } - - private Object getEncodingsAsString(Representation entity) { - if (entity != null && !entity.getEncodings().isEmpty()) { - final StringBuilder value = new StringBuilder(); - for (int i = 0; i < entity.getEncodings().size(); i++) { - if (i > 0) { - value.append(", "); - } - value.append(entity.getEncodings().get(i).getName()); - } - return value.toString(); - } - return null; - } + /** The request to use as a model. */ + private final Request request; + + /** The response to use as a model. */ + private final Response response; + + /** + * Constructor. + * + * @param request The request to use as a model. + * @param response The response to use as a model. + */ + public CallResolver(Request request, Response response) { + this.request = request; + this.response = response; + } + + @Override + public Object resolve(String variableName) { + // Check for a matching response attribute + Object result = + (this.response != null) ? this.response.getAttributes().get(variableName) : null; + if (result != null) { + return result; + } + + // Check for a matching request attribute + result = (this.request != null) ? this.request.getAttributes().get(variableName) : null; + if (result != null) { + return result; + } + + if (variableName == null) { + return null; + } + + // Check for a matching request or response property + if (this.request != null) { + ChallengeResponse cr = this.request.getChallengeResponse(); + Representation entity = this.request.getEntity(); + + switch (variableName) { + case "c": + return Boolean.toString(this.request.isConfidential()); + case "cia": + return this.request.getClientInfo().getAddress(); + case "ciua": + return this.request.getClientInfo().getUpstreamAddress(); + case "cig": + return this.request.getClientInfo().getAgent(); + case "cri": + return (cr != null) ? cr.getIdentifier() : null; + case "crs": + return (cr != null && cr.getScheme() != null) + ? cr.getScheme().getTechnicalName() + : null; + case "d": + return DateUtils.format(new Date(), FORMAT_RFC_1123.getFirst()); + case "ecs": + return (entity != null && entity.getCharacterSet() != null) + ? entity.getCharacterSet().getName() + : null; + case "ee": + return getEncodingsAsString(entity); + case "eed": + return getExpirationDateAsString(entity); + case "el": + return getLanguagesAsString(entity); + case "emd": + return getModificationDateAsString(entity); + case "emt": + return (entity != null && entity.getMediaType() != null) + ? entity.getMediaType().getName() + : null; + case "es": + return (entity != null && entity.getSize() != -1) + ? Long.toString(entity.getSize()) + : null; + case "et": + return (entity != null && entity.getTag() != null) + ? entity.getTag().getName() + : null; + case "m": + return (this.request.getMethod() != null) + ? this.request.getMethod().getName() + : null; + case "p": + return (this.request.getProtocol() != null) + ? this.request.getProtocol().getName() + : null; + default: + if (variableName.startsWith("o")) { + return getReferenceContent( + variableName.substring(1), this.request.getRootRef()); + } else if (variableName.startsWith("f")) { + return getReferenceContent( + variableName.substring(1), this.request.getReferrerRef()); + } else if (variableName.startsWith("h")) { + return getReferenceContent( + variableName.substring(1), this.request.getHostRef()); + } else if (variableName.startsWith("r")) { + return getReferenceContent( + variableName.substring(1), this.request.getResourceRef()); + } + break; + } + } + + if (this.response != null) { + Representation entity = this.response.getEntity(); + Status status = this.response.getStatus(); + ServerInfo serverInfo = this.response.getServerInfo(); + + switch (variableName) { + case "ECS": + return (entity != null && entity.getCharacterSet() != null) + ? entity.getCharacterSet().getName() + : null; + case "EE": + return getEncodingsAsString(entity); + case "EED": + return getExpirationDateAsString(entity); + case "EL": + return getLanguagesAsString(entity); + case "EMD": + return getModificationDateAsString(entity); + case "EMT": + return (entity != null && entity.getMediaType() != null) + ? entity.getMediaType().getName() + : null; + case "ES": + return (entity != null && entity.getSize() != -1) + ? Long.toString(entity.getSize()) + : null; + case "ET": + return (entity != null && entity.getTag() != null) + ? entity.getTag().getName() + : null; + case "S": + return (status != null) ? Integer.toString(status.getCode()) : null; + case "SIA": + return serverInfo.getAddress(); + case "SIG": + return serverInfo.getAgent(); + case "SIP": + return (serverInfo.getPort() != -1) + ? Integer.toString(serverInfo.getPort()) + : null; + default: + if (variableName.startsWith("R")) { + return getReferenceContent( + variableName.substring(1), this.response.getLocationRef()); + } + } + } + + return null; + } + + /** + * Returns the content corresponding to a reference property. + * + * @param partName The variable subpart name. + * @param reference The reference to use as a model. + * @return The content corresponding to a reference property. + */ + private String getReferenceContent(String partName, Reference reference) { + if (reference == null || partName == null) { + return null; + } + + switch (partName) { + case "a": + return reference.getAuthority(); + case "e": + return reference.getRelativePart(); + case "f": + return reference.getFragment(); + case "h": + return reference.getHostIdentifier(); + case "i": + return reference.getIdentifier(); + case "p": + return reference.getPath(); + case "q": + return reference.getQuery(); + case "r": + return reference.getRemainingPart(); + default: + if (partName.startsWith("b")) { + return getReferenceContent(partName.substring(1), reference.getBaseRef()); + } else if (partName.startsWith("t")) { + return getReferenceContent(partName.substring(1), reference.getTargetRef()); + } else if (partName.isEmpty()) { + return reference.toString(false, false); + } + break; + } + + return null; + } + + private Object getModificationDateAsString(Representation entity) { + return (entity != null && (entity.getModificationDate() != null)) + ? DateUtils.format(entity.getModificationDate(), FORMAT_RFC_1123.getFirst()) + : null; + } + + private Object getExpirationDateAsString(Representation entity) { + return (entity != null && entity.getExpirationDate() != null) + ? DateUtils.format(entity.getExpirationDate(), FORMAT_RFC_1123.getFirst()) + : null; + } + + private Object getLanguagesAsString(Representation entity) { + if (entity != null && !entity.getLanguages().isEmpty()) { + final StringBuilder value = new StringBuilder(); + for (int i = 0; i < entity.getLanguages().size(); i++) { + if (i > 0) { + value.append(", "); + } + value.append(entity.getLanguages().get(i).getName()); + } + return value.toString(); + } + return null; + } + + private Object getEncodingsAsString(Representation entity) { + if (entity != null && !entity.getEncodings().isEmpty()) { + final StringBuilder value = new StringBuilder(); + for (int i = 0; i < entity.getEncodings().size(); i++) { + if (i > 0) { + value.append(", "); + } + value.append(entity.getEncodings().get(i).getName()); + } + return value.toString(); + } + return null; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java b/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java index 5ea5508747..443588bdb2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java @@ -1,47 +1,42 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.util.Collection; import java.util.HashSet; -/** - * Set implementation that is case insensitive. - */ +/** Implementation of the Set Interface that is case-insensitive. */ public class CaseInsensitiveHashSet extends HashSet { - private static final long serialVersionUID = 1L; - - /** - * Constructor initializing the set with the given collection. - * - * @param source The source collection to use for initialization. - */ - public CaseInsensitiveHashSet(Collection source) { - super(source); - } - - @Override - public boolean add(String element) { - return super.add(element.toLowerCase()); - } - - /** - * Verify containment by ignoring case. - */ - public boolean contains(String element) { - return super.contains(element.toLowerCase()); - } - - @Override - public boolean contains(Object o) { - return contains(o.toString()); - } + private static final long serialVersionUID = 1L; + + /** + * Constructor initializing the set with the given collection. + * + * @param source The source collection to use for initialization. + */ + public CaseInsensitiveHashSet(Collection source) { + super(source); + } + + @Override + public boolean add(String element) { + return super.add(element.toLowerCase()); + } + + /** Verify containment by ignoring the case. */ + public boolean contains(String element) { + return super.contains(element.toLowerCase()); + } + + @Override + public boolean contains(Object o) { + return contains(o.toString()); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java index c3101106c8..c414763d8b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import org.restlet.Application; @@ -17,109 +16,113 @@ /** * Client dispatcher for a component child. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state as member variables. - * + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state as member variables. + * * @author Jerome Louvel */ public class ChildClientDispatcher extends TemplateDispatcher { - /** The child context. */ - private ChildContext childContext; - - /** - * Constructor. - * - * @param childContext The child context. - */ - public ChildClientDispatcher(ChildContext childContext) { - this.childContext = childContext; - } + /** The child context. */ + private ChildContext childContext; - /** - * Transmits the call to the parent component except if the call is internal as - * denoted by the {@link Protocol#RIAP} protocol and targets this child - * application. - * - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public int doHandle(Request request, Response response) { - int result = CONTINUE; + /** + * Constructor. + * + * @param childContext The child context. + */ + public ChildClientDispatcher(ChildContext childContext) { + this.childContext = childContext; + } - Protocol protocol = request.getProtocol(); + /** + * Transmits the call to the parent component except if the call is internal as denoted by the + * {@link Protocol#RIAP} protocol and targets this child application. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public int doHandle(Request request, Response response) { + int result = CONTINUE; - if (protocol.equals(Protocol.RIAP)) { - // Let's dispatch it - LocalReference cr = new LocalReference(request.getResourceRef()); + Protocol protocol = request.getProtocol(); - if (cr.getRiapAuthorityType() == LocalReference.RIAP_APPLICATION) { - if ((getChildContext() != null) - && (getChildContext().getChild() instanceof Application application)) { - request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); - application.getInboundRoot().handle(request, response); - } - } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { - parentHandle(request, response); - } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_HOST) { - parentHandle(request, response); - } else { - getLogger().warning( - "Unknown RIAP authority. Only \"component\", \"host\" and \"application\" are supported."); - result = STOP; - } - } else { - if ((getChildContext() != null) - && (getChildContext().getChild() instanceof Application application)) { + if (protocol.equals(Protocol.RIAP)) { + // Let's dispatch it + LocalReference cr = new LocalReference(request.getResourceRef()); - if (!application.getConnectorService().getClientProtocols().contains(protocol)) { - getLogger().fine( - "The protocol used by this request is not declared in the application's connector service (" - + protocol - + "). Please update the list of client connectors used by your application and restart it."); - } - } + if (cr.getRiapAuthorityType() == LocalReference.RIAP_APPLICATION) { + if ((getChildContext() != null) + && (getChildContext().getChild() instanceof Application application)) { + request.getResourceRef() + .setBaseRef(request.getResourceRef().getHostIdentifier()); + application.getInboundRoot().handle(request, response); + } + } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { + parentHandle(request, response); + } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_HOST) { + parentHandle(request, response); + } else { + getLogger() + .warning( + "Unknown RIAP authority. Only \"component\", \"host\" and \"application\" are supported."); + result = STOP; + } + } else { + if ((getChildContext() != null) + && (getChildContext().getChild() instanceof Application application)) { - parentHandle(request, response); - } + if (!application.getConnectorService().getClientProtocols().contains(protocol)) { + getLogger() + .fine( + "The protocol used by this request is not declared in the application's connector service (" + + protocol + + "). Please update the list of client connectors used by your application and restart it."); + } + } - return result; - } + parentHandle(request, response); + } - /** - * Returns the child context. - * - * @return The child context. - */ - private ChildContext getChildContext() { - return childContext; - } + return result; + } - /** - * Asks to the parent component to handle the call. - * - * @param request The request to handle. - * @param response The response to update. - */ - private void parentHandle(Request request, Response response) { - if (getChildContext() != null) { - if (getChildContext().getParentContext() != null) { - if (getChildContext().getParentContext().getClientDispatcher() != null) { - getChildContext().getParentContext().getClientDispatcher().handle(request, response); - } else { - getLogger().warning( - "The parent context doesn't have a client dispatcher available. Unable to handle call."); - } - } else { - getLogger().warning("Your Restlet doesn't have a parent context available."); - } - } else { - getLogger().warning("Your Restlet doesn't have a context available."); - } - } + /** + * Returns the child context. + * + * @return The child context. + */ + private ChildContext getChildContext() { + return childContext; + } + /** + * Asks to the parent component to handle the call. + * + * @param request The request to handle. + * @param response The response to update. + */ + private void parentHandle(Request request, Response response) { + if (getChildContext() != null) { + if (getChildContext().getParentContext() != null) { + if (getChildContext().getParentContext().getClientDispatcher() != null) { + getChildContext() + .getParentContext() + .getClientDispatcher() + .handle(request, response); + } else { + getLogger() + .warning( + "The parent context doesn't have a client dispatcher available. Unable to handle call."); + } + } else { + getLogger().warning("Your Restlet doesn't have a parent context available."); + } + } else { + getLogger().warning("Your Restlet doesn't have a context available."); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java b/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java index c66ce1dd88..405cf04360 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import org.restlet.Context; @@ -14,60 +13,64 @@ import org.restlet.engine.log.LogUtils; /** - * Context based on a parent component's context but dedicated to a child - * Restlet, typically to an application. - * + * Context based on a parent component's context but dedicated to a child Restlet, typically to an + * application. + * * @author Jerome Louvel */ public class ChildContext extends Context { - /** The child delegate, typically an application. */ - private volatile Restlet child; - - /** The parent context. */ - private volatile Context parentContext; + /** The child delegate, typically an application. */ + private volatile Restlet child; - /** - * Constructor. - * - * @param parentContext The parent context. - */ - public ChildContext(Context parentContext) { - this.child = null; - this.parentContext = parentContext; - setClientDispatcher(new ChildClientDispatcher(this)); - setServerDispatcher((parentContext != null) ? getParentContext().getServerDispatcher() : null); - setExecutorService((parentContext != null) ? ((parentContext.getExecutorService() != null) - ? new WrapperScheduledExecutorService(parentContext.getExecutorService()) - : null) : null); - } + /** The parent context. */ + private volatile Context parentContext; - /** - * Returns the child. - * - * @return the child. - */ - public Restlet getChild() { - return this.child; - } + /** + * Constructor. + * + * @param parentContext The parent context. + */ + public ChildContext(Context parentContext) { + this.child = null; + this.parentContext = parentContext; + setClientDispatcher(new ChildClientDispatcher(this)); + setServerDispatcher( + (parentContext != null) ? getParentContext().getServerDispatcher() : null); + setExecutorService( + (parentContext != null) + ? ((parentContext.getExecutorService() != null) + ? new WrapperScheduledExecutorService( + parentContext.getExecutorService()) + : null) + : null); + } - /** - * Returns the parent context. - * - * @return The parent context. - */ - protected Context getParentContext() { - return this.parentContext; - } + /** + * Returns the child. + * + * @return the child. + */ + public Restlet getChild() { + return this.child; + } - /** - * Sets the child. - * - * @param child The child. - */ - public void setChild(Restlet child) { - this.child = child; - setLogger(LogUtils.getLoggerName(this.parentContext.getLogger().getName(), child)); - } + /** + * Returns the parent context. + * + * @return The parent context. + */ + protected Context getParentContext() { + return this.parentContext; + } + /** + * Sets the child. + * + * @param child The child. + */ + public void setChild(Restlet child) { + this.child = child; + setLogger(LogUtils.getLoggerName(this.parentContext.getLogger().getName(), child)); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java b/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java index 9a8aa29442..84d3aff446 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java @@ -1,47 +1,42 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; /** - * {@link Runnable} That allows to define the context class loader. - * + * {@link Runnable} That allows defining the context class loader. + * * @author Jerome Louvel - * */ public abstract class ContextualRunnable implements Runnable { - /** The contextual class loader used at run time. */ - private ClassLoader contextClassLoader; - - /** - * Constructor. - */ - public ContextualRunnable() { - this.contextClassLoader = Thread.currentThread().getContextClassLoader(); - } + /** The contextual class loader used at run time. */ + private ClassLoader contextClassLoader; - /** - * Returns the runnable's context class loader. - * - * @return The runnable's context class loader. - */ - public ClassLoader getContextClassLoader() { - return contextClassLoader; - } + /** Constructor. */ + public ContextualRunnable() { + this.contextClassLoader = Thread.currentThread().getContextClassLoader(); + } - /** - * Sets the runnable's context class loader. - * - * @param contextClassLoader The runnable's context class loader. - */ - public void setContextClassLoader(ClassLoader contextClassLoader) { - this.contextClassLoader = contextClassLoader; - } + /** + * Returns the runnable's context class loader. + * + * @return The runnable's context class loader. + */ + public ClassLoader getContextClassLoader() { + return contextClassLoader; + } + /** + * Sets the runnable's context class loader. + * + * @param contextClassLoader The runnable's context class loader. + */ + public void setContextClassLoader(ClassLoader contextClassLoader) { + this.contextClassLoader = contextClassLoader; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java index df8cfba2e4..baab40cb01 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.util.Arrays; @@ -16,218 +15,216 @@ /** * Date manipulation utilities. - * + * * @author Jerome Louvel */ public final class DateUtils { - /** - * Obsoleted HTTP date format (ANSI C asctime() format). Pattern: "EEE MMM dd - * HH:mm:ss yyyy". - */ - public static final List FORMAT_ASC_TIME = unmodifiableList("EEE MMM dd HH:mm:ss yyyy"); - - /** - * Obsoleted HTTP date format (RFC 1036). Pattern: "EEEE, dd-MMM-yy HH:mm:ss - * zzz". - */ - public static final List FORMAT_RFC_1036 = unmodifiableList("EEEE, dd-MMM-yy HH:mm:ss zzz"); - - /** - * Preferred HTTP date format (RFC 1123). Pattern: "EEE, dd MMM yyyy HH:mm:ss - * zzz". - */ - public static final List FORMAT_RFC_1123 = unmodifiableList("EEE, dd MMM yyyy HH:mm:ss zzz"); - - /** W3C date format (RFC 3339). Pattern: "yyyy-MM-dd'T'HH:mm:ssz". */ - public static final List FORMAT_RFC_3339 = unmodifiableList("yyyy-MM-dd'T'HH:mm:ssz"); - - /** AWS date format (ISO 8601). Pattern: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'". */ - public static final List FORMAT_ISO_8601 = unmodifiableList("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - - /** - * Common date format (RFC 822). Patterns: "EEE, dd MMM yy HH:mm:ss z" or "EEE, - * dd MMM yy HH:mm z", "dd MMM yy HH:mm:ss z" or "dd MMM yy HH:mm z". - */ - public static final List FORMAT_RFC_822 = unmodifiableList("EEE, dd MMM yy HH:mm:ss z", - "EEE, dd MMM yy HH:mm z", "dd MMM yy HH:mm:ss z", "dd MMM yy HH:mm z"); - - /** Remember the often used GMT time zone. */ - private static final java.util.TimeZone TIMEZONE_GMT = java.util.TimeZone.getTimeZone("GMT"); - - /** - * Compares two date with a precision of one second. - * - * @param baseDate The base date - * @param afterDate The date supposed to be after. - * @return True if the afterDate is indeed after the baseDate. - */ - public static boolean after(final Date baseDate, final Date afterDate) { - if ((baseDate == null) || (afterDate == null)) { - throw new IllegalArgumentException("Can't compare the dates, at least one of them is null"); - } - - long baseTime = baseDate.getTime() / 1000; - long afterTime = afterDate.getTime() / 1000; - return baseTime < afterTime; - } - - /** - * Compares two date with a precision of one second. - * - * @param baseDate The base date - * @param beforeDate The date supposed to be before. - * @return True if the beforeDate is indeed before the baseDate. - */ - public static boolean before(final Date baseDate, final Date beforeDate) { - if ((baseDate == null) || (beforeDate == null)) { - throw new IllegalArgumentException("Can't compare the dates, at least one of them is null"); - } - - long baseTime = baseDate.getTime() / 1000; - long beforeTime = beforeDate.getTime() / 1000; - return beforeTime < baseTime; - } - - /** - * Compares two date with a precision of one second. - * - * @param baseDate The base date - * @param otherDate The other date supposed to be equals. - * @return True if both dates are equals. - */ - public static boolean equals(final Date baseDate, final Date otherDate) { - if ((baseDate == null) || (otherDate == null)) { - throw new IllegalArgumentException("Can't compare the dates, at least one of them is null"); - } - - long baseTime = baseDate.getTime() / 1000; - long otherTime = otherDate.getTime() / 1000; - return otherTime == baseTime; - } - - /** - * Formats a Date in the default HTTP format (RFC 1123). - * - * @param date The date to format. - * @return The formatted date. - */ - public static String format(final Date date) { - return format(date, DateUtils.FORMAT_RFC_1123.get(0)); - } - - /** - * Formats a Date according to the first format in the array. - * - * @param date The date to format. - * @param formats The array of date formats to use. - * @return The formatted date. - */ - public static String format(final Date date, final List formats) { - return format(date, formats != null ? formats.get(0) : null); - } - - /** - * Formats a Date according to the given format. - * - * @param date The date to format. - * @param format The date format to use. - * @return The formatted date. - */ - public static String format(final Date date, final String format) { - if (date == null) { - throw new IllegalArgumentException("Date is null"); - } - - java.text.DateFormat formatter = null; - - if (FORMAT_RFC_3339.get(0).equals(format)) { - formatter = new InternetDateFormat(TIMEZONE_GMT); - } else { - formatter = new java.text.SimpleDateFormat(format, java.util.Locale.US); - formatter.setTimeZone(TIMEZONE_GMT); - } - - return formatter.format(date); - } - - /** - * Parses a formatted date into a Date object using the default HTTP format (RFC - * 1123). - * - * @param date The date to parse. - * @return The parsed date. - */ - public static Date parse(String date) { - return parse(date, FORMAT_RFC_1123); - } - - /** - * Parses a formatted date into a Date object. - * - * @param date The date to parse. - * @param formats The date formats to use sorted by completeness. - * @return The parsed date. - */ - public static Date parse(String date, List formats) { - if (date == null) { - throw new IllegalArgumentException("Date is null"); - } - - Date result = null; - - String format = null; - int formatsSize = formats.size(); - - for (int i = 0; (result == null) && (i < formatsSize); i++) { - format = formats.get(i); - java.text.DateFormat parser = null; - - if (FORMAT_RFC_3339.get(0).equals(format)) { - parser = new InternetDateFormat(TIMEZONE_GMT); - } else { - parser = new java.text.SimpleDateFormat(format, java.util.Locale.US); - parser.setTimeZone(TIMEZONE_GMT); - } - try { - result = parser.parse(date); - } catch (Exception e) { - // Ignores error as the next format may work better - } - } - - return result; - } - - /** - * Returns an immutable version of a given date. - * - * @param date The modifiable date. - * @return An immutable version of a given date. - */ - public static Date unmodifiable(Date date) { - return (date == null) ? null : new ImmutableDate(date); - } - - /** - * Helper method to help initialize this class by providing unmodifiable lists - * based on arrays. - * - * @param Any valid java object - * @param array to be converted into an unmodifiable list - * @return unmodifiable list based on the provided array - */ - @SafeVarargs - private static List unmodifiableList(final T... array) { - return Collections.unmodifiableList(Arrays.asList(array)); - } - - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private DateUtils() { - - } - + /** + * Obsoleted HTTP date format (ANSI C asctime() format). Pattern: "EEE MMM dd HH:mm:ss yyyy". + */ + public static final List FORMAT_ASC_TIME = unmodifiableList("EEE MMM dd HH:mm:ss yyyy"); + + /** Obsoleted HTTP date format (RFC 1036). Pattern: "EEEE, dd-MMM-yy HH:mm:ss zzz". */ + public static final List FORMAT_RFC_1036 = + unmodifiableList("EEEE, dd-MMM-yy HH:mm:ss zzz"); + + /** Preferred HTTP date format (RFC 1123). Pattern: "EEE, dd MMM yyyy HH:mm:ss zzz". */ + public static final List FORMAT_RFC_1123 = + unmodifiableList("EEE, dd MMM yyyy HH:mm:ss zzz"); + + /** W3C date format (RFC 3339). Pattern: "yyyy-MM-dd'T'HH:mm:ssz". */ + public static final List FORMAT_RFC_3339 = unmodifiableList("yyyy-MM-dd'T'HH:mm:ssz"); + + /** AWS date format (ISO 8601). Pattern: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'". */ + public static final List FORMAT_ISO_8601 = + unmodifiableList("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + /** + * Common date format (RFC 822). Patterns: "EEE, dd MMM yy HH:mm:ss z" or "EEE, dd MMM yy HH:mm + * z", "dd MMM yy HH:mm:ss z" or "dd MMM yy HH:mm z". + */ + public static final List FORMAT_RFC_822 = + unmodifiableList( + "EEE, dd MMM yy HH:mm:ss z", + "EEE, dd MMM yy HH:mm z", + "dd MMM yy HH:mm:ss z", + "dd MMM yy HH:mm z"); + + /** Remember the often used GMT time zone. */ + private static final java.util.TimeZone TIMEZONE_GMT = java.util.TimeZone.getTimeZone("GMT"); + + /** + * Compares two date with a precision of one second. + * + * @param baseDate The base date + * @param afterDate The date supposed to be after. + * @return True if the afterDate is indeed after the baseDate. + */ + public static boolean after(final Date baseDate, final Date afterDate) { + if ((baseDate == null) || (afterDate == null)) { + throw new IllegalArgumentException( + "Can't compare the dates, at least one of them is null"); + } + + long baseTime = baseDate.getTime() / 1000; + long afterTime = afterDate.getTime() / 1000; + return baseTime < afterTime; + } + + /** + * Compares two date with a precision of one second. + * + * @param baseDate The base date + * @param beforeDate The date supposed to be before. + * @return True if the beforeDate is indeed before the baseDate. + */ + public static boolean before(final Date baseDate, final Date beforeDate) { + if ((baseDate == null) || (beforeDate == null)) { + throw new IllegalArgumentException( + "Can't compare the dates, at least one of them is null"); + } + + long baseTime = baseDate.getTime() / 1000; + long beforeTime = beforeDate.getTime() / 1000; + return beforeTime < baseTime; + } + + /** + * Compares two date with a precision of one second. + * + * @param baseDate The base date + * @param otherDate The other date supposed to be equals. + * @return True if both dates are equals. + */ + public static boolean equals(final Date baseDate, final Date otherDate) { + if ((baseDate == null) || (otherDate == null)) { + throw new IllegalArgumentException( + "Can't compare the dates, at least one of them is null"); + } + + long baseTime = baseDate.getTime() / 1000; + long otherTime = otherDate.getTime() / 1000; + return otherTime == baseTime; + } + + /** + * Formats a Date in the default HTTP format (RFC 1123). + * + * @param date The date to format. + * @return The formatted date. + */ + public static String format(final Date date) { + return format(date, DateUtils.FORMAT_RFC_1123.getFirst()); + } + + /** + * Formats a Date according to the first format in the array. + * + * @param date The date to format. + * @param formats The array of date formats to use. + * @return The formatted date. + */ + public static String format(final Date date, final List formats) { + return format(date, formats != null ? formats.getFirst() : null); + } + + /** + * Formats a Date according to the given format. + * + * @param date The date to format. + * @param format The date format to use. + * @return The formatted date. + */ + public static String format(final Date date, final String format) { + if (date == null) { + throw new IllegalArgumentException("Date is null"); + } + + final java.text.DateFormat formatter; + + if (FORMAT_RFC_3339.getFirst().equals(format)) { + formatter = new InternetDateFormat(TIMEZONE_GMT); + } else { + formatter = new java.text.SimpleDateFormat(format, java.util.Locale.US); + formatter.setTimeZone(TIMEZONE_GMT); + } + + return formatter.format(date); + } + + /** + * Parses a formatted date into a Date object using the default HTTP format (RFC 1123). + * + * @param date The date to parse. + * @return The parsed date. + */ + public static Date parse(String date) { + return parse(date, FORMAT_RFC_1123); + } + + /** + * Parses a formatted date into a Date object. + * + * @param date The date to parse. + * @param formats The date formats to use sorted by completeness. + * @return The parsed date. + */ + public static Date parse(String date, List formats) { + if (date == null) { + throw new IllegalArgumentException("Date is null"); + } + + Date result = null; + + String format = null; + int formatsSize = formats.size(); + + for (int i = 0; (result == null) && (i < formatsSize); i++) { + format = formats.get(i); + java.text.DateFormat parser = null; + + if (FORMAT_RFC_3339.getFirst().equals(format)) { + parser = new InternetDateFormat(TIMEZONE_GMT); + } else { + parser = new java.text.SimpleDateFormat(format, java.util.Locale.US); + parser.setTimeZone(TIMEZONE_GMT); + } + try { + result = parser.parse(date); + } catch (Exception e) { + // Ignores error as the next format may work better + } + } + + return result; + } + + /** + * Returns an immutable version of a given date. + * + * @param date The modifiable date. + * @return An immutable version of a given date. + */ + public static Date unmodifiable(Date date) { + return (date == null) ? null : new ImmutableDate(date); + } + + /** + * Helper method to help initialize this class by providing unmodifiable lists based on arrays. + * + * @param Any valid java object + * @param array to be converted into an unmodifiable list + * @return unmodifiable list based on the provided array + */ + @SafeVarargs + private static List unmodifiableList(final T... array) { + return Collections.unmodifiableList(Arrays.asList(array)); + } + + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private DateUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java index 729106a8d5..73b04bade1 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java @@ -1,145 +1,172 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; import org.restlet.Context; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - /** - * A Utility class which extends the provided {@link DefaultHandler} and - * implements the {@link org.w3c.dom.ls.LSResourceResolver} interface. All the - * methods of this class do nothing besides generating log messages. - * + * A Utility class which extends the provided {@link DefaultHandler} and implements the {@link + * org.w3c.dom.ls.LSResourceResolver} interface. All the methods of this class do nothing besides + * generating log messages. + * * @author Raif S. Naffah * @author Jerome Louvel */ -public class DefaultSaxHandler extends DefaultHandler implements org.w3c.dom.ls.LSResourceResolver -{ +public class DefaultSaxHandler extends DefaultHandler implements org.w3c.dom.ls.LSResourceResolver { - /** - * A class field set to {@code true} if the JAXP debug property is turned - * on; {@code false} otherwise. This is used to control the degree of output - * generated in the logs. - */ - private static boolean debug; + /** + * A class field set to {@code true} if the JAXP debug property is turned on; {@code false} + * otherwise. This is used to control the degree of output generated in the logs. + */ + private static boolean debug; - static { - try { - final String debugStr = System.getProperty("jaxp.debug"); - debug = debugStr != null && !"false".equalsIgnoreCase(debugStr); - } catch (SecurityException x) { - debug = false; - } - } + static { + try { + final String debugStr = System.getProperty("jaxp.debug"); + debug = debugStr != null && !"false".equalsIgnoreCase(debugStr); + } catch (SecurityException x) { + debug = false; + } + } - /** - * Set to {@code true} if the current Context's logger is capable of outputting - * messages at the CONFIG level; {@code false} otherwise. - */ - private volatile boolean loggable; + /** + * Set to {@code true} if the current Context's logger is capable of outputting messages at the + * CONFIG level; {@code false} otherwise. + */ + private volatile boolean loggable; - /** The current context JDK {@link Logger} to use for message output. */ - private volatile Logger logger; + /** The current context JDK {@link Logger} to use for message output. */ + private volatile Logger logger; - /** - * Trivial constructor. - */ - public DefaultSaxHandler() { - super(); - logger = Context.getCurrentLogger(); - loggable = logger.isLoggable(Level.CONFIG); - } + /** Trivial constructor. */ + public DefaultSaxHandler() { + super(); + logger = Context.getCurrentLogger(); + loggable = logger.isLoggable(Level.CONFIG); + } - @Override - public void error(SAXParseException x) throws SAXException { - if (loggable) { - final String msg = "[ERROR] - Unexpected exception while parsing " + "an instance of PUBLIC [" - + x.getPublicId() + "], SYSTEM [" + x.getSystemId() + "] - line #" + x.getLineNumber() - + ", column #" + x.getColumnNumber(); - if (debug) { - Context.getCurrentLogger().log(Level.CONFIG, msg, x); - } else { - logger.config(msg + ": " + x.getLocalizedMessage()); - } - } - } + @Override + public void error(SAXParseException x) throws SAXException { + if (loggable) { + final String msg = + "[ERROR] - Unexpected exception while parsing " + + "an instance of PUBLIC [" + + x.getPublicId() + + "], SYSTEM [" + + x.getSystemId() + + "] - line #" + + x.getLineNumber() + + ", column #" + + x.getColumnNumber(); + if (debug) { + Context.getCurrentLogger().log(Level.CONFIG, msg, x); + } else { + logger.config(msg + ": " + x.getLocalizedMessage()); + } + } + } - @Override - public void fatalError(SAXParseException x) throws SAXException { - if (loggable) { - final String msg = "[FATAL] - Unexpected exception while parsing " + "an instance of PUBLIC [" - + x.getPublicId() + "], SYSTEM [" + x.getSystemId() + "] - line #" + x.getLineNumber() - + ", column #" + x.getColumnNumber(); - if (debug) { - Context.getCurrentLogger().log(Level.CONFIG, msg, x); - } else { - logger.config(msg + ": " + x.getLocalizedMessage()); - } - } - } + @Override + public void fatalError(SAXParseException x) { + if (loggable) { + final String msg = + "[FATAL] - Unexpected exception while parsing " + + "an instance of PUBLIC [" + + x.getPublicId() + + "], SYSTEM [" + + x.getSystemId() + + "] - line #" + + x.getLineNumber() + + ", column #" + + x.getColumnNumber(); + if (debug) { + Context.getCurrentLogger().log(Level.CONFIG, msg, x); + } else { + logger.config(msg + ": " + x.getLocalizedMessage()); + } + } + } - @Override - public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { - if (loggable) { - logger.config("Resolve entity with PUBLIC [" + publicId + "], and SYSTEM [" + systemId + "]"); - } - return super.resolveEntity(publicId, systemId); - } + @Override + public InputSource resolveEntity(String publicId, String systemId) + throws IOException, SAXException { + if (loggable) { + logger.config( + "Resolve entity with PUBLIC [" + publicId + "], and SYSTEM [" + systemId + "]"); + } + return super.resolveEntity(publicId, systemId); + } - /** - * Allow the application to resolve external resources. - *

- * This implementation always returns a {@code null}. - * - * @param type The type of the resource being resolved. - * @param namespaceUri The namespace of the resource being resolved. - * @param publicId The public identifier. - * @param systemId The system identifier. - * @param baseUri The absolute base URI of the resource being parsed. - * @return Always {@code null}. - */ - public org.w3c.dom.ls.LSInput resolveResource(String type, String namespaceUri, String publicId, String systemId, - String baseUri) { - if (loggable) { - logger.config("Resolve resource with type [" + type + "], namespace URI [" + namespaceUri + "], PUBLIC [" - + publicId + "], SYSTEM [" + systemId + "], and base URI [" + baseUri + "]"); - } - return null; - } + /** + * Allow the application to resolve external resources. + * + *

This implementation always returns a {@code null}. + * + * @param type The type of the resource being resolved. + * @param namespaceUri The namespace of the resource being resolved. + * @param publicId The public identifier. + * @param systemId The system identifier. + * @param baseUri The absolute base URI of the resource being parsed. + * @return Always {@code null}. + */ + public org.w3c.dom.ls.LSInput resolveResource( + String type, String namespaceUri, String publicId, String systemId, String baseUri) { + if (loggable) { + logger.config( + "Resolve resource with type [" + + type + + "], namespace URI [" + + namespaceUri + + "], PUBLIC [" + + publicId + + "], SYSTEM [" + + systemId + + "], and base URI [" + + baseUri + + "]"); + } + return null; + } - @Override - public void skippedEntity(String name) throws SAXException { - super.skippedEntity(name); - if (loggable) { - logger.config("Skipped entity named [" + name + "]"); - } - } + @Override + public void skippedEntity(String name) throws SAXException { + super.skippedEntity(name); + if (loggable) { + logger.config("Skipped entity named [" + name + "]"); + } + } - @Override - public void warning(SAXParseException x) throws SAXException { - if (loggable) { - final String msg = "[WARN] - Unexpected exception while parsing " + "an instance of PUBLIC [" - + x.getPublicId() + "], SYSTEM [" + x.getSystemId() + "] - line #" + x.getLineNumber() - + ", column #" + x.getColumnNumber(); - if (debug) { - Context.getCurrentLogger().log(Level.CONFIG, msg, x); - } else { - logger.config(msg + ": " + x.getLocalizedMessage()); - } - } - } + @Override + public void warning(SAXParseException x) throws SAXException { + if (loggable) { + final String msg = + "[WARN] - Unexpected exception while parsing " + + "an instance of PUBLIC [" + + x.getPublicId() + + "], SYSTEM [" + + x.getSystemId() + + "] - line #" + + x.getLineNumber() + + ", column #" + + x.getColumnNumber(); + if (debug) { + Context.getCurrentLogger().log(Level.CONFIG, msg, x); + } else { + logger.config(msg + ": " + x.getLocalizedMessage()); + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java b/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java index ab92cdb16b..395544b14c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java @@ -1,168 +1,162 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; -import org.restlet.engine.Edition; -import org.restlet.engine.Engine; - import java.io.IOException; import java.net.URL; import java.util.Enumeration; import java.util.Vector; +import org.restlet.engine.Edition; +import org.restlet.engine.Engine; /** - * Flexible engine class loader. Uses the current class's class loader as its - * parent. Can also check with the user class loader defined by - * {@link Engine#getUserClassLoader()} or with - * {@link Thread#getContextClassLoader()} or with {@link Class#forName(String)}. - * + * Flexible engine class loader. Uses the current class's class loader as its parent. Can also check + * with the user class loader defined by {@link Engine#getUserClassLoader()} or with {@link + * Thread#getContextClassLoader()} or with {@link Class#forName(String)}. + * * @author Jerome Louvel */ public class EngineClassLoader extends ClassLoader { - /** The parent Restlet engine. */ - private final Engine engine; - - /** - * Constructor. - */ - public EngineClassLoader(Engine engine) { - super(EngineClassLoader.class.getClassLoader()); - this.engine = engine; - } - - @Override - protected Class findClass(String name) throws ClassNotFoundException { - Class result = null; - - // First try the user class loader - ClassLoader cl = getEngine().getUserClassLoader(); - - if (cl != null) { - try { - result = cl.loadClass(name); - } catch (ClassNotFoundException cnfe) { - // Ignore - } - } - - // Then try the current thread's class loader - if (result == null) { - cl = Thread.currentThread().getContextClassLoader(); - - if (cl != null) { - try { - result = cl.loadClass(name); - } catch (ClassNotFoundException cnfe) { - // Ignore - } - } - } - - // Finally try with this ultimate approach - if (result == null) { - try { - result = Class.forName(name); - } catch (ClassNotFoundException cnfe) { - // Ignore - } - } - - // Otherwise throw an exception - if (result == null) { - throw new ClassNotFoundException(name); - } - - return result; - } - - @Override - protected URL findResource(String name) { - URL result = null; - - // First try the user class loader - ClassLoader cl = getEngine().getUserClassLoader(); - - if (cl != null) { - result = cl.getResource(name); - } - - // Then try the current thread's class loader - if (result == null) { - cl = Thread.currentThread().getContextClassLoader(); - - if (cl != null) { - result = cl.getResource(name); - } - } - - return result; - } - - @Override - protected Enumeration findResources(String name) throws IOException { - Enumeration result = null; - - // First try the user class loader - ClassLoader cl = getEngine().getUserClassLoader(); - - if (cl != null) { - result = cl.getResources(name); - } - - // Then try the current thread's class loader - if (result == null) { - cl = Thread.currentThread().getContextClassLoader(); - - if (cl != null) { - result = cl.getResources(name); - } - } - - return result; - } - - /** - * Returns the parent Restlet engine. - * - * @return The parent Restlet engine. - */ - protected Engine getEngine() { - return engine; - } - - @Override - public Enumeration getResources(String name) throws IOException { - Enumeration allUrls = super.getResources(name); - Vector result = new Vector(); - - if (allUrls != null) { - try { - URL url; - while (allUrls.hasMoreElements()) { - url = allUrls.nextElement(); - - if (!result.contains(url)) { - result.add(url); - } - } - } catch (NullPointerException e) { - // At this time (June 2009) a NPE is thrown with Dalvik JVM. - // Let's throw the NPE for the other editions. - if (Edition.ANDROID.isNotCurrentEdition()) { - throw e; - } - } - } - - return result.elements(); - } - + /** The parent Restlet engine. */ + private final Engine engine; + + /** Constructor. */ + public EngineClassLoader(Engine engine) { + super(EngineClassLoader.class.getClassLoader()); + this.engine = engine; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + Class result = null; + + // First, try the user class loader + ClassLoader cl = getEngine().getUserClassLoader(); + + if (cl != null) { + try { + result = cl.loadClass(name); + } catch (ClassNotFoundException cnfe) { + // Ignore + } + } + + // Then try the current thread's class loader + if (result == null) { + cl = Thread.currentThread().getContextClassLoader(); + + if (cl != null) { + try { + result = cl.loadClass(name); + } catch (ClassNotFoundException ignored) { + // Ignore + } + } + } + + // Finally try with this ultimate approach + if (result == null) { + try { + result = Class.forName(name); + } catch (ClassNotFoundException ignored) { + // Ignore + } + } + + // Otherwise throw an exception + if (result == null) { + throw new ClassNotFoundException(name); + } + + return result; + } + + @Override + protected URL findResource(String name) { + URL result = null; + + // First, try the user class loader + ClassLoader cl = getEngine().getUserClassLoader(); + + if (cl != null) { + result = cl.getResource(name); + } + + // Then try the current thread's class loader + if (result == null) { + cl = Thread.currentThread().getContextClassLoader(); + + if (cl != null) { + result = cl.getResource(name); + } + } + + return result; + } + + @Override + protected Enumeration findResources(String name) throws IOException { + Enumeration result = null; + + // First, try the user class loader + ClassLoader cl = getEngine().getUserClassLoader(); + + if (cl != null) { + result = cl.getResources(name); + } + + // Then try the current thread's class loader + if (result == null) { + cl = Thread.currentThread().getContextClassLoader(); + + if (cl != null) { + result = cl.getResources(name); + } + } + + return result; + } + + /** + * Returns the parent Restlet engine. + * + * @return The parent Restlet engine. + */ + protected Engine getEngine() { + return engine; + } + + @Override + public Enumeration getResources(String name) throws IOException { + Enumeration allUrls = super.getResources(name); + Vector result = new Vector(); + + if (allUrls != null) { + try { + URL url; + while (allUrls.hasMoreElements()) { + url = allUrls.nextElement(); + + if (!result.contains(url)) { + result.add(url); + } + } + } catch (NullPointerException e) { + // At this time (June 2009) a NPE is thrown with Dalvik JVM. + // Let's throw the NPE for the other editions. + if (Edition.ANDROID.isNotCurrentEdition()) { + throw e; + } + } + } + + return result.elements(); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java b/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java index bdc8c28732..8fc3c0191b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java @@ -1,21 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; -import org.restlet.Context; -import org.restlet.data.CharacterSet; -import org.restlet.data.Form; -import org.restlet.data.Parameter; -import org.restlet.representation.Representation; -import org.restlet.util.Series; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -24,347 +16,361 @@ import java.util.List; import java.util.Map; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.data.CharacterSet; +import org.restlet.data.Form; +import org.restlet.data.Parameter; +import org.restlet.representation.Representation; +import org.restlet.util.Series; /** * Form reader. - * + * * @author Jerome Louvel */ public class FormReader { - /** The encoding to use, decoding is enabled, see {@link #decode}. */ - private volatile CharacterSet characterSet; - - /** Indicates if the parameters should be decoded. */ - private volatile boolean decode; - - /** The separator character used between parameters. */ - private volatile char separator; - - /** The form stream. */ - private volatile InputStream stream; - - /** - * Constructor.
- * In case the representation does not define a character set, the UTF-8 - * character set is used. - * - * @param representation The web form content. - * @throws IOException if the stream of the representation could not be opened. - */ - public FormReader(Representation representation) throws IOException { - this(representation, true); - } - - /** - * Constructor.
- * In case the representation does not define a character set, the UTF-8 - * character set is used. - * - * @param representation The web form content. - * @param decode Indicates if the parameters should be decoded using the - * given character set. - * @throws IOException if the stream of the representation could not be opened. - */ - public FormReader(Representation representation, boolean decode) throws IOException { - this.decode = decode; - this.stream = representation.getStream(); - this.separator = '&'; - - if (representation.getCharacterSet() != null) { - this.characterSet = representation.getCharacterSet(); - } else { - this.characterSet = CharacterSet.UTF_8; - } - } - - /** - * Constructor. Will leave the parsed data encoded. - * - * @param parametersString The parameters string. - * @param separator The separator character used between parameters. - */ - public FormReader(String parametersString, char separator) { - this(parametersString, null, separator, false); - } - - /** - * Constructor. - * - * @param parametersString The parameters string. - * @param characterSet The supported character encoding. Set to null to - * leave the data encoded. - * @param separator The separator character used between parameters. - */ - public FormReader(String parametersString, CharacterSet characterSet, char separator) { - this(parametersString, characterSet, separator, true); - } - - /** - * Constructor. - * - * @param parametersString The parameters string. - * @param characterSet The supported character encoding. Set to null to - * leave the data encoded. - * @param separator The separator character used between parameters. - * @param decode Indicates if the parameters should be decoded using - * the given character set. - */ - public FormReader(String parametersString, CharacterSet characterSet, char separator, boolean decode) { - this.decode = decode; - this.stream = new ByteArrayInputStream(parametersString.getBytes()); - this.characterSet = characterSet; - this.separator = separator; - } - - /** - * Adds the parameters into a given series. - * - * @param parameters The target parameter series. - */ - public void addParameters(Series parameters) { - boolean readNext = true; - Parameter param = null; - - if (this.stream != null) { - // Let's read all form parameters - try { - while (readNext) { - param = readNextParameter(); - - if (param != null) { - // Add parsed parameter to the form - parameters.add(param); - } else { - // Last parameter parsed - readNext = false; - } - } - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.WARNING, - "Unable to parse a form parameter. Skipping the remaining parameters.", ioe); - } - - try { - this.stream.close(); - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to close the form input stream", ioe); - } - } - } - - /** - * Reads all the parameters. - * - * @return The form read. - * @throws IOException If the parameters could not be read. - */ - public Form read() throws IOException { - Form result = new Form(); - - if (this.stream != null) { - Parameter param = readNextParameter(); - - while (param != null) { - result.add(param); - param = readNextParameter(); - } - - this.stream.close(); - } - - return result; - } - - /** - * Reads the first parameter with the given name. - * - * @param name The parameter name to match. - * @return The parameter value. - * @throws IOException - */ - public Parameter readFirstParameter(String name) throws IOException { - Parameter result = null; - - if (this.stream != null) { - Parameter param = readNextParameter(); - - while ((param != null) && (result == null)) { - if (param.getName().equals(name)) { - result = param; - } - - param = readNextParameter(); - } - - this.stream.close(); - } - - return result; - } - - /** - * Reads the next parameter available or null. - * - * @return The next parameter available or null. - * @throws IOException If the next parameter could not be read. - */ - public Parameter readNextParameter() throws IOException { - Parameter result = null; - - if (this.stream != null) { - try { - boolean readingName = true; - StringBuilder nameBuffer = new StringBuilder(); - StringBuilder valueBuffer = new StringBuilder(); - int nextChar = 0; - - while ((result == null) && (nextChar != -1)) { - nextChar = this.stream.read(); - - if (readingName) { - if (nextChar == '=') { - if (!nameBuffer.isEmpty()) { - readingName = false; - } else { - throw new IOException("Empty parameter name detected. Please check your form data"); - } - } else if (endOfCurrentParameterReached(nextChar)) { - if (!nameBuffer.isEmpty()) { - result = FormUtils.create(nameBuffer, null, this.decode, this.characterSet); - } else if (nextChar == -1) { - // Do nothing return null preference - } else { - Context.getCurrentLogger() - .fine("Empty parameter name detected. Please check your form data"); - } - } else { - nameBuffer.append((char) nextChar); - } - } else { - // reading value - if (endOfCurrentParameterReached(nextChar)) { - result = FormUtils.create(nameBuffer, valueBuffer, this.decode, this.characterSet); - } else { - valueBuffer.append((char) nextChar); - } - } - } - } catch (UnsupportedEncodingException uee) { - throw new IOException("Unsupported encoding. Please contact the administrator"); - } - } - - return result; - } - - private boolean endOfCurrentParameterReached(int nextChar) { - return (nextChar == this.separator) || (nextChar == -1); - } - - /** - * Reads the parameters with the given name. If multiple values are found, a - * list is returned created. - * - * @param name The parameter name to match. - * @return The parameter value or list of values. - * @throws IOException If the parameters could not be read. - */ - @SuppressWarnings("unchecked") - public Object readParameter(String name) throws IOException { - Object result = null; - - if (this.stream != null) { - Parameter param = readNextParameter(); - - while (param != null) { - if (param.getName().equals(name)) { - if (result != null) { - List values = null; - - if (result instanceof List) { - // Multiple values already found for this parameter - values = (List) result; - } else { - // Second value found for this parameter - // Create a list of values - values = new ArrayList(); - values.add(result); - result = values; - } - - if (param.getValue() == null) { - values.add(Series.EMPTY_VALUE); - } else { - values.add(param.getValue()); - } - } else { - if (param.getValue() == null) { - result = Series.EMPTY_VALUE; - } else { - result = param.getValue(); - } - } - } - - param = readNextParameter(); - } - - this.stream.close(); - } - - return result; - } - - /** - * Reads the parameters whose name is a key in the given map. If a matching - * parameter is found, its value is put in the map. If multiple values are - * found, a list is created and set in the map. - * - * @param parameters The parameters map controlling the reading. - * @throws IOException If the parameters could not be read. - */ - @SuppressWarnings("unchecked") - public void readParameters(Map parameters) throws IOException { - if (this.stream != null) { - Parameter param = readNextParameter(); - Object currentValue = null; - - while (param != null) { - if (parameters.containsKey(param.getName())) { - currentValue = parameters.get(param.getName()); - - if (currentValue != null) { - List values = null; - - if (currentValue instanceof List) { - // Multiple values already found for this parameter - values = (List) currentValue; - } else { - // Second value found for this parameter - // Create a list of values - values = new ArrayList(); - values.add(currentValue); - parameters.put(param.getName(), values); - } - - if (param.getValue() == null) { - values.add(Series.EMPTY_VALUE); - } else { - values.add(param.getValue()); - } - } else { - if (param.getValue() == null) { - parameters.put(param.getName(), Series.EMPTY_VALUE); - } else { - parameters.put(param.getName(), param.getValue()); - } - } - } - - param = readNextParameter(); - } - - this.stream.close(); - } - } + /** The encoding to use, decoding is enabled, see {@link #decode}. */ + private volatile CharacterSet characterSet; + + /** Indicates if the parameters should be decoded. */ + private volatile boolean decode; + + /** The separator character used between parameters. */ + private volatile char separator; + + /** The form stream. */ + private volatile InputStream stream; + + /** + * Constructor.
+ * In case the representation does not define a character set, the UTF-8 character set is used. + * + * @param representation The web form content. + * @throws IOException if the stream of the representation could not be opened. + */ + public FormReader(Representation representation) throws IOException { + this(representation, true); + } + + /** + * Constructor.
+ * In case the representation does not define a character set, the UTF-8 character set is used. + * + * @param representation The web form content. + * @param decode Indicates if the parameters should be decoded using the given character set. + * @throws IOException if the stream of the representation could not be opened. + */ + public FormReader(Representation representation, boolean decode) throws IOException { + this.decode = decode; + this.stream = representation.getStream(); + this.separator = '&'; + + if (representation.getCharacterSet() != null) { + this.characterSet = representation.getCharacterSet(); + } else { + this.characterSet = CharacterSet.UTF_8; + } + } + + /** + * Constructor. Will leave the parsed data encoded. + * + * @param parametersString The parameters string. + * @param separator The separator character used between parameters. + */ + public FormReader(String parametersString, char separator) { + this(parametersString, null, separator, false); + } + + /** + * Constructor. + * + * @param parametersString The parameters string. + * @param characterSet The supported character encoding. Set to null to leave the data encoded. + * @param separator The separator character used between parameters. + */ + public FormReader(String parametersString, CharacterSet characterSet, char separator) { + this(parametersString, characterSet, separator, true); + } + + /** + * Constructor. + * + * @param parametersString The parameters string. + * @param characterSet The supported character encoding. Set to null to leave the data encoded. + * @param separator The separator character used between parameters. + * @param decode Indicates if the parameters should be decoded using the given character set. + */ + public FormReader( + String parametersString, CharacterSet characterSet, char separator, boolean decode) { + this.decode = decode; + this.stream = new ByteArrayInputStream(parametersString.getBytes()); + this.characterSet = characterSet; + this.separator = separator; + } + + /** + * Adds the parameters into a given series. + * + * @param parameters The target parameter series. + */ + public void addParameters(Series parameters) { + boolean readNext = true; + Parameter param = null; + + if (this.stream != null) { + // Let's read all form parameters + try { + while (readNext) { + param = readNextParameter(); + + if (param != null) { + // Add parsed parameter to the form + parameters.add(param); + } else { + // Last parameter parsed + readNext = false; + } + } + } catch (IOException ioe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to parse a form parameter. Skipping the remaining parameters.", + ioe); + } + + try { + this.stream.close(); + } catch (IOException ioe) { + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to close the form input stream", ioe); + } + } + } + + /** + * Reads all the parameters. + * + * @return The form read. + * @throws IOException If the parameters could not be read. + */ + public Form read() throws IOException { + Form result = new Form(); + + if (this.stream != null) { + Parameter param = readNextParameter(); + + while (param != null) { + result.add(param); + param = readNextParameter(); + } + + this.stream.close(); + } + + return result; + } + + /** + * Reads the first parameter with the given name. + * + * @param name The parameter name to match. + * @return The parameter value. + * @throws IOException + */ + public Parameter readFirstParameter(String name) throws IOException { + Parameter result = null; + + if (this.stream != null) { + Parameter param = readNextParameter(); + + while ((param != null) && (result == null)) { + if (param.getName().equals(name)) { + result = param; + } + + param = readNextParameter(); + } + + this.stream.close(); + } + + return result; + } + + /** + * Reads the next parameter available or null. + * + * @return The next parameter available or null. + * @throws IOException If the next parameter could not be read. + */ + public Parameter readNextParameter() throws IOException { + Parameter result = null; + + if (this.stream != null) { + try { + boolean readingName = true; + StringBuilder nameBuffer = new StringBuilder(); + StringBuilder valueBuffer = new StringBuilder(); + int nextChar = 0; + + while ((result == null) && (nextChar != -1)) { + nextChar = this.stream.read(); + + if (readingName) { + if (nextChar == '=') { + if (!nameBuffer.isEmpty()) { + readingName = false; + } else { + throw new IOException( + "Empty parameter name detected. Please check your form data"); + } + } else if (endOfCurrentParameterReached(nextChar)) { + if (!nameBuffer.isEmpty()) { + result = + FormUtils.create( + nameBuffer, null, this.decode, this.characterSet); + } else if (nextChar == -1) { + // Do nothing and return a null preference + } else { + Context.getCurrentLogger() + .fine( + "Empty parameter name detected. Please check your form data"); + } + } else { + nameBuffer.append((char) nextChar); + } + } else { + // reading value + if (endOfCurrentParameterReached(nextChar)) { + result = + FormUtils.create( + nameBuffer, + valueBuffer, + this.decode, + this.characterSet); + } else { + valueBuffer.append((char) nextChar); + } + } + } + } catch (UnsupportedEncodingException uee) { + throw new IOException("Unsupported encoding. Please contact the administrator"); + } + } + + return result; + } + + private boolean endOfCurrentParameterReached(int nextChar) { + return (nextChar == this.separator) || (nextChar == -1); + } + + /** + * Reads the parameters with the given name. If multiple values are found, a list is returned + * created. + * + * @param name The parameter name to match. + * @return The parameter value or list of values. + * @throws IOException If the parameters could not be read. + */ + @SuppressWarnings("unchecked") + public Object readParameter(String name) throws IOException { + Object result = null; + + if (this.stream != null) { + Parameter param = readNextParameter(); + + while (param != null) { + if (param.getName().equals(name)) { + if (result != null) { + final List values; + + if (result instanceof List) { + // Multiple values already found for this parameter + values = (List) result; + } else { + // Second value found for this parameter + // Create a list of values + values = new ArrayList<>(); + values.add(result); + result = values; + } + + if (param.getValue() == null) { + values.add(Series.EMPTY_VALUE); + } else { + values.add(param.getValue()); + } + } else { + if (param.getValue() == null) { + result = Series.EMPTY_VALUE; + } else { + result = param.getValue(); + } + } + } + + param = readNextParameter(); + } + + this.stream.close(); + } + + return result; + } + + /** + * Reads the parameters whose name is a key in the given map. If a matching parameter is found, + * its value is put in the map. If multiple values are found, a list is created and set in the + * map. + * + * @param parameters The parameters map controlling the reading. + * @throws IOException If the parameters could not be read. + */ + @SuppressWarnings("unchecked") + public void readParameters(Map parameters) throws IOException { + if (this.stream != null) { + Parameter param = readNextParameter(); + Object currentValue = null; + + while (param != null) { + if (parameters.containsKey(param.getName())) { + currentValue = parameters.get(param.getName()); + + if (currentValue != null) { + final List values; + + if (currentValue instanceof List) { + // Multiple values already found for this parameter + values = (List) currentValue; + } else { + // Second value found for this parameter + // Create a list of values + values = new ArrayList<>(); + values.add(currentValue); + parameters.put(param.getName(), values); + } + + if (param.getValue() == null) { + values.add(Series.EMPTY_VALUE); + } else { + values.add(param.getValue()); + } + } else { + if (param.getValue() == null) { + parameters.put(param.getName(), Series.EMPTY_VALUE); + } else { + parameters.put(param.getName(), param.getValue()); + } + } + } + + param = readNextParameter(); + } + + this.stream.close(); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java index abe6899fd1..efe4bf29d7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java @@ -1,239 +1,256 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; -import org.restlet.Context; -import org.restlet.data.*; -import org.restlet.representation.Representation; - import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.data.CharacterSet; +import org.restlet.data.Form; +import org.restlet.data.MediaType; +import org.restlet.data.Parameter; +import org.restlet.data.Reference; +import org.restlet.representation.Representation; /** * Representation of a Web form containing submitted parameters. - * + * * @author Jerome Louvel */ public class FormUtils { - /** - * Creates a parameter. - * - * @param name The parameter name buffer. - * @param value The parameter value buffer (can be null). - * @param decode If true, the name and values are decoded with the given - * {@link CharacterSet}, if false, than nothing is decoded. - * @param characterSet The supported character encoding. - * @return The created parameter. - */ - public static Parameter create(CharSequence name, CharSequence value, boolean decode, CharacterSet characterSet) { - Parameter result = null; - - if (name != null) { - String nameStr; - if (decode) { - nameStr = Reference.decode(name.toString(), characterSet); - } else { - nameStr = name.toString(); - } - if (value != null) { - String valueStr; - if (decode) { - valueStr = Reference.decode(value.toString(), characterSet); - } else { - valueStr = value.toString(); - } - result = new Parameter(nameStr, valueStr); - } else { - result = new Parameter(nameStr, null); - } - } - return result; - } - - /** - * Reads the first parameter with the given name. - * - * @param post The web form representation. - * @param name The parameter name to match. - * @return The parameter. - * @throws IOException - */ - public static Parameter getFirstParameter(Representation post, String name) throws IOException { - if (!post.isAvailable()) { - throw new IllegalStateException( - "The Web form cannot be parsed as no fresh content is available. If this entity has been already read once, caching of the entity is required"); - } - - return new FormReader(post).readFirstParameter(name); - } - - /** - * Reads the first parameter with the given name. - * - * @param query The query string. - * @param name The parameter name to match. - * @param characterSet The supported character encoding. - * @param separator The separator character to append between parameters. - * @param decode Indicates if the parameters should be decoded using the - * given character set. - * @return The parameter. - * @throws IOException - */ - public static Parameter getFirstParameter(String query, String name, CharacterSet characterSet, char separator, - boolean decode) throws IOException { - return new FormReader(query, characterSet, separator, decode).readFirstParameter(name); - } - - /** - * Reads the parameters with the given name.
- * If multiple values are found, a list is returned created. - * - * @param form The web form representation. - * @param name The parameter name to match. - * @return The parameter value or list of values. - * @throws IOException If the parameters could not be read. - */ - public static Object getParameter(Representation form, String name) throws IOException { - if (!form.isAvailable()) { - throw new IllegalStateException( - "The Web form cannot be parsed as no fresh content is available. If this entity has been already read once, caching of the entity is required"); - } - - return new FormReader(form).readParameter(name); - } - - /** - * Reads the parameters with the given name.
- * If multiple values are found, a list is returned created. - * - * @param query The query string. - * @param name The parameter name to match. - * @param characterSet The supported character encoding. - * @param separator The separator character to append between parameters. - * @param decode Indicates if the parameters should be decoded using the - * given character set. s * @return The parameter value or - * list of values. - * @throws IOException If the parameters could not be read. - */ - public static Object getParameter(String query, String name, CharacterSet characterSet, char separator, - boolean decode) throws IOException { - return new FormReader(query, characterSet, separator, decode).readParameter(name); - } - - /** - * Reads the parameters whose name is a key in the given map.
- * If a matching parameter is found, its value is put in the map.
- * If multiple values are found, a list is created and set in the map. - * - * @param post The web form representation. - * @param parameters The parameters map controlling the reading. - * @throws IOException If the parameters could not be read. - */ - public static void getParameters(Representation post, Map parameters) throws IOException { - if (!post.isAvailable()) { - throw new IllegalStateException( - "The Web form cannot be parsed as no fresh content is available. If this entity has been already read once, caching of the entity is required"); - } - - new FormReader(post).readParameters(parameters); - } - - /** - * Reads the parameters whose name is a key in the given map.
- * If a matching parameter is found, its value is put in the map.
- * If multiple values are found, a list is created and set in the map. - * - * @param parametersString The query string. - * @param parameters The parameters map controlling the reading. - * @param characterSet The supported character encoding. - * @param separator The separator character to append between parameters. - * @param decode Indicates if the parameters should be decoded using - * the given character set. - * @throws IOException If the parameters could not be read. - */ - public static void getParameters(String parametersString, Map parameters, CharacterSet characterSet, - char separator, boolean decode) throws IOException { - new FormReader(parametersString, characterSet, separator, decode).readParameters(parameters); - } - - /** - * Indicates if the searched parameter is specified in the given media range. - * - * @param searchedParam The searched parameter. - * @param mediaRange The media range to inspect. - * @return True if the searched parameter is specified in the given media range. - */ - public static boolean isParameterFound(Parameter searchedParam, MediaType mediaRange) { - boolean result = false; - - for (Iterator iter = mediaRange.getParameters().iterator(); !result && iter.hasNext();) { - result = searchedParam.equals(iter.next()); - } - - return result; - } - - /** - * Parses a post into a given form. - * - * @param form The target form. - * @param post The posted form. - * @param decode Indicates if the parameters should be decoded. - */ - public static void parse(Form form, Representation post, boolean decode) { - if (post != null) { - if (post.isAvailable()) { - FormReader fr = null; - - try { - fr = new FormReader(post, decode); - } catch (IOException ioe) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to create a form reader. Parsing aborted.", - ioe); - } - - if (fr != null) { - fr.addParameters(form); - } - } else { - Context.getCurrentLogger().log(Level.FINE, - "The form wasn't changed as the given representation isn't available."); - } - } - } - - /** - * Parses a parameters string into a given form. - * - * @param form The target form. - * @param parametersString The parameters string. - * @param characterSet The supported character encoding. - * @param decode Indicates if the parameters should be decoded using - * the given character set. - * @param separator The separator character to append between parameters. - */ - public static void parse(Form form, String parametersString, CharacterSet characterSet, boolean decode, - char separator) { - if ((parametersString != null) && !parametersString.isEmpty()) { - FormReader fr = new FormReader(parametersString, characterSet, separator, decode); - fr.addParameters(form); - } - } - - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e., it isn't instantiable and extensible. - */ - private FormUtils() { - } + /** + * Creates a parameter. + * + * @param name The parameter name buffer. + * @param value The parameter value buffer (can be null). + * @param decode If true, the name and values are decoded with the given {@link CharacterSet}, + * if false, than nothing is decoded. + * @param characterSet The supported character encoding. + * @return The created parameter. + */ + public static Parameter create( + CharSequence name, CharSequence value, boolean decode, CharacterSet characterSet) { + Parameter result = null; + + if (name != null) { + String nameStr; + if (decode) { + nameStr = Reference.decode(name.toString(), characterSet); + } else { + nameStr = name.toString(); + } + if (value != null) { + String valueStr; + if (decode) { + valueStr = Reference.decode(value.toString(), characterSet); + } else { + valueStr = value.toString(); + } + result = new Parameter(nameStr, valueStr); + } else { + result = new Parameter(nameStr, null); + } + } + return result; + } + + /** + * Reads the first parameter with the given name. + * + * @param post The web form representation. + * @param name The parameter name to match. + * @return The parameter. + * @throws IOException + */ + public static Parameter getFirstParameter(Representation post, String name) throws IOException { + if (!post.isAvailable()) { + throw new IllegalStateException( + "The Web form cannot be parsed as no fresh content is available. If this entity has been already read once, caching of the entity is required"); + } + + return new FormReader(post).readFirstParameter(name); + } + + /** + * Reads the first parameter with the given name. + * + * @param query The query string. + * @param name The parameter name to match. + * @param characterSet The supported character encoding. + * @param separator The separator character to append between parameters. + * @param decode Indicates if the parameters should be decoded using the given character set. + * @return The parameter. + * @throws IOException + */ + public static Parameter getFirstParameter( + String query, String name, CharacterSet characterSet, char separator, boolean decode) + throws IOException { + return new FormReader(query, characterSet, separator, decode).readFirstParameter(name); + } + + /** + * Reads the parameters with the given name.
+ * If multiple values are found, a list is returned created. + * + * @param form The web form representation. + * @param name The parameter name to match. + * @return The parameter value or list of values. + * @throws IOException If the parameters could not be read. + */ + public static Object getParameter(Representation form, String name) throws IOException { + if (!form.isAvailable()) { + throw new IllegalStateException( + "The Web form cannot be parsed as no fresh content is available. If this entity has been already read once, caching of the entity is required"); + } + + return new FormReader(form).readParameter(name); + } + + /** + * Reads the parameters with the given name.
+ * If multiple values are found, a list is returned created. + * + * @param query The query string. + * @param name The parameter name to match. + * @param characterSet The supported character encoding. + * @param separator The separator character to append between parameters. + * @param decode Indicates if the parameters should be decoded using the given character set. s + * * @return The parameter value or list of values. + * @throws IOException If the parameters could not be read. + */ + public static Object getParameter( + String query, String name, CharacterSet characterSet, char separator, boolean decode) + throws IOException { + return new FormReader(query, characterSet, separator, decode).readParameter(name); + } + + /** + * Reads the parameters whose name is a key in the given map.
+ * If a matching parameter is found, its value is put in the map.
+ * If multiple values are found, a list is created and set in the map. + * + * @param post The web form representation. + * @param parameters The parameters map controlling the reading. + * @throws IOException If the parameters could not be read. + */ + public static void getParameters(Representation post, Map parameters) + throws IOException { + if (!post.isAvailable()) { + throw new IllegalStateException( + "The Web form cannot be parsed as no fresh content is available. If this entity has been already read once, caching of the entity is required"); + } + + new FormReader(post).readParameters(parameters); + } + + /** + * Reads the parameters whose name is a key in the given map.
+ * If a matching parameter is found, its value is put in the map.
+ * If multiple values are found, a list is created and set in the map. + * + * @param parametersString The query string. + * @param parameters The parameters map controlling the reading. + * @param characterSet The supported character encoding. + * @param separator The separator character to append between parameters. + * @param decode Indicates if the parameters should be decoded using the given character set. + * @throws IOException If the parameters could not be read. + */ + public static void getParameters( + String parametersString, + Map parameters, + CharacterSet characterSet, + char separator, + boolean decode) + throws IOException { + new FormReader(parametersString, characterSet, separator, decode) + .readParameters(parameters); + } + + /** + * Indicates if the searched parameter is specified in the given media range. + * + * @param searchedParam The searched parameter. + * @param mediaRange The media range to inspect. + * @return True if the searched parameter is specified in the given media range. + */ + public static boolean isParameterFound(Parameter searchedParam, MediaType mediaRange) { + boolean result = false; + + for (Iterator iter = mediaRange.getParameters().iterator(); + !result && iter.hasNext(); ) { + result = searchedParam.equals(iter.next()); + } + + return result; + } + + /** + * Parses a post into a given form. + * + * @param form The target form. + * @param post The posted form. + * @param decode Indicates if the parameters should be decoded. + */ + public static void parse(Form form, Representation post, boolean decode) { + if (post != null) { + if (post.isAvailable()) { + FormReader fr = null; + + try { + fr = new FormReader(post, decode); + } catch (IOException ioe) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to create a form reader. Parsing aborted.", + ioe); + } + + if (fr != null) { + fr.addParameters(form); + } + } else { + Context.getCurrentLogger() + .log( + Level.FINE, + "The form wasn't changed as the given representation isn't available."); + } + } + } + + /** + * Parses a parameters string into a given form. + * + * @param form The target form. + * @param parametersString The parameters string. + * @param characterSet The supported character encoding. + * @param decode Indicates if the parameters should be decoded using the given character set. + * @param separator The separator character to append between parameters. + */ + public static void parse( + Form form, + String parametersString, + CharacterSet characterSet, + boolean decode, + char separator) { + if ((parametersString != null) && !parametersString.isEmpty()) { + FormReader fr = new FormReader(parametersString, characterSet, separator, decode); + fr.addParameters(form); + } + } + + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private FormUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java b/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java index 9e077da20e..6bed2cf62a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java @@ -1,105 +1,102 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.util.Date; /** * Class acting as an immutable date class based on the {@link Date} class. - * - * Throws {@link UnsupportedOperationException} when mutable methods are - * invoked. - * + * + *

Throws {@link UnsupportedOperationException} when mutable methods are invoked. + * * @author Piyush Purang (ppurang@gmail.com) * @see java.util.Date */ public final class ImmutableDate extends Date { - private static final long serialVersionUID = -5946186780670229206L; - - /** - * Private constructor. A factory method is provided. - * - * @param date date to be made immutable - */ - public ImmutableDate(Date date) { - super(date.getTime()); - } + private static final long serialVersionUID = -5946186780670229206L; - /** {@inheritDoc} */ - @Override - public Object clone() throws UnsupportedOperationException { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** + * Private constructor. A factory method is provided. + * + * @param date date to be made immutable + */ + public ImmutableDate(Date date) { + super(date.getTime()); + } - /** - * As an ImmutableDate is immutable, this method throws an - * UnsupportedOperationException exception. - */ - @Override - public void setDate(int arg0) { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** {@inheritDoc} */ + @Override + public Object clone() throws UnsupportedOperationException { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } - /** - * As an ImmutableDate is immutable, this method throws an - * UnsupportedOperationException exception. - */ - @Override - public void setHours(int arg0) { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** + * As an ImmutableDate is immutable, this method throws an UnsupportedOperationException + * exception. + */ + @Override + public void setDate(int arg0) { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } - /** - * As an ImmutableDate is immutable, this method throws an - * UnsupportedOperationException exception. - */ - @Override - public void setMinutes(int arg0) { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** + * As an ImmutableDate is immutable, this method throws an UnsupportedOperationException + * exception. + */ + @Override + public void setHours(int arg0) { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } - /** - * As an ImmutableDate is immutable, this method throws an - * UnsupportedOperationException exception. - */ - @Override - public void setMonth(int arg0) { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** + * As an ImmutableDate is immutable, this method throws an UnsupportedOperationException + * exception. + */ + @Override + public void setMinutes(int arg0) { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } - /** - * As an ImmutableDate is immutable, this method throws an - * UnsupportedOperationException exception. - */ - @Override - public void setSeconds(int arg0) { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** + * As an ImmutableDate is immutable, this method throws an UnsupportedOperationException + * exception. + */ + @Override + public void setMonth(int arg0) { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } - /** - * As an ImmutableDate is immutable, this method throws an - * UnsupportedOperationException exception. - */ - @Override - public void setTime(long arg0) { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** + * As an ImmutableDate is immutable, this method throws an UnsupportedOperationException + * exception. + */ + @Override + public void setSeconds(int arg0) { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } - /** - * As an ImmutableDate is immutable, this method throws an - * UnsupportedOperationException exception. - */ - @Override - public void setYear(int arg0) { - throw new UnsupportedOperationException("ImmutableDate is immutable"); - } + /** + * As an ImmutableDate is immutable, this method throws an UnsupportedOperationException + * exception. + */ + @Override + public void setTime(long arg0) { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } + /** + * As an ImmutableDate is immutable, this method throws an UnsupportedOperationException + * exception. + */ + @Override + public void setYear(int arg0) { + throw new UnsupportedOperationException("ImmutableDate is immutable"); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java b/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java index c17a6d6675..0392a8c527 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java @@ -1,458 +1,448 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; -import java.text.*; -import java.util.*; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.FieldPosition; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.SimpleTimeZone; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * This class handles Internet date/time strings in accordance with RFC 3339. It - * provides static methods to convert from various Java constructs (long, Date, - * and Calendar) to RFC 3339 format strings and to parse these strings back into - * the same Java constructs. - *

- * In addition to the static utility methods, this class also wraps a Calendar - * object allowing this class to be used as a value object in place of a Java - * construct. - *

- * Strings are parsed in accordance with the RFC 3339 format: - * + * This class handles Internet date/time strings in accordance with RFC 3339. It provides static + * methods to convert from various Java constructs (long, Date, and Calendar) to RFC 3339 format + * strings and to parse these strings back into the same Java constructs. + * + *

In addition to the static utility methods, this class also wraps a Calendar object allowing + * this class to be used as a value object in place of a Java construct. + * + *

Strings are parsed in accordance with the RFC 3339 format: + * *

  * YYYY-MM-DD(T|t|\s)hh:mm:ss[.ddd][tzd]
  * 
- * - * The tzd represents the time zone designator and is either an - * upper or lower case 'Z' indicating UTC or a signed hh:mm offset. - * + * + * The tzd represents the time zone designator and is either an upper or lower case 'Z' + * indicating UTC or a signed hh:mm offset. + * * @author Frank Hellwig (frank@hellwig.org) */ public class InternetDateFormat extends DateFormat { - private static volatile DecimalFormat df2 = new DecimalFormat("00"); - - private static volatile DecimalFormat df4 = new DecimalFormat("0000"); - - /** The Regex pattern to match. */ - private static volatile Pattern pattern; - - private static final long serialVersionUID = 1L; - - /** - * A time zone with zero offset and no DST. - */ - public static final TimeZone UTC = new SimpleTimeZone(0, "Z"); - - static { - String reDate = "(\\d{4})-(\\d{2})-(\\d{2})"; - String reTime = "(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?"; - String reZone = "(?:([zZ])|(?:(\\+|\\-)(\\d{2}):(\\d{2})))"; - String re = reDate + "[tT\\s]" + reTime + reZone; - pattern = Pattern.compile(re); - } - - /** - * Returns the current date and time as an RFC 3339 date/time string using the - * UTC (Z) time zone. - * - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public static String now() { - return now(UTC); - } - - /** - * Returns the current date and time as an RFC 3339 date/time string using the - * specified time zone. - * - * @param zone the time zone to use - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public static String now(TimeZone zone) { - return toString(System.currentTimeMillis(), zone); - } - - /** - * Our private parse utility that parses the string, clears the calendar, and - * then sets the fields. - * - * @param s the string to parse - * @param cal the calendar object to populate - * @throws IllegalArgumentException if the string is not a valid RFC 3339 - * date/time string - */ - private static void parse(String s, Calendar cal) { - Matcher m = pattern.matcher(s); - if (!m.matches()) { - throw new IllegalArgumentException("Invalid date/time: " + s); - } - cal.clear(); - cal.set(Calendar.YEAR, Integer.parseInt(m.group(1))); - cal.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1); - cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(3))); - cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4))); - cal.set(Calendar.MINUTE, Integer.parseInt(m.group(5))); - cal.set(Calendar.SECOND, Integer.parseInt(m.group(6))); - if (m.group(7) != null) { - float fraction = Float.parseFloat(m.group(7)); - cal.set(Calendar.MILLISECOND, (int) (fraction * 1000F)); - } - if (m.group(8) != null) { - cal.setTimeZone(new SimpleTimeZone(0, "Z")); - } else { - int sign = m.group(9).equals("-") ? -1 : 1; - int tzhour; - tzhour = Integer.parseInt(m.group(10)); - int tzminute = Integer.parseInt(m.group(11)); - int offset = sign * ((tzhour * 60) + tzminute); - String id = Integer.toString(offset); - cal.setTimeZone(new SimpleTimeZone(offset * 60000, id)); - } - } - - /** - * Parses an RFC 3339 date/time string to a Calendar object. - * - * @param s the string to parse - * @return the Calendar object - * @throws IllegalArgumentException if the string is not a valid RFC 3339 - * date/time string - */ - public static Calendar parseCalendar(String s) { - Calendar cal = new GregorianCalendar(); - parse(s, cal); - return cal; - } - - /** - * Parses an RFC 3339 date/time string to a Date object. - * - * @param s the string to parse - * @return the Date object - * @throws IllegalArgumentException if the string is not a valid RFC 3339 - * date/time string - */ - public static Date parseDate(String s) { - Calendar cal = new GregorianCalendar(); - parse(s, cal); - return cal.getTime(); - } - - /** - * Parses an RFC 3339 date/time string to a millisecond time value. - * - * @param s the string to parse - * @return the millisecond time value - * @throws IllegalArgumentException if the string is not a valid RFC 3339 - * date/time string - */ - public static long parseTime(String s) { - Calendar cal = new GregorianCalendar(); - parse(s, cal); - return cal.getTimeInMillis(); - } - - /** - * Converts the specified Calendar object to an RFC 3339 date/time string. - * Unlike the toString methods for Date and long, no additional variant of this - * method taking a time zone is provided since the time zone is built into the - * Calendar object. - * - * @param cal the Calendar object - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public static String toString(Calendar cal) { - StringBuilder buf = new StringBuilder(); - buf.append(df4.format(cal.get(Calendar.YEAR))); - buf.append("-"); - buf.append(df2.format(cal.get(Calendar.MONTH) + 1L)); - buf.append("-"); - buf.append(df2.format(cal.get(Calendar.DAY_OF_MONTH))); - buf.append("T"); - buf.append(df2.format(cal.get(Calendar.HOUR_OF_DAY))); - buf.append(":"); - buf.append(df2.format(cal.get(Calendar.MINUTE))); - buf.append(":"); - buf.append(df2.format(cal.get(Calendar.SECOND))); - - int ms = cal.get(Calendar.MILLISECOND); - if (ms != 0) { - buf.append(".").append((int) (ms / 10F)); - } - - int tzminute = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 60000; - if (tzminute == 0) { - buf.append("Z"); - } else { - if (tzminute < 0) { - tzminute = -tzminute; - buf.append("-"); - } else { - buf.append("+"); - } - int tzhour = tzminute / 60; - tzminute -= tzhour * 60; - buf.append(df2.format(tzhour)); - buf.append(":"); - buf.append(df2.format(tzminute)); - } - return buf.toString(); - } - - /** - * Converts the specified Date object to an RFC 3339 date/time string using the - * UTC (Z) time zone. - * - * @param date the Date object - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public static String toString(Date date) { - return toString(date, UTC); - } - - /** - * Converts the specified Date object to an RFC 3339 date/time string using the - * specified time zone. - * - * @param date the Date object - * @param zone the time zone to use - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public static String toString(Date date, TimeZone zone) { - InternetDateFormat dt = new InternetDateFormat(date, zone); - return dt.toString(); - } - - /** - * Converts the specified millisecond time value to an RFC 3339 date/time string - * using the UTC (Z) time zone. - * - * @param time the millisecond time value - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public static String toString(long time) { - return toString(time, UTC); - } - - /** - * Converts the specified millisecond time value to an RFC 3339 date/time string - * using the specified time zone. - * - * @param time the millisecond time value - * @param zone the time zone to use - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public static String toString(long time, TimeZone zone) { - InternetDateFormat dt = new InternetDateFormat(time, zone); - return dt.toString(); - } - - /** - * Creates a new InternetDateFormat object from the specified Date object using - * the UTC (Z) time zone. - * - * @param date the Date object - * @return the InternetDateFormat object - */ - public static InternetDateFormat valueOf(Date date) { - return new InternetDateFormat(date); - } - - /** - * Creates a new InternetDateFormat object from the specified Date object using - * the specified time zone. - * - * @param date the Date object - * @param zone the time zone to use - * @return the InternetDateFormat object - */ - public static InternetDateFormat valueOf(Date date, TimeZone zone) { - return new InternetDateFormat(date, zone); - } - - /** - * Creates a new InternetDateFormat object from the specified millisecond time - * value using the UTC (Z) time zone. - * - * @param time the millisecond time value - * @return the InternetDateFormat object - */ - public static InternetDateFormat valueOf(long time) { - return new InternetDateFormat(time); - } - - /** - * Creates a new InternetDateFormat object from the specified millisecond time - * value using the specified time zone. - * - * @param time the millisecond time value - * @param zone the time zone to use - * @return the InternetDateFormat object - */ - public static InternetDateFormat valueOf(long time, TimeZone zone) { - return new InternetDateFormat(time, zone); - } - - /** - * Creates a new InternetDateFormat object by parsing an RFC 3339 date/time - * string. - * - * @param s the string to parse - * @return the InternetDateFormat object - * @throws IllegalArgumentException if the string is not a valid RFC 3339 - * date/time string - */ - public static InternetDateFormat valueOf(String s) { - return new InternetDateFormat(s); - } - - /** - * The Calendar object that allows this class to act as a value holder. - */ - private Calendar cal; - - /** - * Creates a new InternetDateFormat object set to the current time using the UTC - * (Z) time zone. - */ - public InternetDateFormat() { - this(UTC); - } - - /** - * Creates a new InternetDateFormat object initialized from a Calendar object. - * The specified calendar object is cloned thereby isolating this - * InternetDateFormat object from any changes made to the specified calendar - * object after calling this constructor. - * - * @param cal the Calendar object - */ - public InternetDateFormat(Calendar cal) { - this.cal = (Calendar) cal.clone(); - } - - /** - * Creates a new InternetDateFormat object initialized from a Date object using - * the UTC (Z) time zone. - * - * @param date the Date object - */ - public InternetDateFormat(Date date) { - this(date, UTC); - } - - /** - * Creates a new InternetDateFormat object initialized from a Date object using - * the specified time zone. - * - * @param date the Date object - * @param zone the time zone to use - */ - public InternetDateFormat(Date date, TimeZone zone) { - cal = new GregorianCalendar(zone); - cal.setTime(date); - } - - /** - * Creates a new InternetDateFormat object initialized from a millisecond time - * value using the UTC (Z) time zone. - * - * @param time the millisecond time value - */ - public InternetDateFormat(long time) { - this(time, UTC); - } - - /** - * Creates a new InternetDateFormat object initialized from a millisecond time - * value using the specified time zone. - * - * @param time the millisecond time value - * @param zone the time zone to use - */ - public InternetDateFormat(long time, TimeZone zone) { - cal = new GregorianCalendar(zone); - cal.setTimeInMillis(time); - } - - /** - * Creates a new InternetDateFormat object by parsing an RFC 3339 date/time - * string. - * - * @param s the string to parse - * @throws IllegalArgumentException if the string is not a valid RFC 3339 - * date/time string - */ - public InternetDateFormat(String s) { - cal = parseCalendar(s); - } - - /** - * Creates a new InternetDateFormat object set to the current time using the - * specified time zone. - * - * @param zone the time zone to use - */ - public InternetDateFormat(TimeZone zone) { - cal = new GregorianCalendar(zone); - } - - @Override - public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { - return toAppendTo.append(valueOf(date)); - } - - /** - * Gets the Calendar object wrapped by this InternetDateFormat object. - * - * @return the cloned Calendar object - */ - public Calendar getCalendar() { - return (Calendar) cal.clone(); - } - - /** - * Gets the value of this InternetDateFormat object as a Date object. - * - * @return the Date object - */ - public Date getDate() { - return cal.getTime(); - } - - /** - * Gets the value of this InternetDateFormat object as millisecond time value. - * - * @return the millisecond time value - */ - public long getTime() { - return cal.getTimeInMillis(); - } - - @Override - public Date parse(String source) throws ParseException { - return parse(source, (ParsePosition) null); - } - - @Override - public Date parse(String source, ParsePosition pos) { - return parseDate(source); - } - - /** - * Converts this InternetDateFormat object to an RFC 3339 date/time string. - * - * @return an RFC 3339 date/time string (does not include milliseconds) - */ - public String toString() { - return toString(cal); - } + private static volatile DecimalFormat df2 = new DecimalFormat("00"); + + private static volatile DecimalFormat df4 = new DecimalFormat("0000"); + + /** The Regex pattern to match. */ + private static volatile Pattern pattern; + + private static final long serialVersionUID = 1L; + + /** A time zone with zero offset and no DST. */ + public static final TimeZone UTC = new SimpleTimeZone(0, "Z"); + + static { + String reDate = "(\\d{4})-(\\d{2})-(\\d{2})"; + String reTime = "(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?"; + String reZone = "(?:([zZ])|(?:(\\+|\\-)(\\d{2}):(\\d{2})))"; + String re = reDate + "[tT\\s]" + reTime + reZone; + pattern = Pattern.compile(re); + } + + /** + * Returns the current date and time as an RFC 3339 date/time string using the UTC (Z) time + * zone. + * + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public static String now() { + return now(UTC); + } + + /** + * Returns the current date and time as an RFC 3339 date/time string using the specified time + * zone. + * + * @param zone the time zone to use + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public static String now(TimeZone zone) { + return toString(System.currentTimeMillis(), zone); + } + + /** + * Our private parse utility that parses the string, clears the calendar, and then sets the + * fields. + * + * @param s the string to parse + * @param cal the calendar object to populate + * @throws IllegalArgumentException if the string is not a valid RFC 3339 date/time string + */ + private static void parse(String s, Calendar cal) { + Matcher m = pattern.matcher(s); + if (!m.matches()) { + throw new IllegalArgumentException("Invalid date/time: " + s); + } + cal.clear(); + cal.set(Calendar.YEAR, Integer.parseInt(m.group(1))); + cal.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1); + cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(3))); + cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4))); + cal.set(Calendar.MINUTE, Integer.parseInt(m.group(5))); + cal.set(Calendar.SECOND, Integer.parseInt(m.group(6))); + if (m.group(7) != null) { + float fraction = Float.parseFloat(m.group(7)); + cal.set(Calendar.MILLISECOND, (int) (fraction * 1000F)); + } + if (m.group(8) != null) { + cal.setTimeZone(new SimpleTimeZone(0, "Z")); + } else { + int sign = m.group(9).equals("-") ? -1 : 1; + int tzhour; + tzhour = Integer.parseInt(m.group(10)); + int tzminute = Integer.parseInt(m.group(11)); + int offset = sign * ((tzhour * 60) + tzminute); + String id = Integer.toString(offset); + cal.setTimeZone(new SimpleTimeZone(offset * 60000, id)); + } + } + + /** + * Parses an RFC 3339 date/time string to a Calendar object. + * + * @param s the string to parse + * @return the Calendar object + * @throws IllegalArgumentException if the string is not a valid RFC 3339 date/time string + */ + public static Calendar parseCalendar(String s) { + Calendar cal = new GregorianCalendar(); + parse(s, cal); + return cal; + } + + /** + * Parses an RFC 3339 date/time string to a Date object. + * + * @param s the string to parse + * @return the Date object + * @throws IllegalArgumentException if the string is not a valid RFC 3339 date/time string + */ + public static Date parseDate(String s) { + Calendar cal = new GregorianCalendar(); + parse(s, cal); + return cal.getTime(); + } + + /** + * Parses an RFC 3339 date/time string to a millisecond time value. + * + * @param s the string to parse + * @return the millisecond time value + * @throws IllegalArgumentException if the string is not a valid RFC 3339 date/time string + */ + public static long parseTime(String s) { + Calendar cal = new GregorianCalendar(); + parse(s, cal); + return cal.getTimeInMillis(); + } + + /** + * Converts the specified Calendar object to an RFC 3339 date/time string. Unlike the toString + * methods for Date and long, no additional variant of this method taking a time zone is + * provided since the time zone is built into the Calendar object. + * + * @param cal the Calendar object + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public static String toString(Calendar cal) { + StringBuilder buf = new StringBuilder(); + buf.append(df4.format(cal.get(Calendar.YEAR))); + buf.append("-"); + buf.append(df2.format(cal.get(Calendar.MONTH) + 1L)); + buf.append("-"); + buf.append(df2.format(cal.get(Calendar.DAY_OF_MONTH))); + buf.append("T"); + buf.append(df2.format(cal.get(Calendar.HOUR_OF_DAY))); + buf.append(":"); + buf.append(df2.format(cal.get(Calendar.MINUTE))); + buf.append(":"); + buf.append(df2.format(cal.get(Calendar.SECOND))); + + int ms = cal.get(Calendar.MILLISECOND); + if (ms != 0) { + buf.append(".").append((int) (ms / 10F)); + } + + int tzminute = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 60000; + if (tzminute == 0) { + buf.append("Z"); + } else { + if (tzminute < 0) { + tzminute = -tzminute; + buf.append("-"); + } else { + buf.append("+"); + } + int tzhour = tzminute / 60; + tzminute -= tzhour * 60; + buf.append(df2.format(tzhour)); + buf.append(":"); + buf.append(df2.format(tzminute)); + } + return buf.toString(); + } + + /** + * Converts the specified Date object to an RFC 3339 date/time string using the UTC (Z) time + * zone. + * + * @param date the Date object + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public static String toString(Date date) { + return toString(date, UTC); + } + + /** + * Converts the specified Date object to an RFC 3339 date/time string using the specified time + * zone. + * + * @param date the Date object + * @param zone the time zone to use + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public static String toString(Date date, TimeZone zone) { + InternetDateFormat dt = new InternetDateFormat(date, zone); + return dt.toString(); + } + + /** + * Converts the specified millisecond time value to an RFC 3339 date/time string using the UTC + * (Z) time zone. + * + * @param time the millisecond time value + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public static String toString(long time) { + return toString(time, UTC); + } + + /** + * Converts the specified millisecond time value to an RFC 3339 date/time string using the + * specified time zone. + * + * @param time the millisecond time value + * @param zone the time zone to use + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public static String toString(long time, TimeZone zone) { + InternetDateFormat dt = new InternetDateFormat(time, zone); + return dt.toString(); + } + + /** + * Creates a new InternetDateFormat object from the specified Date object using the UTC (Z) time + * zone. + * + * @param date the Date object + * @return the InternetDateFormat object + */ + public static InternetDateFormat valueOf(Date date) { + return new InternetDateFormat(date); + } + + /** + * Creates a new InternetDateFormat object from the specified Date object using the specified + * time zone. + * + * @param date the Date object + * @param zone the time zone to use + * @return the InternetDateFormat object + */ + public static InternetDateFormat valueOf(Date date, TimeZone zone) { + return new InternetDateFormat(date, zone); + } + + /** + * Creates a new InternetDateFormat object from the specified millisecond time value using the + * UTC (Z) time zone. + * + * @param time the millisecond time value + * @return the InternetDateFormat object + */ + public static InternetDateFormat valueOf(long time) { + return new InternetDateFormat(time); + } + + /** + * Creates a new InternetDateFormat object from the specified millisecond time value using the + * specified time zone. + * + * @param time the millisecond time value + * @param zone the time zone to use + * @return the InternetDateFormat object + */ + public static InternetDateFormat valueOf(long time, TimeZone zone) { + return new InternetDateFormat(time, zone); + } + + /** + * Creates a new InternetDateFormat object by parsing an RFC 3339 date/time string. + * + * @param s the string to parse + * @return the InternetDateFormat object + * @throws IllegalArgumentException if the string is not a valid RFC 3339 date/time string + */ + public static InternetDateFormat valueOf(String s) { + return new InternetDateFormat(s); + } + + /** The Calendar object that allows this class to act as a value holder. */ + private Calendar cal; + + /** + * Creates a new InternetDateFormat object set to the current time using the UTC (Z) time zone. + */ + public InternetDateFormat() { + this(UTC); + } + + /** + * Creates a new InternetDateFormat object initialized from a Calendar object. The specified + * calendar object is cloned thereby isolating this InternetDateFormat object from any changes + * made to the specified calendar object after calling this constructor. + * + * @param cal the Calendar object + */ + public InternetDateFormat(Calendar cal) { + this.cal = (Calendar) cal.clone(); + } + + /** + * Creates a new InternetDateFormat object initialized from a Date object using the UTC (Z) time + * zone. + * + * @param date the Date object + */ + public InternetDateFormat(Date date) { + this(date, UTC); + } + + /** + * Creates a new InternetDateFormat object initialized from a Date object using the specified + * time zone. + * + * @param date the Date object + * @param zone the time zone to use + */ + public InternetDateFormat(Date date, TimeZone zone) { + cal = new GregorianCalendar(zone); + cal.setTime(date); + } + + /** + * Creates a new InternetDateFormat object initialized from a millisecond time value using the + * UTC (Z) time zone. + * + * @param time the millisecond time value + */ + public InternetDateFormat(long time) { + this(time, UTC); + } + + /** + * Creates a new InternetDateFormat object initialized from a millisecond time value using the + * specified time zone. + * + * @param time the millisecond time value + * @param zone the time zone to use + */ + public InternetDateFormat(long time, TimeZone zone) { + cal = new GregorianCalendar(zone); + cal.setTimeInMillis(time); + } + + /** + * Creates a new InternetDateFormat object by parsing an RFC 3339 date/time string. + * + * @param s the string to parse + * @throws IllegalArgumentException if the string is not a valid RFC 3339 date/time string + */ + public InternetDateFormat(String s) { + cal = parseCalendar(s); + } + + /** + * Creates a new InternetDateFormat object set to the current time using the specified time + * zone. + * + * @param zone the time zone to use + */ + public InternetDateFormat(TimeZone zone) { + cal = new GregorianCalendar(zone); + } + + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { + return toAppendTo.append(valueOf(date)); + } + + /** + * Gets the Calendar object wrapped by this InternetDateFormat object. + * + * @return the cloned Calendar object + */ + public Calendar getCalendar() { + return (Calendar) cal.clone(); + } + + /** + * Gets the value of this InternetDateFormat object as a Date object. + * + * @return the Date object + */ + public Date getDate() { + return cal.getTime(); + } + + /** + * Gets the value of this InternetDateFormat object as millisecond time value. + * + * @return the millisecond time value + */ + public long getTime() { + return cal.getTimeInMillis(); + } + + @Override + public Date parse(String source) throws ParseException { + return parse(source, (ParsePosition) null); + } + + @Override + public Date parse(String source, ParsePosition pos) { + return parseDate(source); + } + + /** + * Converts this InternetDateFormat object to an RFC 3339 date/time string. + * + * @return an RFC 3339 date/time string (does not include milliseconds) + */ + public String toString() { + return toString(cal); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java index 7715de55cd..a263077c2b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.util.ArrayList; @@ -14,34 +13,32 @@ /** * Emulate List functions missing from GWT port of List - * + * * @author Rob Heittman */ public class ListUtils { - /** - * Unlike List.subList(), which returns a live view of a set of List elements, - * this method returns a new copy of the list. List.subList() is not available - * in GWT 1.5 and was removed on purpose. - * - * @param list The source List - * @param fromIndex Starting index in the source List - * @param toIndex Ending index in the source List - * @throws IndexOutOfBoundsException Call exceeds the bounds of the source List - * @throws IllegalArgumentException fromIndex and toIndex are not in sequence - * @return a copy of the selected range - */ - public static List copySubList(List list, int fromIndex, int toIndex) { - if (fromIndex < 0) - throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); - if (toIndex > list.size()) - throw new IndexOutOfBoundsException("toIndex = " + toIndex); - if (fromIndex > toIndex) - throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); - ArrayList subList = new ArrayList(); - for (int i = fromIndex; i <= toIndex; i++) { - subList.add(list.get(i)); - } - return subList; - } - + /** + * Unlike List.subList(), which returns a live view of a set of List elements, this method + * returns a new copy of the list. List.subList() is not available in GWT 1.5 and was removed on + * purpose. + * + * @param list The source List + * @param fromIndex Starting index in the source List + * @param toIndex Ending index in the source List + * @throws IndexOutOfBoundsException Call exceeds the bounds of the source List + * @throws IllegalArgumentException fromIndex and toIndex are not in sequence + * @return a copy of the selected range + */ + public static List copySubList(List list, int fromIndex, int toIndex) { + if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); + if (toIndex > list.size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex); + if (fromIndex > toIndex) + throw new IllegalArgumentException( + "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); + ArrayList subList = new ArrayList(); + for (int i = fromIndex; i <= toIndex; i++) { + subList.add(list.get(i)); + } + return subList; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java b/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java index 03f065a281..4ef8d1a091 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java @@ -1,39 +1,37 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; -import org.restlet.util.Resolver; - import java.util.Map; +import org.restlet.util.Resolver; /** * Resolves variable values based on a map. - * + * * @author Jerome Louvel */ public class MapResolver extends Resolver { - /** The variables to use when formatting. */ - private final Map map; + /** The variables to use when formatting. */ + private final Map map; - /** - * Constructor. - * - * @param map The variables to use when formatting. - */ - public MapResolver(Map map) { - this.map = map; - } + /** + * Constructor. + * + * @param map The variables to use when formatting. + */ + public MapResolver(Map map) { + this.map = map; + } - @Override - public Object resolve(String variableName) { - return this.map.get(variableName); - } + @Override + public Object resolve(String variableName) { + return this.map.get(variableName); + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java index 6e5d09cfc9..25d76a7b4f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.util.Queue; @@ -14,113 +13,104 @@ /** * Generic object pool. - * + * * @author Jerome Louvel - * * @param */ public abstract class Pool { - /** Store of reusable objects. */ - private final Queue store; - - /** - * Default constructor. - */ - public Pool() { - this.store = createStore(); - } - - /** - * Constructor. Pre-creates the minimum number of objects if needed using the - * {@link #preCreate(int)} method. - * - * @param initialSize The initial number of objects in the pool. - */ - public Pool(int initialSize) { - this(); - preCreate(initialSize); - } - - /** - * Checks in an object into the pool. - * - * @param object The object to check in. - */ - public void checkin(T object) { - if (object != null) { - clear(object); - this.store.offer(object); - } - } - - /** - * Checks out an object from the pool. Creates a new one if the pool is empty. - * - * @return An object from the pool. - */ - public T checkout() { - T result; - - if ((result = this.store.poll()) == null) { - result = createObject(); - } - - return result; - } - - /** - * Clears the store of reusable objects. - */ - public void clear() { - getStore().clear(); - } - - /** - * Clears the given object when it is checked in the pool. Does nothing by - * default. - * - * @param object The object to clear. - */ - protected void clear(T object) { - - } - - /** - * Creates a new reusable object. - * - * @return A new reusable object. - */ - protected abstract T createObject(); - - /** - * Creates the store of reusable objects. - * - * @return The store of reusable objects. - */ - protected Queue createStore() { - return new ConcurrentLinkedQueue(); - } - - /** - * Returns the store containing the reusable objects. - * - * @return The store containing the reusable objects. - */ - protected Queue getStore() { - return store; - } - - /** - * Pre-creates the initial objects using the {@link #createObject()} method and - * check them in the pool using the {@link #checkin(Object)} method. - * - * @param initialSize The initial number of objects. - */ - public void preCreate(int initialSize) { - for (int i = 0; i < initialSize; i++) { - checkin(createObject()); - } - } - + /** Store of reusable objects. */ + private final Queue store; + + /** Default constructor. */ + public Pool() { + this.store = createStore(); + } + + /** + * Constructor. Pre-creates the minimum number of objects if needed using the {@link + * #preCreate(int)} method. + * + * @param initialSize The initial number of objects in the pool. + */ + public Pool(int initialSize) { + this(); + preCreate(initialSize); + } + + /** + * Checks in an object into the pool. + * + * @param object The object to check in. + */ + public void checkin(T object) { + if (object != null) { + clear(object); + this.store.offer(object); + } + } + + /** + * Checks out an object from the pool. Creates a new one if the pool is empty. + * + * @return An object from the pool. + */ + public T checkout() { + T result; + + if ((result = this.store.poll()) == null) { + result = createObject(); + } + + return result; + } + + /** Clears the store of reusable objects. */ + public void clear() { + getStore().clear(); + } + + /** + * Clears the given object when it is checked in the pool. Does nothing by default. + * + * @param object The object to clear. + */ + protected void clear(T object) {} + + /** + * Creates a new reusable object. + * + * @return A new reusable object. + */ + protected abstract T createObject(); + + /** + * Creates the store of reusable objects. + * + * @return The store of reusable objects. + */ + protected Queue createStore() { + return new ConcurrentLinkedQueue(); + } + + /** + * Returns the store containing the reusable objects. + * + * @return The store containing the reusable objects. + */ + protected Queue getStore() { + return store; + } + + /** + * Pre-creates the initial objects using the {@link #createObject()} method and check them in + * the pool using the {@link #checkin(Object)} method. + * + * @param initialSize The initial number of objects. + */ + public void preCreate(int initialSize) { + for (int i = 0; i < initialSize; i++) { + checkin(createObject()); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java index 899f5086e8..b75bd07f62 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import org.restlet.Request; @@ -18,89 +17,84 @@ /** * Utilities related to URI references. - * + * * @author Jerome Louvel */ public class ReferenceUtils { - /** - * Returns the request URI. - * - * @param resourceRef The resource reference. - * @param request The parent request. - * @return The absolute request URI. - */ - public static Reference update(Reference resourceRef, Request request) { - Reference result = resourceRef.isAbsolute() ? resourceRef : resourceRef.getTargetRef(); - - // Optionally update the request before formatting its URI - result = AuthenticatorUtils.updateReference(result, request.getChallengeResponse(), request); - - return result; - } - - /** - * Returns the request URI. - * - * @param resourceRef The resource reference. - * @param isProxied Indicates if the request goes through a proxy and requires - * an absolute URI. - * @param request The parent request. - * @return The absolute request URI. - */ - public static String format(Reference resourceRef, boolean isProxied, Request request) { - String result = null; - Reference requestRef = update(resourceRef, request); - - if (isProxied) { - result = requestRef.getIdentifier(); - } else { - if (requestRef.hasQuery()) { - result = requestRef.getPath() + "?" + requestRef.getQuery(); - } else { - result = requestRef.getPath(); - } - - if ((result == null) || (result.isEmpty())) { - result = "/"; - } - } - - return result; - } - - /** - * Returns the original reference especially by detecting potential proxy - * forwardings. - * - * @param resourceRef The request's resource reference. - * @param headers The set of request's headers. - * @return The original reference. - */ - public static Reference getOriginalRef(Reference resourceRef, Series

headers) { - Reference originalRef = resourceRef.getTargetRef(); - - if (headers == null) { - return originalRef; - } - - String value = headers.getFirstValue(HeaderConstants.HEADER_X_FORWARDED_PORT); - if (value != null) { - originalRef.setHostPort(Integer.parseInt(value)); - } - - value = headers.getFirstValue(HeaderConstants.HEADER_X_FORWARDED_PROTO); - if (value != null) { - originalRef.setScheme(value); - } - - return originalRef; - } - - /** - * Constructor. - */ - private ReferenceUtils() { - } - + /** + * Returns the request URI. + * + * @param resourceRef The resource reference. + * @param request The parent request. + * @return The absolute request URI. + */ + public static Reference update(Reference resourceRef, Request request) { + Reference result = resourceRef.isAbsolute() ? resourceRef : resourceRef.getTargetRef(); + + // Optionally update the request before formatting its URI + result = + AuthenticatorUtils.updateReference(result, request.getChallengeResponse(), request); + + return result; + } + + /** + * Returns the request URI. + * + * @param resourceRef The resource reference. + * @param isProxied Indicates if the request goes through a proxy and requires an absolute URI. + * @param request The parent request. + * @return The absolute request URI. + */ + public static String format(Reference resourceRef, boolean isProxied, Request request) { + String result = null; + Reference requestRef = update(resourceRef, request); + + if (isProxied) { + result = requestRef.getIdentifier(); + } else { + if (requestRef.hasQuery()) { + result = requestRef.getPath() + "?" + requestRef.getQuery(); + } else { + result = requestRef.getPath(); + } + + if ((result == null) || (result.isEmpty())) { + result = "/"; + } + } + + return result; + } + + /** + * Returns the original reference, especially by detecting potential proxy forwarding. + * + * @param resourceRef The request's resource reference. + * @param headers The set of request's headers. + * @return The original reference. + */ + public static Reference getOriginalRef(Reference resourceRef, Series
headers) { + Reference originalRef = resourceRef.getTargetRef(); + + if (headers == null) { + return originalRef; + } + + String value = headers.getFirstValue(HeaderConstants.HEADER_X_FORWARDED_PORT); + if (value != null) { + originalRef.setHostPort(Integer.parseInt(value)); + } + + value = headers.getFirstValue(HeaderConstants.HEADER_X_FORWARDED_PROTO); + if (value != null) { + originalRef.setScheme(value); + } + + return originalRef; + } + + /** Constructor. */ + private ReferenceUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java index e23c9610da..5774626f59 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.util.Collections; @@ -15,22 +14,21 @@ /** * Utilities for manipulation of {@link Set}. - * + * * @author Manuel Boillod */ public class SetUtils { - /** - * Returns a new {@link java.util.HashSet} with the given elements - * - * @param elements The elements - * @return A new {@link java.util.HashSet} with the given elements - */ - @SafeVarargs - public static Set newHashSet(E... elements) { - HashSet set = new HashSet<>(elements.length); - Collections.addAll(set, elements); - return set; - } - + /** + * Returns a new {@link java.util.HashSet} with the given elements + * + * @param elements The elements + * @return A new {@link java.util.HashSet} with the given elements + */ + @SafeVarargs + public static Set newHashSet(E... elements) { + HashSet set = new HashSet<>(elements.length); + Collections.addAll(set, elements); + return set; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java index d836193698..dad6aee9e5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.nio.charset.StandardCharsets; @@ -22,601 +21,581 @@ */ public class StringUtils { - /** - * Represents an XML or HTML character entity reference. - * - * @author Thierry Boileau - */ - private static class CharacterEntity { - /** - * Character reference. - */ - private String name; - - /** - * Numeric character reference. - */ - private Integer numericValue; + /** + * Represents an XML or HTML character entity reference. + * + * @author Thierry Boileau + */ + private static class CharacterEntity { + /** Character reference. */ + private String name; - /** - * Constructor. - * - * @param numericValue The numeric value of the entity. - * @param name the name of the entity. - */ - public CharacterEntity(Integer numericValue, String name) { - super(); - this.numericValue = numericValue; - this.name = name; - } + /** Numeric character reference. */ + private Integer numericValue; - /** - * Returns the name of the entity. - * - * @return The name of the entity. - */ - public String getName() { - return name; - } + /** + * Constructor. + * + * @param numericValue The numeric value of the entity. + * @param name the name of the entity. + */ + public CharacterEntity(Integer numericValue, String name) { + super(); + this.numericValue = numericValue; + this.name = name; + } - /** - * Returns the numeric value of the entity. - * - * @return The numeric value of the entity. - */ - public Integer getNumericValue() { - return numericValue; - } - } + /** + * Returns the name of the entity. + * + * @return The name of the entity. + */ + public String getName() { + return name; + } - /** - * Stores a list en entities and is able to return one given its numeric value - * or name. - * - * @author Thierry Boileau - */ - private static class CharacterEntitySolver { - /** Map of names of entities according to their numeric value. */ - private final String[] toName; + /** + * Returns the numeric value of the entity. + * + * @return The numeric value of the entity. + */ + public Integer getNumericValue() { + return numericValue; + } + } - /** Map of numeric values of entities according to their name. */ - private final Map toValue; + /** + * Stores a list of entities and is able to return one given its numeric value or name. + * + * @author Thierry Boileau + */ + private static class CharacterEntitySolver { + /** Map of names of entities according to their numeric value. */ + private final String[] toName; - /** - * Constructor. - */ - public CharacterEntitySolver() { - toName = new String[10000]; - toValue = new HashMap<>(); - } + /** Map of numeric values of entities according to their name. */ + private final Map toValue; - /** - * Adds an entity to solve. - * - * @param value The numeric value of the entity. - * @param name The name of the entity. - */ - public void add(Integer value, String name) { - toName[value] = name; - toValue.put(name, value); - } + /** Constructor. */ + public CharacterEntitySolver() { + toName = new String[10000]; + toValue = new HashMap<>(); + } - /** - * Returns the entity name according to its numeric value. - * - * @param value The numeric value of the entity. - * @return The entity name according to its numeric value. - */ - public String getName(int value) { - return toName[value]; - } + /** + * Adds an entity to solve. + * + * @param value The numeric value of the entity. + * @param name The name of the entity. + */ + public void add(Integer value, String name) { + toName[value] = name; + toValue.put(name, value); + } - /** - * Returns the numeric value of an entity according to its name. - * - * @param name The name of the entity. - * @return The numeric value of an entity according to its name. - */ - public Integer getValue(String name) { - return toValue.get(name); - } + /** + * Returns the entity name according to its numeric value. + * + * @param value The numeric value of the entity. + * @return The entity name according to its numeric value. + */ + public String getName(int value) { + return toName[value]; + } - } + /** + * Returns the numeric value of an entity according to its name. + * + * @param name The name of the entity. + * @return The numeric value of an entity according to its name. + */ + public Integer getValue(String name) { + return toValue.get(name); + } + } - /** Entities defined for HTML 4.0. */ - private static CharacterEntitySolver html40Entities; + /** Entities defined for HTML 4.0. */ + private static final CharacterEntitySolver html40Entities; - /** Entities defined in http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent. */ - private static List htmlLat1; + static { + // Basic entities + final List xml10 = new ArrayList<>(); + xml10.add(new CharacterEntity(34, "quot")); + xml10.add(new CharacterEntity(38, "amp")); + xml10.add(new CharacterEntity(62, "gt")); + xml10.add(new CharacterEntity(60, "lt")); - /** Entities defined in http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent. */ - private static List htmlSpecial; + // cf. http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent + final List htmlLat1 = new ArrayList<>(); + htmlLat1.add(new CharacterEntity(160, "nbsp")); + htmlLat1.add(new CharacterEntity(161, "iexcl")); + htmlLat1.add(new CharacterEntity(162, "cent")); + htmlLat1.add(new CharacterEntity(163, "pound")); + htmlLat1.add(new CharacterEntity(164, "curren")); + htmlLat1.add(new CharacterEntity(165, "yen")); + htmlLat1.add(new CharacterEntity(166, "brvbar")); + htmlLat1.add(new CharacterEntity(167, "sect")); + htmlLat1.add(new CharacterEntity(168, "uml")); + htmlLat1.add(new CharacterEntity(169, "copy")); + htmlLat1.add(new CharacterEntity(170, "ordf")); + htmlLat1.add(new CharacterEntity(171, "laquo")); + htmlLat1.add(new CharacterEntity(172, "not")); + htmlLat1.add(new CharacterEntity(173, "shy")); + htmlLat1.add(new CharacterEntity(174, "reg")); + htmlLat1.add(new CharacterEntity(175, "macr")); + htmlLat1.add(new CharacterEntity(176, "deg")); + htmlLat1.add(new CharacterEntity(177, "plusmn")); + htmlLat1.add(new CharacterEntity(178, "sup2")); + htmlLat1.add(new CharacterEntity(179, "sup3")); + htmlLat1.add(new CharacterEntity(180, "acute")); + htmlLat1.add(new CharacterEntity(181, "micro")); + htmlLat1.add(new CharacterEntity(182, "para")); + htmlLat1.add(new CharacterEntity(183, "middot")); + htmlLat1.add(new CharacterEntity(184, "cedil")); + htmlLat1.add(new CharacterEntity(185, "sup1")); + htmlLat1.add(new CharacterEntity(186, "ordm")); + htmlLat1.add(new CharacterEntity(187, "raquo")); + htmlLat1.add(new CharacterEntity(188, "frac14")); + htmlLat1.add(new CharacterEntity(189, "frac12")); + htmlLat1.add(new CharacterEntity(190, "frac34")); + htmlLat1.add(new CharacterEntity(191, "iquest")); + htmlLat1.add(new CharacterEntity(192, "Agrave")); + htmlLat1.add(new CharacterEntity(193, "Aacute")); + htmlLat1.add(new CharacterEntity(194, "Acirc")); + htmlLat1.add(new CharacterEntity(195, "Atilde")); + htmlLat1.add(new CharacterEntity(196, "Auml")); + htmlLat1.add(new CharacterEntity(197, "Aring")); + htmlLat1.add(new CharacterEntity(198, "AElig")); + htmlLat1.add(new CharacterEntity(199, "Ccedil")); + htmlLat1.add(new CharacterEntity(200, "Egrave")); + htmlLat1.add(new CharacterEntity(201, "Eacute")); + htmlLat1.add(new CharacterEntity(202, "Ecirc")); + htmlLat1.add(new CharacterEntity(203, "Euml")); + htmlLat1.add(new CharacterEntity(204, "Igrave")); + htmlLat1.add(new CharacterEntity(205, "Iacute")); + htmlLat1.add(new CharacterEntity(206, "Icirc")); + htmlLat1.add(new CharacterEntity(207, "Iuml")); + htmlLat1.add(new CharacterEntity(208, "ETH")); + htmlLat1.add(new CharacterEntity(209, "Ntilde")); + htmlLat1.add(new CharacterEntity(210, "Ograve")); + htmlLat1.add(new CharacterEntity(211, "Oacute")); + htmlLat1.add(new CharacterEntity(212, "Ocirc")); + htmlLat1.add(new CharacterEntity(213, "Otilde")); + htmlLat1.add(new CharacterEntity(214, "Ouml")); + htmlLat1.add(new CharacterEntity(215, "times")); + htmlLat1.add(new CharacterEntity(216, "Oslash")); + htmlLat1.add(new CharacterEntity(217, "Ugrave")); + htmlLat1.add(new CharacterEntity(218, "Uacute")); + htmlLat1.add(new CharacterEntity(219, "Ucirc")); + htmlLat1.add(new CharacterEntity(220, "Uuml")); + htmlLat1.add(new CharacterEntity(221, "Yacute")); + htmlLat1.add(new CharacterEntity(222, "THORN")); + htmlLat1.add(new CharacterEntity(223, "szlig")); + htmlLat1.add(new CharacterEntity(224, "agrave")); + htmlLat1.add(new CharacterEntity(225, "aacute")); + htmlLat1.add(new CharacterEntity(226, "acirc")); + htmlLat1.add(new CharacterEntity(227, "atilde")); + htmlLat1.add(new CharacterEntity(228, "auml")); + htmlLat1.add(new CharacterEntity(229, "aring")); + htmlLat1.add(new CharacterEntity(230, "aelig")); + htmlLat1.add(new CharacterEntity(231, "ccedil")); + htmlLat1.add(new CharacterEntity(232, "egrave")); + htmlLat1.add(new CharacterEntity(233, "eacute")); + htmlLat1.add(new CharacterEntity(234, "ecirc")); + htmlLat1.add(new CharacterEntity(235, "euml")); + htmlLat1.add(new CharacterEntity(236, "igrave")); + htmlLat1.add(new CharacterEntity(237, "iacute")); + htmlLat1.add(new CharacterEntity(238, "icirc")); + htmlLat1.add(new CharacterEntity(239, "iuml")); + htmlLat1.add(new CharacterEntity(240, "eth")); + htmlLat1.add(new CharacterEntity(241, "ntilde")); + htmlLat1.add(new CharacterEntity(242, "ograve")); + htmlLat1.add(new CharacterEntity(243, "oacute")); + htmlLat1.add(new CharacterEntity(244, "ocirc")); + htmlLat1.add(new CharacterEntity(245, "otilde")); + htmlLat1.add(new CharacterEntity(246, "ouml")); + htmlLat1.add(new CharacterEntity(247, "divide")); + htmlLat1.add(new CharacterEntity(248, "oslash")); + htmlLat1.add(new CharacterEntity(249, "ugrave")); + htmlLat1.add(new CharacterEntity(250, "uacute")); + htmlLat1.add(new CharacterEntity(251, "ucirc")); + htmlLat1.add(new CharacterEntity(252, "uuml")); + htmlLat1.add(new CharacterEntity(253, "yacute")); + htmlLat1.add(new CharacterEntity(254, "thorn")); + htmlLat1.add(new CharacterEntity(255, "yuml")); - /** Entities defined in http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent. */ - private static List htmlSymbol; + // cf. http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent + final List htmlSymbol = new ArrayList<>(); + htmlSymbol.add(new CharacterEntity(402, "fnof")); + htmlSymbol.add(new CharacterEntity(913, "Alpha")); + htmlSymbol.add(new CharacterEntity(914, "Beta")); + htmlSymbol.add(new CharacterEntity(915, "Gamma")); + htmlSymbol.add(new CharacterEntity(916, "Delta")); + htmlSymbol.add(new CharacterEntity(917, "Epsilon")); + htmlSymbol.add(new CharacterEntity(918, "Zeta")); + htmlSymbol.add(new CharacterEntity(919, "Eta")); + htmlSymbol.add(new CharacterEntity(920, "Theta")); + htmlSymbol.add(new CharacterEntity(921, "Iota")); + htmlSymbol.add(new CharacterEntity(922, "Kappa")); + htmlSymbol.add(new CharacterEntity(923, "Lambda")); + htmlSymbol.add(new CharacterEntity(924, "Mu")); + htmlSymbol.add(new CharacterEntity(925, "Nu")); + htmlSymbol.add(new CharacterEntity(926, "Xi")); + htmlSymbol.add(new CharacterEntity(927, "Omicron")); + htmlSymbol.add(new CharacterEntity(928, "Pi")); + htmlSymbol.add(new CharacterEntity(929, "Rho")); + htmlSymbol.add(new CharacterEntity(931, "Sigma")); + htmlSymbol.add(new CharacterEntity(932, "Tau")); + htmlSymbol.add(new CharacterEntity(933, "Upsilon")); + htmlSymbol.add(new CharacterEntity(934, "Phi")); + htmlSymbol.add(new CharacterEntity(935, "Chi")); + htmlSymbol.add(new CharacterEntity(936, "Psi")); + htmlSymbol.add(new CharacterEntity(937, "Omega")); + htmlSymbol.add(new CharacterEntity(945, "alpha")); + htmlSymbol.add(new CharacterEntity(946, "beta")); + htmlSymbol.add(new CharacterEntity(947, "gamma")); + htmlSymbol.add(new CharacterEntity(948, "delta")); + htmlSymbol.add(new CharacterEntity(949, "epsilon")); + htmlSymbol.add(new CharacterEntity(950, "zeta")); + htmlSymbol.add(new CharacterEntity(951, "eta")); + htmlSymbol.add(new CharacterEntity(952, "theta")); + htmlSymbol.add(new CharacterEntity(953, "iota")); + htmlSymbol.add(new CharacterEntity(954, "kappa")); + htmlSymbol.add(new CharacterEntity(955, "lambda")); + htmlSymbol.add(new CharacterEntity(956, "mu")); + htmlSymbol.add(new CharacterEntity(957, "nu")); + htmlSymbol.add(new CharacterEntity(958, "xi")); + htmlSymbol.add(new CharacterEntity(959, "omicron")); + htmlSymbol.add(new CharacterEntity(960, "pi")); + htmlSymbol.add(new CharacterEntity(961, "rho")); + htmlSymbol.add(new CharacterEntity(962, "sigmaf")); + htmlSymbol.add(new CharacterEntity(963, "sigma")); + htmlSymbol.add(new CharacterEntity(964, "tau")); + htmlSymbol.add(new CharacterEntity(965, "upsilon")); + htmlSymbol.add(new CharacterEntity(966, "phi")); + htmlSymbol.add(new CharacterEntity(967, "chi")); + htmlSymbol.add(new CharacterEntity(968, "psi")); + htmlSymbol.add(new CharacterEntity(969, "omega")); + htmlSymbol.add(new CharacterEntity(977, "thetasym")); + htmlSymbol.add(new CharacterEntity(978, "upsih")); + htmlSymbol.add(new CharacterEntity(982, "piv")); + htmlSymbol.add(new CharacterEntity(8230, "hellip")); + htmlSymbol.add(new CharacterEntity(8242, "prime")); + htmlSymbol.add(new CharacterEntity(8243, "Prime")); + htmlSymbol.add(new CharacterEntity(8254, "oline")); + htmlSymbol.add(new CharacterEntity(8260, "frasl")); + htmlSymbol.add(new CharacterEntity(8465, "image")); + htmlSymbol.add(new CharacterEntity(8472, "weierp")); + htmlSymbol.add(new CharacterEntity(8476, "real")); + htmlSymbol.add(new CharacterEntity(8482, "trade")); + htmlSymbol.add(new CharacterEntity(8501, "alefsym")); + htmlSymbol.add(new CharacterEntity(8592, "larr")); + htmlSymbol.add(new CharacterEntity(8593, "uarr")); + htmlSymbol.add(new CharacterEntity(8594, "rarr")); + htmlSymbol.add(new CharacterEntity(8595, "darr")); + htmlSymbol.add(new CharacterEntity(8596, "harr")); + htmlSymbol.add(new CharacterEntity(8629, "crarr")); + htmlSymbol.add(new CharacterEntity(8656, "lArr")); + htmlSymbol.add(new CharacterEntity(8657, "uArr")); + htmlSymbol.add(new CharacterEntity(8658, "rArr")); + htmlSymbol.add(new CharacterEntity(8659, "dArr")); + htmlSymbol.add(new CharacterEntity(8660, "hArr")); + htmlSymbol.add(new CharacterEntity(8704, "forall")); + htmlSymbol.add(new CharacterEntity(8706, "part")); + htmlSymbol.add(new CharacterEntity(8707, "exist")); + htmlSymbol.add(new CharacterEntity(8709, "empty")); + htmlSymbol.add(new CharacterEntity(8711, "nabla")); + htmlSymbol.add(new CharacterEntity(8712, "isin")); + htmlSymbol.add(new CharacterEntity(8713, "notin")); + htmlSymbol.add(new CharacterEntity(8715, "ni")); + htmlSymbol.add(new CharacterEntity(8719, "prod")); + htmlSymbol.add(new CharacterEntity(8721, "sum")); + htmlSymbol.add(new CharacterEntity(8722, "minus")); + htmlSymbol.add(new CharacterEntity(8727, "lowast")); + htmlSymbol.add(new CharacterEntity(8730, "radic")); + htmlSymbol.add(new CharacterEntity(8733, "prop")); + htmlSymbol.add(new CharacterEntity(8734, "infin")); + htmlSymbol.add(new CharacterEntity(8736, "ang")); + htmlSymbol.add(new CharacterEntity(8743, "and")); + htmlSymbol.add(new CharacterEntity(8744, "or")); + htmlSymbol.add(new CharacterEntity(8745, "cap")); + htmlSymbol.add(new CharacterEntity(8746, "cup")); + htmlSymbol.add(new CharacterEntity(8747, "int")); + htmlSymbol.add(new CharacterEntity(8756, "there4")); + htmlSymbol.add(new CharacterEntity(8764, "sim")); + htmlSymbol.add(new CharacterEntity(8773, "cong")); + htmlSymbol.add(new CharacterEntity(8776, "asymp")); + htmlSymbol.add(new CharacterEntity(8800, "ne")); + htmlSymbol.add(new CharacterEntity(8801, "equiv")); + htmlSymbol.add(new CharacterEntity(8804, "le")); + htmlSymbol.add(new CharacterEntity(8805, "ge")); + htmlSymbol.add(new CharacterEntity(8834, "sub")); + htmlSymbol.add(new CharacterEntity(8835, "sup")); + htmlSymbol.add(new CharacterEntity(8836, "nsub")); + htmlSymbol.add(new CharacterEntity(8838, "sube")); + htmlSymbol.add(new CharacterEntity(8839, "supe")); + htmlSymbol.add(new CharacterEntity(8853, "oplus")); + htmlSymbol.add(new CharacterEntity(8855, "otimes")); + htmlSymbol.add(new CharacterEntity(8869, "perp")); + htmlSymbol.add(new CharacterEntity(8901, "sdot")); + htmlSymbol.add(new CharacterEntity(8968, "lceil")); + htmlSymbol.add(new CharacterEntity(8969, "rceil")); + htmlSymbol.add(new CharacterEntity(8970, "lfloor")); + htmlSymbol.add(new CharacterEntity(8971, "rfloor")); + htmlSymbol.add(new CharacterEntity(9001, "lang")); + htmlSymbol.add(new CharacterEntity(9002, "rang")); + htmlSymbol.add(new CharacterEntity(9674, "loz")); + htmlSymbol.add(new CharacterEntity(9824, "spades")); + htmlSymbol.add(new CharacterEntity(9827, "clubs")); + htmlSymbol.add(new CharacterEntity(9829, "hearts")); + htmlSymbol.add(new CharacterEntity(9830, "diams")); - /** Basic entities. */ - private static List xml10; + // cf. http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent + final List htmlSpecial = new ArrayList<>(); + htmlSpecial.add(new CharacterEntity(34, "quot")); + htmlSpecial.add(new CharacterEntity(38, "amp")); + htmlSpecial.add(new CharacterEntity(39, "apos")); + htmlSpecial.add(new CharacterEntity(60, "lt")); + htmlSpecial.add(new CharacterEntity(62, "gt")); + htmlSpecial.add(new CharacterEntity(338, "OElig")); + htmlSpecial.add(new CharacterEntity(339, "oelig")); + htmlSpecial.add(new CharacterEntity(352, "Scaron")); + htmlSpecial.add(new CharacterEntity(353, "scaron")); + htmlSpecial.add(new CharacterEntity(376, "Yuml")); + htmlSpecial.add(new CharacterEntity(710, "circ")); + htmlSpecial.add(new CharacterEntity(732, "tilde")); + htmlSpecial.add(new CharacterEntity(8194, "ensp")); + htmlSpecial.add(new CharacterEntity(8195, "emsp")); + htmlSpecial.add(new CharacterEntity(8201, "thinsp")); + htmlSpecial.add(new CharacterEntity(8204, "zwnj")); + htmlSpecial.add(new CharacterEntity(8205, "zwj")); + htmlSpecial.add(new CharacterEntity(8206, "lrm")); + htmlSpecial.add(new CharacterEntity(8207, "rlm")); + htmlSpecial.add(new CharacterEntity(8211, "ndash")); + htmlSpecial.add(new CharacterEntity(8212, "mdash")); + htmlSpecial.add(new CharacterEntity(8216, "lsquo")); + htmlSpecial.add(new CharacterEntity(8217, "rsquo")); + htmlSpecial.add(new CharacterEntity(8218, "sbquo")); + htmlSpecial.add(new CharacterEntity(8220, "ldquo")); + htmlSpecial.add(new CharacterEntity(8221, "rdquo")); + htmlSpecial.add(new CharacterEntity(8222, "bdquo")); + htmlSpecial.add(new CharacterEntity(8224, "dagger")); + htmlSpecial.add(new CharacterEntity(8225, "Dagger")); + htmlSpecial.add(new CharacterEntity(8226, "bull")); + htmlSpecial.add(new CharacterEntity(8240, "permil")); + htmlSpecial.add(new CharacterEntity(8249, "lsaquo")); + htmlSpecial.add(new CharacterEntity(8250, "rsaquo")); + htmlSpecial.add(new CharacterEntity(8364, "euro")); - static { - xml10 = new ArrayList(); - xml10.add(new CharacterEntity(34, "quot")); - xml10.add(new CharacterEntity(38, "amp")); - xml10.add(new CharacterEntity(62, "gt")); - xml10.add(new CharacterEntity(60, "lt")); - htmlLat1 = new ArrayList(); - htmlLat1.add(new CharacterEntity(160, "nbsp")); - htmlLat1.add(new CharacterEntity(161, "iexcl")); - htmlLat1.add(new CharacterEntity(162, "cent")); - htmlLat1.add(new CharacterEntity(163, "pound")); - htmlLat1.add(new CharacterEntity(164, "curren")); - htmlLat1.add(new CharacterEntity(165, "yen")); - htmlLat1.add(new CharacterEntity(166, "brvbar")); - htmlLat1.add(new CharacterEntity(167, "sect")); - htmlLat1.add(new CharacterEntity(168, "uml")); - htmlLat1.add(new CharacterEntity(169, "copy")); - htmlLat1.add(new CharacterEntity(170, "ordf")); - htmlLat1.add(new CharacterEntity(171, "laquo")); - htmlLat1.add(new CharacterEntity(172, "not")); - htmlLat1.add(new CharacterEntity(173, "shy")); - htmlLat1.add(new CharacterEntity(174, "reg")); - htmlLat1.add(new CharacterEntity(175, "macr")); - htmlLat1.add(new CharacterEntity(176, "deg")); - htmlLat1.add(new CharacterEntity(177, "plusmn")); - htmlLat1.add(new CharacterEntity(178, "sup2")); - htmlLat1.add(new CharacterEntity(179, "sup3")); - htmlLat1.add(new CharacterEntity(180, "acute")); - htmlLat1.add(new CharacterEntity(181, "micro")); - htmlLat1.add(new CharacterEntity(182, "para")); - htmlLat1.add(new CharacterEntity(183, "middot")); - htmlLat1.add(new CharacterEntity(184, "cedil")); - htmlLat1.add(new CharacterEntity(185, "sup1")); - htmlLat1.add(new CharacterEntity(186, "ordm")); - htmlLat1.add(new CharacterEntity(187, "raquo")); - htmlLat1.add(new CharacterEntity(188, "frac14")); - htmlLat1.add(new CharacterEntity(189, "frac12")); - htmlLat1.add(new CharacterEntity(190, "frac34")); - htmlLat1.add(new CharacterEntity(191, "iquest")); - htmlLat1.add(new CharacterEntity(192, "Agrave")); - htmlLat1.add(new CharacterEntity(193, "Aacute")); - htmlLat1.add(new CharacterEntity(194, "Acirc")); - htmlLat1.add(new CharacterEntity(195, "Atilde")); - htmlLat1.add(new CharacterEntity(196, "Auml")); - htmlLat1.add(new CharacterEntity(197, "Aring")); - htmlLat1.add(new CharacterEntity(198, "AElig")); - htmlLat1.add(new CharacterEntity(199, "Ccedil")); - htmlLat1.add(new CharacterEntity(200, "Egrave")); - htmlLat1.add(new CharacterEntity(201, "Eacute")); - htmlLat1.add(new CharacterEntity(202, "Ecirc")); - htmlLat1.add(new CharacterEntity(203, "Euml")); - htmlLat1.add(new CharacterEntity(204, "Igrave")); - htmlLat1.add(new CharacterEntity(205, "Iacute")); - htmlLat1.add(new CharacterEntity(206, "Icirc")); - htmlLat1.add(new CharacterEntity(207, "Iuml")); - htmlLat1.add(new CharacterEntity(208, "ETH")); - htmlLat1.add(new CharacterEntity(209, "Ntilde")); - htmlLat1.add(new CharacterEntity(210, "Ograve")); - htmlLat1.add(new CharacterEntity(211, "Oacute")); - htmlLat1.add(new CharacterEntity(212, "Ocirc")); - htmlLat1.add(new CharacterEntity(213, "Otilde")); - htmlLat1.add(new CharacterEntity(214, "Ouml")); - htmlLat1.add(new CharacterEntity(215, "times")); - htmlLat1.add(new CharacterEntity(216, "Oslash")); - htmlLat1.add(new CharacterEntity(217, "Ugrave")); - htmlLat1.add(new CharacterEntity(218, "Uacute")); - htmlLat1.add(new CharacterEntity(219, "Ucirc")); - htmlLat1.add(new CharacterEntity(220, "Uuml")); - htmlLat1.add(new CharacterEntity(221, "Yacute")); - htmlLat1.add(new CharacterEntity(222, "THORN")); - htmlLat1.add(new CharacterEntity(223, "szlig")); - htmlLat1.add(new CharacterEntity(224, "agrave")); - htmlLat1.add(new CharacterEntity(225, "aacute")); - htmlLat1.add(new CharacterEntity(226, "acirc")); - htmlLat1.add(new CharacterEntity(227, "atilde")); - htmlLat1.add(new CharacterEntity(228, "auml")); - htmlLat1.add(new CharacterEntity(229, "aring")); - htmlLat1.add(new CharacterEntity(230, "aelig")); - htmlLat1.add(new CharacterEntity(231, "ccedil")); - htmlLat1.add(new CharacterEntity(232, "egrave")); - htmlLat1.add(new CharacterEntity(233, "eacute")); - htmlLat1.add(new CharacterEntity(234, "ecirc")); - htmlLat1.add(new CharacterEntity(235, "euml")); - htmlLat1.add(new CharacterEntity(236, "igrave")); - htmlLat1.add(new CharacterEntity(237, "iacute")); - htmlLat1.add(new CharacterEntity(238, "icirc")); - htmlLat1.add(new CharacterEntity(239, "iuml")); - htmlLat1.add(new CharacterEntity(240, "eth")); - htmlLat1.add(new CharacterEntity(241, "ntilde")); - htmlLat1.add(new CharacterEntity(242, "ograve")); - htmlLat1.add(new CharacterEntity(243, "oacute")); - htmlLat1.add(new CharacterEntity(244, "ocirc")); - htmlLat1.add(new CharacterEntity(245, "otilde")); - htmlLat1.add(new CharacterEntity(246, "ouml")); - htmlLat1.add(new CharacterEntity(247, "divide")); - htmlLat1.add(new CharacterEntity(248, "oslash")); - htmlLat1.add(new CharacterEntity(249, "ugrave")); - htmlLat1.add(new CharacterEntity(250, "uacute")); - htmlLat1.add(new CharacterEntity(251, "ucirc")); - htmlLat1.add(new CharacterEntity(252, "uuml")); - htmlLat1.add(new CharacterEntity(253, "yacute")); - htmlLat1.add(new CharacterEntity(254, "thorn")); - htmlLat1.add(new CharacterEntity(255, "yuml")); - htmlSymbol = new ArrayList(); - htmlSymbol.add(new CharacterEntity(402, "fnof")); - htmlSymbol.add(new CharacterEntity(913, "Alpha")); - htmlSymbol.add(new CharacterEntity(914, "Beta")); - htmlSymbol.add(new CharacterEntity(915, "Gamma")); - htmlSymbol.add(new CharacterEntity(916, "Delta")); - htmlSymbol.add(new CharacterEntity(917, "Epsilon")); - htmlSymbol.add(new CharacterEntity(918, "Zeta")); - htmlSymbol.add(new CharacterEntity(919, "Eta")); - htmlSymbol.add(new CharacterEntity(920, "Theta")); - htmlSymbol.add(new CharacterEntity(921, "Iota")); - htmlSymbol.add(new CharacterEntity(922, "Kappa")); - htmlSymbol.add(new CharacterEntity(923, "Lambda")); - htmlSymbol.add(new CharacterEntity(924, "Mu")); - htmlSymbol.add(new CharacterEntity(925, "Nu")); - htmlSymbol.add(new CharacterEntity(926, "Xi")); - htmlSymbol.add(new CharacterEntity(927, "Omicron")); - htmlSymbol.add(new CharacterEntity(928, "Pi")); - htmlSymbol.add(new CharacterEntity(929, "Rho")); - htmlSymbol.add(new CharacterEntity(931, "Sigma")); - htmlSymbol.add(new CharacterEntity(932, "Tau")); - htmlSymbol.add(new CharacterEntity(933, "Upsilon")); - htmlSymbol.add(new CharacterEntity(934, "Phi")); - htmlSymbol.add(new CharacterEntity(935, "Chi")); - htmlSymbol.add(new CharacterEntity(936, "Psi")); - htmlSymbol.add(new CharacterEntity(937, "Omega")); - htmlSymbol.add(new CharacterEntity(945, "alpha")); - htmlSymbol.add(new CharacterEntity(946, "beta")); - htmlSymbol.add(new CharacterEntity(947, "gamma")); - htmlSymbol.add(new CharacterEntity(948, "delta")); - htmlSymbol.add(new CharacterEntity(949, "epsilon")); - htmlSymbol.add(new CharacterEntity(950, "zeta")); - htmlSymbol.add(new CharacterEntity(951, "eta")); - htmlSymbol.add(new CharacterEntity(952, "theta")); - htmlSymbol.add(new CharacterEntity(953, "iota")); - htmlSymbol.add(new CharacterEntity(954, "kappa")); - htmlSymbol.add(new CharacterEntity(955, "lambda")); - htmlSymbol.add(new CharacterEntity(956, "mu")); - htmlSymbol.add(new CharacterEntity(957, "nu")); - htmlSymbol.add(new CharacterEntity(958, "xi")); - htmlSymbol.add(new CharacterEntity(959, "omicron")); - htmlSymbol.add(new CharacterEntity(960, "pi")); - htmlSymbol.add(new CharacterEntity(961, "rho")); - htmlSymbol.add(new CharacterEntity(962, "sigmaf")); - htmlSymbol.add(new CharacterEntity(963, "sigma")); - htmlSymbol.add(new CharacterEntity(964, "tau")); - htmlSymbol.add(new CharacterEntity(965, "upsilon")); - htmlSymbol.add(new CharacterEntity(966, "phi")); - htmlSymbol.add(new CharacterEntity(967, "chi")); - htmlSymbol.add(new CharacterEntity(968, "psi")); - htmlSymbol.add(new CharacterEntity(969, "omega")); - htmlSymbol.add(new CharacterEntity(977, "thetasym")); - htmlSymbol.add(new CharacterEntity(978, "upsih")); - htmlSymbol.add(new CharacterEntity(982, "piv")); - htmlSymbol.add(new CharacterEntity(8230, "hellip")); - htmlSymbol.add(new CharacterEntity(8242, "prime")); - htmlSymbol.add(new CharacterEntity(8243, "Prime")); - htmlSymbol.add(new CharacterEntity(8254, "oline")); - htmlSymbol.add(new CharacterEntity(8260, "frasl")); - htmlSymbol.add(new CharacterEntity(8465, "image")); - htmlSymbol.add(new CharacterEntity(8472, "weierp")); - htmlSymbol.add(new CharacterEntity(8476, "real")); - htmlSymbol.add(new CharacterEntity(8482, "trade")); - htmlSymbol.add(new CharacterEntity(8501, "alefsym")); - htmlSymbol.add(new CharacterEntity(8592, "larr")); - htmlSymbol.add(new CharacterEntity(8593, "uarr")); - htmlSymbol.add(new CharacterEntity(8594, "rarr")); - htmlSymbol.add(new CharacterEntity(8595, "darr")); - htmlSymbol.add(new CharacterEntity(8596, "harr")); - htmlSymbol.add(new CharacterEntity(8629, "crarr")); - htmlSymbol.add(new CharacterEntity(8656, "lArr")); - htmlSymbol.add(new CharacterEntity(8657, "uArr")); - htmlSymbol.add(new CharacterEntity(8658, "rArr")); - htmlSymbol.add(new CharacterEntity(8659, "dArr")); - htmlSymbol.add(new CharacterEntity(8660, "hArr")); - htmlSymbol.add(new CharacterEntity(8704, "forall")); - htmlSymbol.add(new CharacterEntity(8706, "part")); - htmlSymbol.add(new CharacterEntity(8707, "exist")); - htmlSymbol.add(new CharacterEntity(8709, "empty")); - htmlSymbol.add(new CharacterEntity(8711, "nabla")); - htmlSymbol.add(new CharacterEntity(8712, "isin")); - htmlSymbol.add(new CharacterEntity(8713, "notin")); - htmlSymbol.add(new CharacterEntity(8715, "ni")); - htmlSymbol.add(new CharacterEntity(8719, "prod")); - htmlSymbol.add(new CharacterEntity(8721, "sum")); - htmlSymbol.add(new CharacterEntity(8722, "minus")); - htmlSymbol.add(new CharacterEntity(8727, "lowast")); - htmlSymbol.add(new CharacterEntity(8730, "radic")); - htmlSymbol.add(new CharacterEntity(8733, "prop")); - htmlSymbol.add(new CharacterEntity(8734, "infin")); - htmlSymbol.add(new CharacterEntity(8736, "ang")); - htmlSymbol.add(new CharacterEntity(8743, "and")); - htmlSymbol.add(new CharacterEntity(8744, "or")); - htmlSymbol.add(new CharacterEntity(8745, "cap")); - htmlSymbol.add(new CharacterEntity(8746, "cup")); - htmlSymbol.add(new CharacterEntity(8747, "int")); - htmlSymbol.add(new CharacterEntity(8756, "there4")); - htmlSymbol.add(new CharacterEntity(8764, "sim")); - htmlSymbol.add(new CharacterEntity(8773, "cong")); - htmlSymbol.add(new CharacterEntity(8776, "asymp")); - htmlSymbol.add(new CharacterEntity(8800, "ne")); - htmlSymbol.add(new CharacterEntity(8801, "equiv")); - htmlSymbol.add(new CharacterEntity(8804, "le")); - htmlSymbol.add(new CharacterEntity(8805, "ge")); - htmlSymbol.add(new CharacterEntity(8834, "sub")); - htmlSymbol.add(new CharacterEntity(8835, "sup")); - htmlSymbol.add(new CharacterEntity(8836, "nsub")); - htmlSymbol.add(new CharacterEntity(8838, "sube")); - htmlSymbol.add(new CharacterEntity(8839, "supe")); - htmlSymbol.add(new CharacterEntity(8853, "oplus")); - htmlSymbol.add(new CharacterEntity(8855, "otimes")); - htmlSymbol.add(new CharacterEntity(8869, "perp")); - htmlSymbol.add(new CharacterEntity(8901, "sdot")); - htmlSymbol.add(new CharacterEntity(8968, "lceil")); - htmlSymbol.add(new CharacterEntity(8969, "rceil")); - htmlSymbol.add(new CharacterEntity(8970, "lfloor")); - htmlSymbol.add(new CharacterEntity(8971, "rfloor")); - htmlSymbol.add(new CharacterEntity(9001, "lang")); - htmlSymbol.add(new CharacterEntity(9002, "rang")); - htmlSymbol.add(new CharacterEntity(9674, "loz")); - htmlSymbol.add(new CharacterEntity(9824, "spades")); - htmlSymbol.add(new CharacterEntity(9827, "clubs")); - htmlSymbol.add(new CharacterEntity(9829, "hearts")); - htmlSymbol.add(new CharacterEntity(9830, "diams")); - htmlSpecial = new ArrayList(); - htmlSpecial.add(new CharacterEntity(34, "quot")); - htmlSpecial.add(new CharacterEntity(38, "amp")); - htmlSpecial.add(new CharacterEntity(39, "apos")); - htmlSpecial.add(new CharacterEntity(60, "lt")); - htmlSpecial.add(new CharacterEntity(62, "gt")); - htmlSpecial.add(new CharacterEntity(338, "OElig")); - htmlSpecial.add(new CharacterEntity(339, "oelig")); - htmlSpecial.add(new CharacterEntity(352, "Scaron")); - htmlSpecial.add(new CharacterEntity(353, "scaron")); - htmlSpecial.add(new CharacterEntity(376, "Yuml")); - htmlSpecial.add(new CharacterEntity(710, "circ")); - htmlSpecial.add(new CharacterEntity(732, "tilde")); - htmlSpecial.add(new CharacterEntity(8194, "ensp")); - htmlSpecial.add(new CharacterEntity(8195, "emsp")); - htmlSpecial.add(new CharacterEntity(8201, "thinsp")); - htmlSpecial.add(new CharacterEntity(8204, "zwnj")); - htmlSpecial.add(new CharacterEntity(8205, "zwj")); - htmlSpecial.add(new CharacterEntity(8206, "lrm")); - htmlSpecial.add(new CharacterEntity(8207, "rlm")); - htmlSpecial.add(new CharacterEntity(8211, "ndash")); - htmlSpecial.add(new CharacterEntity(8212, "mdash")); - htmlSpecial.add(new CharacterEntity(8216, "lsquo")); - htmlSpecial.add(new CharacterEntity(8217, "rsquo")); - htmlSpecial.add(new CharacterEntity(8218, "sbquo")); - htmlSpecial.add(new CharacterEntity(8220, "ldquo")); - htmlSpecial.add(new CharacterEntity(8221, "rdquo")); - htmlSpecial.add(new CharacterEntity(8222, "bdquo")); - htmlSpecial.add(new CharacterEntity(8224, "dagger")); - htmlSpecial.add(new CharacterEntity(8225, "Dagger")); - htmlSpecial.add(new CharacterEntity(8226, "bull")); - htmlSpecial.add(new CharacterEntity(8240, "permil")); - htmlSpecial.add(new CharacterEntity(8249, "lsaquo")); - htmlSpecial.add(new CharacterEntity(8250, "rsaquo")); - htmlSpecial.add(new CharacterEntity(8364, "euro")); - html40Entities = new CharacterEntitySolver(); - for (CharacterEntity entity : xml10) { - html40Entities.add(entity.getNumericValue(), entity.getName()); - } - for (CharacterEntity entity : htmlLat1) { - html40Entities.add(entity.getNumericValue(), entity.getName()); - } - for (CharacterEntity entity : htmlSymbol) { - html40Entities.add(entity.getNumericValue(), entity.getName()); - } - for (CharacterEntity entity : htmlSpecial) { - html40Entities.add(entity.getNumericValue(), entity.getName()); - } - } + html40Entities = new CharacterEntitySolver(); + for (CharacterEntity entity : xml10) { + html40Entities.add(entity.getNumericValue(), entity.getName()); + } + for (CharacterEntity entity : htmlLat1) { + html40Entities.add(entity.getNumericValue(), entity.getName()); + } + for (CharacterEntity entity : htmlSymbol) { + html40Entities.add(entity.getNumericValue(), entity.getName()); + } + for (CharacterEntity entity : htmlSpecial) { + html40Entities.add(entity.getNumericValue(), entity.getName()); + } + } - /** - * Returns the string with the first character capitalized. - * - * @param string a string reference to check - * @return the string with the first character capitalized. - */ - public static String firstLower(String string) { - if (!isNullOrEmpty(string)) { - return string.substring(0, 1).toLowerCase() + string.substring(1); - } - return string; - } + /** + * Returns the string with the first character capitalized. + * + * @param string a string reference to check + * @return the string with the first character capitalized. + */ + public static String firstLower(String string) { + if (!isNullOrEmpty(string)) { + return string.substring(0, 1).toLowerCase() + string.substring(1); + } + return string; + } - /** - * Returns the string with the first character capitalized. - * - * @param string a string reference to check - * @return the string with the first character capitalized. - */ - public static String firstUpper(String string) { - if (!isNullOrEmpty(string)) { - return string.substring(0, 1).toUpperCase() + string.substring(1); - } - return string; - } + /** + * Returns the string with the first character capitalized. + * + * @param string a string reference to check + * @return the string with the first character capitalized. + */ + public static String firstUpper(String string) { + if (!isNullOrEmpty(string)) { + return string.substring(0, 1).toUpperCase() + string.substring(1); + } + return string; + } - /** - * Encodes the given String into a sequence of bytes using the Ascii character - * set. - * - * @param string The string to encode. - * @return The String encoded with the Ascii character set as an array of bytes. - */ - public static byte[] getAsciiBytes(String string) { - if (string != null) { - try { - return string.getBytes(StandardCharsets.US_ASCII); - } catch (Exception e) { - // Should not happen. - return null; - } - } - return null; - } + /** + * Encodes the given String into a sequence of bytes using the Ascii character set. + * + * @param string The string to encode. + * @return The String encoded with the Ascii character set as an array of bytes. + */ + public static byte[] getAsciiBytes(String string) { + if (string != null) { + try { + return string.getBytes(StandardCharsets.US_ASCII); + } catch (Exception e) { + // Should not happen. + return null; + } + } + return null; + } - /** - * Encodes the given String into a sequence of bytes using the Latin1 character - * set. - * - * @param string The string to encode. - * @return The String encoded with the Latin1 character set as an array of - * bytes. - */ - public static byte[] getLatin1Bytes(String string) { - if (string != null) { - try { - return string.getBytes(StandardCharsets.ISO_8859_1); - } catch (Exception e) { - // Should not happen. - return null; - } - } - return null; - } + /** + * Encodes the given String into a sequence of bytes using the Latin1 character set. + * + * @param string The string to encode. + * @return The String encoded with the Latin1 character set as an array of bytes. + */ + public static byte[] getLatin1Bytes(String string) { + if (string != null) { + try { + return string.getBytes(StandardCharsets.ISO_8859_1); + } catch (Exception e) { + // Should not happen. + return null; + } + } + return null; + } - /** - * Returns the given {@link String} according to the HTML 4.0 encoding rules. - * - * @param str The {@link String} to encode. - * @return The converted {@link String} according to the HTML 4.0 encoding - * rules. - */ - public static String htmlEscape(String str) { - if (str == null || str.isEmpty()) { - return str; - } - int len = str.length(); - StringBuilder sb = new StringBuilder((int) (len * 1.5)); - for (int i = 0; i < len; i++) { - char c = str.charAt(i); - String entityName = html40Entities.getName(c); - if (entityName == null) { - if (c > 127) { - // Escape non ASCII characters. - sb.append("&#").append(Integer.toString(c, 10)).append(';'); - } else { - // ASCII characters are not escaped. - sb.append(c); - } - } else { - sb.append('&').append(entityName).append(';'); - } - } - return sb.toString(); - } + /** + * Returns the given {@link String} according to the HTML 4.0 encoding rules. + * + * @param str The {@link String} to encode. + * @return The converted {@link String} according to the HTML 4.0 encoding rules. + */ + public static String htmlEscape(String str) { + if (str == null || str.isEmpty()) { + return str; + } + int len = str.length(); + StringBuilder sb = new StringBuilder((int) (len * 1.5)); + for (int i = 0; i < len; i++) { + char c = str.charAt(i); + String entityName = html40Entities.getName(c); + if (entityName == null) { + if (c > 127) { + // Escape non ASCII characters. + sb.append("&#").append(Integer.toString(c, 10)).append(';'); + } else { + // ASCII characters are not escaped. + sb.append(c); + } + } else { + sb.append('&').append(entityName).append(';'); + } + } + return sb.toString(); + } - /** - * Returns the given {@link String} decoded according to the HTML 4.0 decoding - * rules. - * - * @param str The {@link String} to decode. - * @return The given {@link String} decoded according to the HTML 4.0 decoding - * rules. - */ - public static String htmlUnescape(String str) { - if (str == null || str.isEmpty()) { - return str; - } - int len = str.length(); - StringBuilder sb = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = str.charAt(i); - if (c == '&') { - int nextIndex = i + 1; - int semicolonIndex = -1; - int ampersandIndex = -1; - boolean stop = false; - for (int j = nextIndex; !stop && j < len; j++) { - char ch = str.charAt(j); - if (';' == ch) { - semicolonIndex = j; - stop = true; - } else if ('&' == ch) { - ampersandIndex = j; - stop = true; - } - } - if (semicolonIndex != -1) { - // Entity found - if (nextIndex != semicolonIndex) { - int entityValue = -1; - String entityName = str.substring(nextIndex, semicolonIndex); - if (entityName.charAt(0) == '#') { - // Numeric value - if (entityName.length() > 1) { - char hexChar = entityName.charAt(1); - try { - if (hexChar == 'X') { - entityValue = Integer.parseInt(entityName.substring(2), 16); - } else if (hexChar == 'x') { - entityValue = Integer.parseInt(entityName.substring(2), 16); - } else { - entityValue = Integer.parseInt(entityName.substring(1), 10); - } - if (!Character.isValidCodePoint(entityValue)) { - // Invalid Unicode character - entityValue = -1; - } - } catch (NumberFormatException e) { - entityValue = -1; - } - } - } else { - Integer val = html40Entities.getValue(entityName); - if (val != null) { - entityValue = val; - } - } - if (entityValue == -1) { - sb.append('&').append(entityName).append(';'); - } else { - sb.append((char) entityValue); - } - } else { - sb.append("&;"); - } - i = semicolonIndex; - } else if (stop) { - // found a "&" character, it could be another entity - sb.append(str, i, ampersandIndex); // add the consumed characters - i = ampersandIndex - 1; // put the index in order to read the "&" character - } else { - // End of the string reached, no more entities to parse. - sb.append(str, i, len); - i = len; - } - } else { - sb.append(c); - } - } - return sb.toString(); - } + /** + * Returns the given {@link String} decoded according to the HTML 4.0 decoding rules. + * + * @param str The {@link String} to decode. + * @return The given {@link String} decoded according to the HTML 4.0 decoding rules. + */ + public static String htmlUnescape(String str) { + if (str == null || str.isEmpty()) { + return str; + } + int len = str.length(); + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) { + char c = str.charAt(i); + if (c == '&') { + int nextIndex = i + 1; + int semicolonIndex = -1; + int ampersandIndex = -1; + boolean stop = false; + for (int j = nextIndex; !stop && j < len; j++) { + char ch = str.charAt(j); + if (';' == ch) { + semicolonIndex = j; + stop = true; + } else if ('&' == ch) { + ampersandIndex = j; + stop = true; + } + } + if (semicolonIndex != -1) { + // Entity found + if (nextIndex != semicolonIndex) { + int entityValue = -1; + String entityName = str.substring(nextIndex, semicolonIndex); + if (entityName.charAt(0) == '#') { + // Numeric value + if (entityName.length() > 1) { + char hexChar = entityName.charAt(1); + try { + if (hexChar == 'X') { + entityValue = Integer.parseInt(entityName.substring(2), 16); + } else if (hexChar == 'x') { + entityValue = Integer.parseInt(entityName.substring(2), 16); + } else { + entityValue = Integer.parseInt(entityName.substring(1), 10); + } + if (!Character.isValidCodePoint(entityValue)) { + // Invalid Unicode character + entityValue = -1; + } + } catch (NumberFormatException e) { + entityValue = -1; + } + } + } else { + Integer val = html40Entities.getValue(entityName); + if (val != null) { + entityValue = val; + } + } + if (entityValue == -1) { + sb.append('&').append(entityName).append(';'); + } else { + sb.append((char) entityValue); + } + } else { + sb.append("&;"); + } + i = semicolonIndex; + } else if (stop) { + // found a "&" character, it could be another entity + sb.append(str, i, ampersandIndex); // add the consumed characters + i = ampersandIndex - 1; // put the index to read the "&" character + } else { + // End of the string reached, no more entities to parse. + sb.append(str, i, len); + i = len; + } + } else { + sb.append(c); + } + } + return sb.toString(); + } - /** - * Returns {@code true} if the given string is null or is the empty string. - * - * Consider normalizing your string references with {@link #nullToEmpty}. If you - * do, you can use {@link String#isEmpty()} instead of this method, and you - * won't need special null-safe forms of methods like {@link String#toUpperCase} - * either. - * - * @param string a string reference to check - * @return {@code true} if the string is null or is the empty string - */ - public static boolean isNullOrEmpty(String string) { - return string == null || string.isEmpty(); - } + /** + * Returns {@code true} if the given string is null or is the empty string. + * + *

Consider normalizing your string references with {@link #nullToEmpty}. If you do, you can + * use {@link String#isEmpty()} instead of this method, and you won't need special null-safe + * forms of methods like {@link String#toUpperCase} either. + * + * @param string a string reference to check + * @return {@code true} if the string is null or is the empty string + */ + public static boolean isNullOrEmpty(String string) { + return string == null || string.isEmpty(); + } - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private StringUtils() { - } + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private StringUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java index 9f1fb55d76..1afc398387 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java @@ -1,116 +1,108 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; /** * System utilities. - * + * * @author Jerome Louvel */ public class SystemUtils { - /** - * Parses the "java.version" system property and returns the first digit of the - * version number of the Java Runtime Environment (e.g. "1" for "1.3.0"). - * - * @see Official Java - * versioning - * @return The major version number of the Java Runtime Environment. - */ - public static int getJavaMajorVersion() { - int result; - final String javaVersion = System.getProperty("java.version"); - try { - result = Integer.parseInt(javaVersion.substring(0, javaVersion.indexOf("."))); - } catch (Exception e) { - result = 0; - } - - return result; - } + /** + * Parses the "java.version" system property and returns the first digit of the version number + * of the Java Runtime Environment (e.g., "1" for "1.3.0"). + * + * @see Official Java versioning + * @return The major version number of the Java Runtime Environment. + */ + public static int getJavaMajorVersion() { + int result; + final String javaVersion = System.getProperty("java.version"); + try { + result = Integer.parseInt(javaVersion.substring(0, javaVersion.indexOf('.'))); + } catch (Exception e) { + result = 0; + } - /** - * Parses the "java.version" system property and returns the second digit of the - * version number of the Java Runtime Environment (e.g. "3" for "1.3.0"). - * - * @see Official Java - * versioning - * @return The minor version number of the Java Runtime Environment. - */ - public static int getJavaMinorVersion() { - int result; - final String javaVersion = System.getProperty("java.version"); - try { - result = Integer.parseInt(javaVersion.split("\\.")[1]); - } catch (Exception e) { - result = 0; - } + return result; + } - return result; - } + /** + * Parses the "java.version" system property and returns the second digit of the version number + * of the Java Runtime Environment (e.g., "3" for "1.3.0"). + * + * @see Official Java versioning + * @return The minor version number of the Java Runtime Environment. + */ + public static int getJavaMinorVersion() { + int result; + final String javaVersion = System.getProperty("java.version"); + try { + result = Integer.parseInt(javaVersion.split("\\.")[1]); + } catch (Exception e) { + result = 0; + } - /** - * Parses the "java.version" system property and returns the update release - * number of the Java Runtime Environment (e.g. "10" for "1.3.0_10"). - * - * @see Official Java - * versioning - * @return The release number of the Java Runtime Environment or 0 if it does - * not exist. - */ - public static int getJavaUpdateVersion() { - int result; - final String javaVersion = System.getProperty("java.version"); - try { - result = Integer.parseInt(javaVersion.substring(javaVersion.indexOf('_') + 1)); - } catch (Exception e) { - result = 0; - } + return result; + } - return result; - } + /** + * Parses the "java.version" system property and returns the update release number of the Java + * Runtime Environment (e.g., "10" for "1.3.0_10"). + * + * @see Official Java versioning + * @return The release number of the Java Runtime Environment or 0 if it does not exist. + */ + public static int getJavaUpdateVersion() { + int result; + final String javaVersion = System.getProperty("java.version"); + try { + result = Integer.parseInt(javaVersion.substring(javaVersion.indexOf('_') + 1)); + } catch (Exception e) { + result = 0; + } - /** - * Computes the hash code of a set of objects. Follows the algorithm specified - * in List.hasCode(). - * - * @param objects the objects to compute the hashCode - * - * @return The hash code of a set of objects. - */ - public static int hashCode(Object... objects) { - int result = 17; + return result; + } - if (objects != null) { - for (final Object obj : objects) { - result = 31 * result + (obj == null ? 0 : obj.hashCode()); - } - } + /** + * Computes the hash code of a set of objects. Follows the algorithm specified in + * List.hasCode(). + * + * @param objects the objects to compute the hashCode + * @return The hash code of a set of objects. + */ + public static int hashCode(Object... objects) { + int result = 17; - return result; - } + if (objects != null) { + for (final Object obj : objects) { + result = 31 * result + (obj == null ? 0 : obj.hashCode()); + } + } - /** - * Indicates if the current operating system is in the Windows family. - * - * @return True if the current operating system is in the Windows family. - */ - public static boolean isWindows() { - return System.getProperty("os.name").toLowerCase().contains("win"); - } + return result; + } - /** - * Private constructor to ensure that the class acts as a true utility class - * i.e. it isn't instantiable and extensible. - */ - private SystemUtils() { - } + /** + * Indicates if the current operating system is in the Windows family. + * + * @return True if the current operating system is in the Windows family. + */ + public static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("win"); + } + /** + * Private constructor to ensure that the class acts as a true utility class i.e., it isn't + * instantiable and extensible. + */ + private SystemUtils() {} } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java index a1dd5a0cfb..cd3e0d4b5a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import org.restlet.Request; @@ -16,56 +15,56 @@ import org.restlet.routing.Template; /** - * Filter that resolves URI templates in the target resource URI reference using - * the request attributes. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state as member variables. - * + * Filter that resolves URI templates in the target resource URI reference using the request + * attributes. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state as member variables. + * * @author Jerome Louvel */ public class TemplateDispatcher extends Filter { - /** - * If the response entity comes back with no identifier, automatically set the - * request's resource reference's identifier. This is very useful to resolve - * relative references in XSLT for example. - */ - @Override - protected void afterHandle(Request request, Response response) { - if ((response.getEntity() != null) && (response.getEntity().getLocationRef() == null)) { - response.getEntity().setLocationRef(request.getResourceRef().getTargetRef().toString()); - } - } - - /** - * Handles the call after resolving any URI template on the request's target - * resource reference. - * - * @param request The request to handle. - * @param response The response to update. - */ - public int beforeHandle(Request request, Response response) { - // Associate the response to the current thread - Protocol protocol = request.getProtocol(); + /** + * If the response entity comes back with no identifier, automatically set the request's + * resource reference's identifier. This is very useful to resolve relative references in XSLT, + * for example. + */ + @Override + protected void afterHandle(Request request, Response response) { + if ((response.getEntity() != null) && (response.getEntity().getLocationRef() == null)) { + response.getEntity().setLocationRef(request.getResourceRef().getTargetRef().toString()); + } + } - if (protocol == null) { - throw new UnsupportedOperationException("Unable to determine the protocol to use for this call."); - } + /** + * Handles the call after resolving any URI template on the request's target resource reference. + * + * @param request The request to handle. + * @param response The response to update. + */ + public int beforeHandle(Request request, Response response) { + // Associate the response to the current thread + Protocol protocol = request.getProtocol(); - String targetUri = request.getResourceRef().toString(true, false); + if (protocol == null) { + throw new UnsupportedOperationException( + "Unable to determine the protocol to use for this call."); + } - if (targetUri.contains("{")) { - // Template URI detected, create the template - Template template = new Template(targetUri); + String targetUri = request.getResourceRef().toString(true, false); - // Set the formatted target URI - request.setResourceRef(template.format(request, response)); - } + if (targetUri.contains("{")) { + // Template URI detected, create the template + Template template = new Template(targetUri); - request.setOriginalRef(ReferenceUtils.getOriginalRef(request.getResourceRef(), request.getHeaders())); - return CONTINUE; - } + // Set the formatted target URI + request.setResourceRef(template.format(request, response)); + } + request.setOriginalRef( + ReferenceUtils.getOriginalRef(request.getResourceRef(), request.getHeaders())); + return CONTINUE; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java b/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java index 74c66707dd..37abbc4e5b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java @@ -1,132 +1,141 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; import java.util.Collection; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** - * Wrapper of a {@link ScheduledExecutorService} instance, to prevent - * manipulation of the actual service. - * + * Wrapper of a {@link ScheduledExecutorService} instance, to prevent manipulation of the actual + * service. + * * @author Jerome Louvel */ public class WrapperScheduledExecutorService implements ScheduledExecutorService { - /** The wrapped executor service. */ - private final ScheduledExecutorService wrapped; - - /** - * Constructor. - * - * @param wrapped The wrapped executor service. - */ - public WrapperScheduledExecutorService(ScheduledExecutorService wrapped) { - this.wrapped = wrapped; - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - return getWrapped().awaitTermination(timeout, unit); - } - - @Override - public void execute(Runnable command) { - getWrapped().execute(command); - } - - /** - * Returns the wrapped executor service. - * - * @return The wrapped executor service. - */ - protected ScheduledExecutorService getWrapped() { - return wrapped; - } - - @Override - public List> invokeAll(Collection> tasks) throws InterruptedException { - return getWrapped().invokeAll(tasks); - } - - @Override - public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException { - return getWrapped().invokeAll(tasks, timeout, unit); - } - - @Override - public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return getWrapped().invokeAny(tasks); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return getWrapped().invokeAny(tasks, timeout, unit); - } - - @Override - public boolean isShutdown() { - return getWrapped().isShutdown(); - } - - @Override - public boolean isTerminated() { - return getWrapped().isTerminated(); - } - - @Override - public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { - return getWrapped().schedule(callable, delay, unit); - } - - @Override - public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - return getWrapped().schedule(command, delay, unit); - } - - @Override - public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - return getWrapped().scheduleAtFixedRate(command, initialDelay, period, unit); - } - - @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { - return getWrapped().scheduleWithFixedDelay(command, initialDelay, delay, unit); - } - - @Override - public void shutdown() { - getWrapped().shutdown(); - } - - @Override - public List shutdownNow() { - return getWrapped().shutdownNow(); - } - - @Override - public Future submit(Callable task) { - return getWrapped().submit(task); - } - - @Override - public Future submit(Runnable task) { - return getWrapped().submit(task); - } - - @Override - public Future submit(Runnable task, T result) { - return getWrapped().submit(task, result); - } - + /** The wrapped executor service. */ + private final ScheduledExecutorService wrapped; + + /** + * Constructor. + * + * @param wrapped The wrapped executor service. + */ + public WrapperScheduledExecutorService(ScheduledExecutorService wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return getWrapped().awaitTermination(timeout, unit); + } + + @Override + public void execute(Runnable command) { + getWrapped().execute(command); + } + + /** + * Returns the wrapped executor service. + * + * @return The wrapped executor service. + */ + protected ScheduledExecutorService getWrapped() { + return wrapped; + } + + @Override + public List> invokeAll(Collection> tasks) + throws InterruptedException { + return getWrapped().invokeAll(tasks); + } + + @Override + public List> invokeAll( + Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return getWrapped().invokeAll(tasks, timeout, unit); + } + + @Override + public T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + return getWrapped().invokeAny(tasks); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return getWrapped().invokeAny(tasks, timeout, unit); + } + + @Override + public boolean isShutdown() { + return getWrapped().isShutdown(); + } + + @Override + public boolean isTerminated() { + return getWrapped().isTerminated(); + } + + @Override + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + return getWrapped().schedule(callable, delay, unit); + } + + @Override + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return getWrapped().schedule(command, delay, unit); + } + + @Override + public ScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + return getWrapped().scheduleAtFixedRate(command, initialDelay, period, unit); + } + + @Override + public ScheduledFuture scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + return getWrapped().scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + + @Override + public void shutdown() { + getWrapped().shutdown(); + } + + @Override + public List shutdownNow() { + return getWrapped().shutdownNow(); + } + + @Override + public Future submit(Callable task) { + return getWrapped().submit(task); + } + + @Override + public Future submit(Runnable task) { + return getWrapped().submit(task); + } + + @Override + public Future submit(Runnable task, T result) { + return getWrapped().submit(task, result); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java index 6e585fd1ee..5ea10542cf 100644 --- a/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java @@ -1,20 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import java.io.IOException; import org.restlet.data.CharacterSet; import org.restlet.data.Language; import org.restlet.data.MediaType; -import java.io.IOException; - /** * Represents an appendable sequence of characters. * @@ -22,123 +20,122 @@ */ public class AppendableRepresentation extends StringRepresentation implements Appendable { - /** The appendable text. */ - private volatile StringBuilder appendableText; - - /** - * Constructor. The following metadata are used by default: "text/plain" media - * type, no language and the ISO-8859-1 character set. - */ - public AppendableRepresentation() { - this(null); - } - - /** - * Constructor. The following metadata are used by default: "text/plain" media - * type, no language and the ISO-8859-1 character set. - * - * @param text The string value. - */ - public AppendableRepresentation(CharSequence text) { - super(text); - } - - /** - * Constructor. The following metadata are used by default: "text/plain" media - * type, no language and the ISO-8859-1 character set. - * - * @param text The string value. - * @param language The language. - */ - public AppendableRepresentation(CharSequence text, Language language) { - super(text, language); - } - - /** - * Constructor. The following metadata are used by default: no language and the - * ISO-8859-1 character set. - * - * @param text The string value. - * @param mediaType The media type. - */ - public AppendableRepresentation(CharSequence text, MediaType mediaType) { - super(text, mediaType); - } - - /** - * Constructor. The following metadata are used by default: ISO-8859-1 character - * set. - * - * @param text The string value. - * @param mediaType The media type. - * @param language The language. - */ - public AppendableRepresentation(CharSequence text, MediaType mediaType, Language language) { - super(text, mediaType, language); - } - - /** - * Constructor. - * - * @param text The string value. - * @param mediaType The media type. - * @param language The language. - * @param characterSet The character set. - */ - public AppendableRepresentation(CharSequence text, MediaType mediaType, Language language, - CharacterSet characterSet) { - super(text, mediaType, language, characterSet); - } - - @Override - public Appendable append(char c) throws IOException { - if (this.appendableText == null) { - this.appendableText = new StringBuilder().append(c); - } else { - this.appendableText.append(c); - } - - return this; - } - - @Override - public Appendable append(CharSequence csq) throws IOException { - if (this.appendableText == null) { - this.appendableText = new StringBuilder(csq); - } else { - this.appendableText.append(csq); - } - - return this; - } - - @Override - public Appendable append(CharSequence csq, int start, int end) throws IOException { - if (this.appendableText == null) { - this.appendableText = new StringBuilder(); - } - - this.appendableText.append(csq, start, end); - - return this; - } - - @Override - public String getText() { - return (this.appendableText == null) ? null : this.appendableText.toString(); - } - - @Override - public void setText(CharSequence text) { - if (text != null) { - if (this.appendableText == null) { - this.appendableText = new StringBuilder(text); - } else { - this.appendableText.delete(0, this.appendableText.length()); - this.appendableText.append(text); - } - } else { - this.appendableText = null; - } - } + /** The appendable text. */ + private volatile StringBuilder appendableText; + + /** + * Constructor. The following metadata is used by default: "text/plain" media type, no language, + * and the ISO-8859-1 character set. + */ + public AppendableRepresentation() { + this(null); + } + + /** + * Constructor. The following metadata is used by default: "text/plain" media type, no language, + * and the ISO-8859-1 character set. + * + * @param text The string value. + */ + public AppendableRepresentation(CharSequence text) { + super(text); + } + + /** + * Constructor. The following metadata is used by default: "text/plain" media type, no language, + * and the ISO-8859-1 character set. + * + * @param text The string value. + * @param language The language. + */ + public AppendableRepresentation(CharSequence text, Language language) { + super(text, language); + } + + /** + * Constructor. The following metadata is used by default: no language and the ISO-8859-1 + * character set. + * + * @param text The string value. + * @param mediaType The media type. + */ + public AppendableRepresentation(CharSequence text, MediaType mediaType) { + super(text, mediaType); + } + + /** + * Constructor. The following metadata is used by default: ISO-8859-1 character set. + * + * @param text The string value. + * @param mediaType The media type. + * @param language The language. + */ + public AppendableRepresentation(CharSequence text, MediaType mediaType, Language language) { + super(text, mediaType, language); + } + + /** + * Constructor. + * + * @param text The string value. + * @param mediaType The media type. + * @param language The language. + * @param characterSet The character set. + */ + public AppendableRepresentation( + CharSequence text, MediaType mediaType, Language language, CharacterSet characterSet) { + super(text, mediaType, language, characterSet); + } + + @Override + public Appendable append(char c) throws IOException { + if (this.appendableText == null) { + this.appendableText = new StringBuilder().append(c); + } else { + this.appendableText.append(c); + } + + return this; + } + + @Override + public Appendable append(CharSequence csq) throws IOException { + if (this.appendableText == null) { + this.appendableText = new StringBuilder(csq); + } else { + this.appendableText.append(csq); + } + + return this; + } + + @Override + public Appendable append(CharSequence csq, int start, int end) throws IOException { + if (this.appendableText == null) { + this.appendableText = new StringBuilder(); + } + + this.appendableText.append(csq, start, end); + + return this; + } + + @Override + public String getText() { + return (this.appendableText == null) ? null : this.appendableText.toString(); + } + + @Override + public void setText(CharSequence text) { + if (text != null) { + if (this.appendableText == null) { + this.appendableText = new StringBuilder(text); + } else { + this.appendableText.delete(0, this.appendableText.length()); + this.appendableText.append(text); + } + } else { + this.appendableText = null; + } + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java index 70b74a35e1..4ffa2be0fb 100644 --- a/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java @@ -1,170 +1,176 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.engine.io.IoUtils; import org.restlet.util.WrapperRepresentation; -import java.io.*; -import java.util.logging.Level; - /** - * Representation capable of buffering the wrapped representation. This is - * useful when you want to prevent chunk encoding from being used for dynamic - * representations or when you want to reuse a transient representation several - * times.
+ * Representation capable of buffering the wrapped representation. This is useful when you want to + * prevent chunk encoding from being used for dynamic representations or when you want to reuse a + * transient representation several times.
*
- * Be careful as this class could create potentially very large byte buffers in - * memory that could impact your application performance. - * + * Be careful as this class could create potentially very large byte buffers in memory that could + * impact your application performance. + * * @author Thierry Boileau */ public class BufferingRepresentation extends WrapperRepresentation { - /** The cached content as an array of bytes. */ - private volatile byte[] buffer; - - /** Indicates if the wrapped entity has been already cached. */ - private volatile boolean buffered; - - /** - * Constructor. - * - * @param bufferedRepresentation The representation to buffer. - */ - public BufferingRepresentation(Representation bufferedRepresentation) { - super(bufferedRepresentation); - setTransient(false); - } - - /** - * Buffers the content of the wrapped entity. - * - * @throws IOException - */ - private void buffer() throws IOException { - if (!isBuffered()) { - if (getWrappedRepresentation().isAvailable()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - getWrappedRepresentation().write(baos); - baos.flush(); - setBuffer(baos.toByteArray()); - setBuffered(true); - } - } - } - - @Override - public long getAvailableSize() { - return getSize(); - } - - /** - * Returns the buffered content as an array of bytes. - * - * @return The buffered content as an array of bytes. - */ - protected byte[] getBuffer() { - return buffer; - } - - @Override - public Reader getReader() throws IOException { - return IoUtils.getReader(getStream(), getCharacterSet()); - } - - @Override - public long getSize() { - // Read the content, store it and compute the size. - try { - buffer(); - } catch (IOException e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to buffer the wrapped representation", e); - } - - return (getBuffer() != null) ? getBuffer().length : -1L; - } - - @Override - public InputStream getStream() throws IOException { - buffer(); - return (getBuffer() != null) ? new ByteArrayInputStream(getBuffer()) : null; - }; - - @Override - public String getText() throws IOException { - buffer(); - - if (getBuffer() != null) { - return (getCharacterSet() != null) ? new String(getBuffer(), getCharacterSet().toCharset()) - : new String(getBuffer()); - } - - return null; - } - - @Override - public boolean isAvailable() { - try { - buffer(); - } catch (IOException e) { - Context.getCurrentLogger().log(Level.FINER, "Unable to buffer the wrapped representation", e); - } - - return isBuffered(); - } - - /** - * Indicates if the wrapped entity has been already buffered. - * - * @return True if the wrapped entity has been already buffered. - */ - protected boolean isBuffered() { - return buffered; - } - - /** - * Sets the buffered content as an array of bytes. - * - * @param buffer The buffered content as an array of bytes. - */ - protected void setBuffer(byte[] buffer) { - this.buffer = buffer; - } - - /** - * Indicates if the wrapped entity has been already buffered. - * - * @param buffered True if the wrapped entity has been already buffered. - */ - protected void setBuffered(boolean buffered) { - this.buffered = buffered; - } - - @Override - public void write(OutputStream outputStream) throws IOException { - buffer(); - - if (getBuffer() != null) { - outputStream.write(getBuffer()); - } - } - - @Override - public void write(Writer writer) throws IOException { - String text = getText(); - - if (text != null) { - writer.write(text); - } - } - + /** The cached content as an array of bytes. */ + private volatile byte[] buffer; + + /** Indicates if the wrapped entity has been already cached. */ + private volatile boolean buffered; + + /** + * Constructor. + * + * @param bufferedRepresentation The representation to buffer. + */ + public BufferingRepresentation(Representation bufferedRepresentation) { + super(bufferedRepresentation); + setTransient(false); + } + + /** + * Buffers the content of the wrapped entity. + * + * @throws IOException + */ + private void buffer() throws IOException { + if (!isBuffered()) { + if (getWrappedRepresentation().isAvailable()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + getWrappedRepresentation().write(baos); + baos.flush(); + setBuffer(baos.toByteArray()); + setBuffered(true); + } + } + } + + @Override + public long getAvailableSize() { + return getSize(); + } + + /** + * Returns the buffered content as an array of bytes. + * + * @return The buffered content as an array of bytes. + */ + protected byte[] getBuffer() { + return buffer; + } + + @Override + public Reader getReader() throws IOException { + return IoUtils.getReader(getStream(), getCharacterSet()); + } + + @Override + public long getSize() { + // Read the content, store it and compute the size. + try { + buffer(); + } catch (IOException e) { + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to buffer the wrapped representation", e); + } + + return (getBuffer() != null) ? getBuffer().length : -1L; + } + + @Override + public InputStream getStream() throws IOException { + buffer(); + return (getBuffer() != null) ? new ByteArrayInputStream(getBuffer()) : null; + } + ; + + @Override + public String getText() throws IOException { + buffer(); + + if (getBuffer() != null) { + return (getCharacterSet() != null) + ? new String(getBuffer(), getCharacterSet().toCharset()) + : new String(getBuffer()); + } + + return null; + } + + @Override + public boolean isAvailable() { + try { + buffer(); + } catch (IOException e) { + Context.getCurrentLogger() + .log(Level.FINER, "Unable to buffer the wrapped representation", e); + } + + return isBuffered(); + } + + /** + * Indicates if the wrapped entity has been already buffered. + * + * @return True if the wrapped entity has been already buffered. + */ + protected boolean isBuffered() { + return buffered; + } + + /** + * Sets the buffered content as an array of bytes. + * + * @param buffer The buffered content as an array of bytes. + */ + protected void setBuffer(byte[] buffer) { + this.buffer = buffer; + } + + /** + * Indicates if the wrapped entity has been already buffered. + * + * @param buffered True if the wrapped entity has been already buffered. + */ + protected void setBuffered(boolean buffered) { + this.buffered = buffered; + } + + @Override + public void write(OutputStream outputStream) throws IOException { + buffer(); + + if (getBuffer() != null) { + outputStream.write(getBuffer()); + } + } + + @Override + public void write(Writer writer) throws IOException { + String text = getText(); + + if (text != null) { + writer.write(text); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java index 92df21d687..3a5592661a 100644 --- a/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java @@ -1,85 +1,79 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.data.MediaType; - import java.io.ByteArrayInputStream; +import org.restlet.data.MediaType; /** * Representation wrapping a byte array. - * + * * @author Jerome Louvel */ public class ByteArrayRepresentation extends InputRepresentation { - /** - * Constructor. - * - * @param byteArray The byte array to wrap. - */ - public ByteArrayRepresentation(byte[] byteArray) { - super(new ByteArrayInputStream(byteArray)); - } - - /** - * - * @param byteArray The byte array to wrap. - * @param offSet The offset inside the byte array. - * @param length The length to expose inside the byte array. - */ - public ByteArrayRepresentation(byte[] byteArray, int offSet, int length) { - super(new ByteArrayInputStream(byteArray, offSet, length)); - } + /** + * Constructor. + * + * @param byteArray The byte array to wrap. + */ + public ByteArrayRepresentation(byte[] byteArray) { + super(new ByteArrayInputStream(byteArray)); + } - /** - * - * @param byteArray The byte array to wrap. - * @param offSet The offset inside the byte array. - * @param length The length to expose inside the byte array. - * @param mediaType - */ - public ByteArrayRepresentation(byte[] byteArray, int offSet, int length, MediaType mediaType) { - super(new ByteArrayInputStream(byteArray, offSet, length), mediaType); - } + /** + * @param byteArray The byte array to wrap. + * @param offSet The offset inside the byte array. + * @param length The length to expose inside the byte array. + */ + public ByteArrayRepresentation(byte[] byteArray, int offSet, int length) { + super(new ByteArrayInputStream(byteArray, offSet, length)); + } - /** - * - * @param byteArray The byte array to wrap. - * @param offSet The offset inside the byte array. - * @param length The length to expose inside the byte array. - * @param mediaType The media type. - * @param expectedSize - */ - public ByteArrayRepresentation(byte[] byteArray, int offSet, int length, MediaType mediaType, long expectedSize) { - super(new ByteArrayInputStream(byteArray, offSet, length), mediaType, expectedSize); - } + /** + * @param byteArray The byte array to wrap. + * @param offSet The offset inside the byte array. + * @param length The length to expose inside the byte array. + * @param mediaType + */ + public ByteArrayRepresentation(byte[] byteArray, int offSet, int length, MediaType mediaType) { + super(new ByteArrayInputStream(byteArray, offSet, length), mediaType); + } - /** - * Constructor. - * - * @param byteArray The byte array to wrap. - * @param mediaType The media type. - */ - public ByteArrayRepresentation(byte[] byteArray, MediaType mediaType) { - super(new ByteArrayInputStream(byteArray), mediaType); - } + /** + * @param byteArray The byte array to wrap. + * @param offSet The offset inside the byte array. + * @param length The length to expose inside the byte array. + * @param mediaType The media type. + * @param expectedSize + */ + public ByteArrayRepresentation( + byte[] byteArray, int offSet, int length, MediaType mediaType, long expectedSize) { + super(new ByteArrayInputStream(byteArray, offSet, length), mediaType, expectedSize); + } - /** - * - * @param byteArray The byte array to wrap. - * @param mediaType The media type. - * @param expectedSize - */ - public ByteArrayRepresentation(byte[] byteArray, MediaType mediaType, long expectedSize) { - super(new ByteArrayInputStream(byteArray), mediaType, expectedSize); - } + /** + * Constructor. + * + * @param byteArray The byte array to wrap. + * @param mediaType The media type. + */ + public ByteArrayRepresentation(byte[] byteArray, MediaType mediaType) { + super(new ByteArrayInputStream(byteArray), mediaType); + } + /** + * @param byteArray The byte array to wrap. + * @param mediaType The media type. + * @param expectedSize + */ + public ByteArrayRepresentation(byte[] byteArray, MediaType mediaType, long expectedSize) { + super(new ByteArrayInputStream(byteArray), mediaType, expectedSize); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java index 15c61f254f..3aa81e2457 100644 --- a/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java @@ -1,49 +1,46 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.data.CharacterSet; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; +import org.restlet.data.CharacterSet; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; /** * Representation based on a BIO character stream. - * + * * @author Jerome Louvel */ public abstract class CharacterRepresentation extends Representation { - /** - * Constructor. - * - * @param mediaType The media type. - */ - public CharacterRepresentation(MediaType mediaType) { - super(mediaType); - setCharacterSet(CharacterSet.UTF_8); - } - - @Override - public InputStream getStream() throws IOException { - return IoUtils.getStream(getReader(), getCharacterSet()); - } + /** + * Constructor. + * + * @param mediaType The media type. + */ + public CharacterRepresentation(MediaType mediaType) { + super(mediaType); + setCharacterSet(CharacterSet.UTF_8); + } - @Override - public void write(OutputStream outputStream) throws IOException { - Writer writer = IoUtils.getWriter(outputStream, getCharacterSet()); - write(writer); - writer.flush(); - } + @Override + public InputStream getStream() throws IOException { + return IoUtils.getStream(getReader(), getCharacterSet()); + } + @Override + public void write(OutputStream outputStream) throws IOException { + Writer writer = IoUtils.getWriter(outputStream, getCharacterSet()); + write(writer); + writer.flush(); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java index 7b5fb0b4ec..0f4aff1fc7 100644 --- a/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java @@ -1,236 +1,234 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.Context; -import org.restlet.data.Digest; -import org.restlet.engine.io.IoUtils; -import org.restlet.util.WrapperRepresentation; - -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; import java.security.DigestInputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.data.Digest; +import org.restlet.engine.io.IoUtils; +import org.restlet.util.WrapperRepresentation; /** - * Representation capable of computing a digest. It wraps another representation - * and allows to get the computed digest of the wrapped entity after reading or - * writing operations. The final digest value is guaranteed to be correct only - * after the wrapped representation has been entirely exhausted (that is to say - * read or written).
+ * Representation capable of computing a digest. It wraps another representation and allows to get + * the computed digest of the wrapped entity after reading or writing operations. The final digest + * value is guaranteed to be correct only after the wrapped representation has been entirely + * exhausted (meaning read or written).
*
- * This wrapper allows getting the computed digest at the same time the - * representation is read or written. It does not need two separate operations - * which may require specific attention for transient representations. + * This wrapper allows getting the computed digest at the same time the representation is read or + * written. It does not need two separate operations which may require specific attention for + * transient representations. * * @author Jerome Louvel * @see Representation#isTransient() . */ public class DigesterRepresentation extends WrapperRepresentation { - /** The digest algorithm. */ - private final String algorithm; - - /** The computed digest value. */ - private volatile MessageDigest computedDigest; - - /** - * Constructor.
- * By default, the instance relies on the {@link Digest#ALGORITHM_MD5} digest - * algorithm. - * - * @param wrappedRepresentation The wrapped representation. - * @throws NoSuchAlgorithmException - */ - public DigesterRepresentation(Representation wrappedRepresentation) throws NoSuchAlgorithmException { - this(wrappedRepresentation, Digest.ALGORITHM_MD5); - } - - /** - * Constructor.
- * - * @param wrappedRepresentation The wrapped representation. - * @param algorithm The digest algorithm. - * @throws NoSuchAlgorithmException - */ - public DigesterRepresentation(Representation wrappedRepresentation, String algorithm) - throws NoSuchAlgorithmException { - super(wrappedRepresentation); - this.algorithm = algorithm; - this.computedDigest = MessageDigest.getInstance(algorithm); - } - - /** - * Check that the digest computed from the representation content and the digest - * declared by the representation are the same.
- * Since this method relies on the {@link #computeDigest(String)} method, and - * since this method reads entirely the representation's stream, user must take - * care of the content of the representation in case the latter is transient. - * - * @return True if both digests are not null and equals. - */ - public boolean checkDigest() { - Digest digest = getDigest(); - return (digest != null && digest.equals(getComputedDigest())); - } - - /** - * Check that the digest computed from the representation content and the digest - * declared by the representation are the same. It also first checks that the - * algorithms are the same.
- * Since this method relies on the {@link #computeDigest(String)} method, and - * since this method reads entirely the representation's stream, user must take - * care of the content of the representation in case the latter is transient. - * - * @param algorithm The algorithm used to compute the digest to compare with. - * See constant values in {@link org.restlet.data.Digest}. - * @return True if both digests are not null and equals. - */ - public boolean checkDigest(String algorithm) { - boolean result = false; - - if (this.algorithm != null && this.algorithm.equals(algorithm)) { - result = checkDigest(); - } else { - Digest digest = getDigest(); - - if (digest != null) { - if (algorithm.equals(digest.getAlgorithm())) { - result = digest.equals(computeDigest(algorithm)); - } - } - } - - return result; - } - - /** - * Compute the representation digest according to MD5 algorithm.
- * If case this algorithm is the same as the one provided at instantiation, - * the computation operation is made with the current stored computed value and - * does not require to entirely exhaust the representation's stream. - */ - public Digest computeDigest() { - return computeDigest(Digest.ALGORITHM_MD5); - } - - /** - * Compute the representation digest according to the given algorithm.
- * Since this method entirely reads the representation stream, user must take - * care of the content of the representation in case the latter is transient. - * - * @param algorithm The algorithm used to compute the digest. See constant - * values in {@link org.restlet.data.Digest}. - * @return The computed digest or null if the digest cannot be computed. - */ - public Digest computeDigest(String algorithm) { - Digest result = null; - - if (this.algorithm != null && this.algorithm.equals(algorithm)) { - result = getComputedDigest(); - } else if (isAvailable()) { - try { - java.security.MessageDigest md = java.security.MessageDigest.getInstance(algorithm); - try (java.security.DigestInputStream dis = new java.security.DigestInputStream(getStream(), md)) { - org.restlet.engine.io.IoUtils.exhaust(dis); - } - result = new org.restlet.data.Digest(algorithm, md.digest()); - } catch (NoSuchAlgorithmException | IOException e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to check the digest of the representation.", e); - } - } - - return result; - } - - /** - * Exhausts the content of the representation by reading it and silently - * discarding anything read. - * - * @return The number of bytes consumed or -1 if unknown. - */ - @Override - public long exhaust() throws IOException { - long result = -1L; - - if (isAvailable()) { - result = IoUtils.exhaust(getStream()); - } - - return result; - } - - /** - * Returns the current computed digest value of the representation. User must be - * aware that, if the representation has not been entirely read or written, the - * computed digest value may not be accurate. - * - * @return The current computed digest value. - */ - public Digest getComputedDigest() { - return new Digest(this.algorithm, computedDigest.digest()); - } - - @Override - public Reader getReader() throws IOException { - return IoUtils.getReader(getStream(), getCharacterSet()); - } - - /** - * {@inheritDoc}
- * - * The stream of the underlying representation is wrapped with a new instance of - * the {@link DigestInputStream} class, which allows to compute progressively - * the digest value. - */ - @Override - public InputStream getStream() throws IOException { - return new DigestInputStream(getWrappedRepresentation().getStream(), this.computedDigest); - } - - @Override - public String getText() throws IOException { - String result = null; - - if (isAvailable()) { - if (getSize() == 0) { - result = ""; - } else { - java.io.StringWriter sw = new java.io.StringWriter(); - write(sw); - result = sw.toString(); - } - } - - return result; - } - - /** - * {@inheritDoc}
- * - * The output stream is wrapped with a new instance of the - * {@link DigestOutputStream} class, which allows to compute progressively the - * digest value. - */ - @Override - public void write(OutputStream outputStream) throws IOException { - OutputStream dos = new DigestOutputStream(outputStream, this.computedDigest); - getWrappedRepresentation().write(dos); - dos.flush(); - } - - @Override - public void write(Writer writer) throws IOException { - OutputStream os = IoUtils.getStream(writer, getCharacterSet()); - write(os); - os.flush(); - } + /** The digest algorithm. */ + private final String algorithm; + + /** The computed digest value. */ + private volatile MessageDigest computedDigest; + + /** + * Constructor.
+ * By default, the instance relies on the {@link Digest#ALGORITHM_MD5} digest algorithm. + * + * @param wrappedRepresentation The wrapped representation. + * @throws NoSuchAlgorithmException + */ + public DigesterRepresentation(Representation wrappedRepresentation) + throws NoSuchAlgorithmException { + this(wrappedRepresentation, Digest.ALGORITHM_MD5); + } + + /** + * Constructor.
+ * + * @param wrappedRepresentation The wrapped representation. + * @param algorithm The digest algorithm. + * @throws NoSuchAlgorithmException + */ + public DigesterRepresentation(Representation wrappedRepresentation, String algorithm) + throws NoSuchAlgorithmException { + super(wrappedRepresentation); + this.algorithm = algorithm; + this.computedDigest = MessageDigest.getInstance(algorithm); + } + + /** + * Check that the digest computed from the representation content and the digest declared by the + * representation are the same.
+ * Since this method relies on the {@link #computeDigest(String)} method, and since this method + * entirely reads the representation's stream, user must take care of the content of the + * representation in case the latter is transient. + * + * @return True if both digests are not null and equals. + */ + public boolean checkDigest() { + Digest digest = getDigest(); + return (digest != null && digest.equals(getComputedDigest())); + } + + /** + * Check that the digest computed from the representation content and the digest declared by the + * representation are the same. It also first checks that the algorithms are the same.
+ * Since this method relies on the {@link #computeDigest(String)} method, and since this method + * entirely reads the representation's stream, user must take care of the content of the + * representation in case the latter is transient. + * + * @param algorithm The algorithm used to compute the digest to compare with. See constant + * values in {@link org.restlet.data.Digest}. + * @return True if both digests are not null and equals. + */ + public boolean checkDigest(String algorithm) { + boolean result = false; + + if (this.algorithm != null && this.algorithm.equals(algorithm)) { + result = checkDigest(); + } else { + Digest digest = getDigest(); + + if (digest != null) { + if (algorithm.equals(digest.getAlgorithm())) { + result = digest.equals(computeDigest(algorithm)); + } + } + } + + return result; + } + + /** + * Compute the representation digest according to MD5 algorithm.
+ * If case this algorithm is the same as the one provided at instantiation, the computation + * operation is made with the current stored computed value and does not require to entirely + * exhaust the representation's stream. + */ + public Digest computeDigest() { + return computeDigest(Digest.ALGORITHM_MD5); + } + + /** + * Compute the representation digest according to the given algorithm.
+ * Since this method entirely reads the representation stream, the user must take care of the + * content of the representation in case the latter is transient. + * + * @param algorithm The algorithm used to compute the digest. See constant values in {@link + * org.restlet.data.Digest}. + * @return The computed digest or null if the digest cannot be computed. + */ + public Digest computeDigest(String algorithm) { + Digest result = null; + + if (this.algorithm != null && this.algorithm.equals(algorithm)) { + result = getComputedDigest(); + } else if (isAvailable()) { + try { + java.security.MessageDigest md = java.security.MessageDigest.getInstance(algorithm); + try (java.security.DigestInputStream dis = + new java.security.DigestInputStream(getStream(), md)) { + org.restlet.engine.io.IoUtils.exhaust(dis); + } + result = new org.restlet.data.Digest(algorithm, md.digest()); + } catch (NoSuchAlgorithmException | IOException e) { + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to check the digest of the representation.", e); + } + } + + return result; + } + + /** + * Exhausts the content of the representation by reading it and silently discarding anything + * read. + * + * @return The number of bytes consumed or -1 if unknown. + */ + @Override + public long exhaust() throws IOException { + long result = -1L; + + if (isAvailable()) { + result = IoUtils.exhaust(getStream()); + } + + return result; + } + + /** + * Returns the current computed digest value of the representation. User must be aware that, if + * the representation has not been entirely read or written, the computed digest value may not + * be accurate. + * + * @return The current computed digest value. + */ + public Digest getComputedDigest() { + return new Digest(this.algorithm, computedDigest.digest()); + } + + @Override + public Reader getReader() throws IOException { + return IoUtils.getReader(getStream(), getCharacterSet()); + } + + /** + * {@inheritDoc}
+ * The stream of the underlying representation is wrapped with a new instance of the {@link + * DigestInputStream} class, which allows progressively computing the digest value. + */ + @Override + public InputStream getStream() throws IOException { + return new DigestInputStream(getWrappedRepresentation().getStream(), this.computedDigest); + } + + @Override + public String getText() throws IOException { + String result = null; + + if (isAvailable()) { + if (getSize() == 0) { + result = ""; + } else { + java.io.StringWriter sw = new java.io.StringWriter(); + write(sw); + result = sw.toString(); + } + } + + return result; + } + + /** + * {@inheritDoc}
+ * The output stream is wrapped with a new instance of the {@link DigestOutputStream} class, + * which allows progressively computing the digest value. + */ + @Override + public void write(OutputStream outputStream) throws IOException { + OutputStream dos = new DigestOutputStream(outputStream, this.computedDigest); + getWrappedRepresentation().write(dos); + dos.flush(); + } + + @Override + public void write(Writer writer) throws IOException { + OutputStream os = IoUtils.getStream(writer, getCharacterSet()); + write(os); + os.flush(); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java index 21cf0556b8..fef0a44d6f 100644 --- a/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; import java.io.IOException; @@ -15,45 +14,43 @@ import java.io.Reader; /** - * Empty representation with no content. It is always considered available but - * calling the {@link #getText()} method for example will return an empty - * string. It can also have regular metadata available. - * + * Empty representation with no content. It is always considered available but calling the {@link + * #getText()} method for example will return an empty string. It can also have regular metadata + * available. + * * @author Jerome Louvel */ public class EmptyRepresentation extends Representation { - /** - * Constructor. - */ - public EmptyRepresentation() { - setAvailable(false); - setTransient(true); - setSize(0); - } - - @Override - public Reader getReader() throws IOException { - return null; - } - - @Override - public InputStream getStream() throws IOException { - return null; - } - - @Override - public String getText() throws IOException { - return null; - } - - @Override - public void write(java.io.Writer writer) throws IOException { - // Do nothing - } - - @Override - public void write(OutputStream outputStream) throws IOException { - // Do nothing - } + /** Constructor. */ + public EmptyRepresentation() { + setAvailable(false); + setTransient(true); + setSize(0); + } + + @Override + public Reader getReader() throws IOException { + return null; + } + + @Override + public InputStream getStream() throws IOException { + return null; + } + + @Override + public String getText() throws IOException { + return null; + } + + @Override + public void write(java.io.Writer writer) throws IOException { + // Do nothing + } + + @Override + public void write(OutputStream outputStream) throws IOException { + // Do nothing + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java index c543e8d1fc..d031cae78f 100644 --- a/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java @@ -1,215 +1,208 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.Date; import org.restlet.data.Disposition; import org.restlet.data.LocalReference; import org.restlet.data.MediaType; import org.restlet.engine.io.IoUtils; -import java.io.*; -import java.util.Date; - /** - * Representation based on a static file. Note that in order for Web clients to - * display a download box upon reception of a file representation, it needs an - * additional call to {@link Disposition#setType(String)} with a - * {@link Disposition#TYPE_ATTACHMENT} value. - * + * Representation based on a static file. Note that in order for Web clients to display a download + * box upon reception of a file representation, it needs an additional call to {@link + * Disposition#setType(String)} with a {@link Disposition#TYPE_ATTACHMENT} value. + * * @author Jerome Louvel */ public class FileRepresentation extends Representation { - /** - * Creates a new file by detecting if the name is a URI or a simple path name. - * - * @param path The path name or file URI of the represented file (either in - * system format or in 'file:///' format). - * @return The associated File instance. - */ - private static File createFile(String path) { - if (path.startsWith("file://")) { - return new LocalReference(path).getFile(); - } - - return new File(path); - } - - /** - * Indicates if this file should be automatically deleted on release of the - * representation. - */ - private volatile boolean autoDeleting; - - /** The file handle. */ - private volatile File file; - - /** - * Constructor that does not set an expiration date for {@code file} - * - * @param file The represented file. - * @param mediaType The representation's media type. - * @see #FileRepresentation(File, MediaType, int) - */ - public FileRepresentation(File file, MediaType mediaType) { - this(file, mediaType, -1); - } - - /** - * Constructor. If a positive "timeToLive" parameter is given, then the - * expiration date is set accordingly. If "timeToLive" is equal to zero, then - * the expiration date is set to the current date, meaning that it will - * immediately expire on the client. If -1 is given, then no expiration date is - * set. - * - * @param file The represented file. - * @param mediaType The representation's media type. - * @param timeToLive The time to live before it expires (in seconds). - */ - public FileRepresentation(File file, MediaType mediaType, int timeToLive) { - super(mediaType); - this.file = file; - setModificationDate(new Date(file.lastModified())); - - if (timeToLive == 0) { - setExpirationDate(null); - } else if (timeToLive > 0) { - setExpirationDate(new Date(System.currentTimeMillis() + (1000L * timeToLive))); - } - - setMediaType(mediaType); - Disposition disposition = new Disposition(); - disposition.setFilename(file.getName()); - this.setDisposition(disposition); - } - - /** - * Constructor that does not set an expiration date for {@code path} - * - * @param path The path name or file URI of the represented file (either in - * system format or in 'file:///' format). - * @param mediaType The representation's media type. - * @see #FileRepresentation(String, MediaType, int) - */ - public FileRepresentation(String path, MediaType mediaType) { - this(path, mediaType, -1); - } - - /** - * Constructor. - * - * @param path The path name or file URI of the represented file (either - * in system format or in 'file:///' format). - * @param mediaType The representation's media type. - * @param timeToLive The time to live before it expires (in seconds). - * @see java.io.File#File(String) - */ - public FileRepresentation(String path, MediaType mediaType, int timeToLive) { - this(createFile(path), mediaType, timeToLive); - } - - /** - * Returns the file handle. - * - * @return the file handle. - */ - public File getFile() { - return this.file; - } - - @Override - public Reader getReader() throws IOException { - return new FileReader(this.file); - } - - @Override - public long getSize() { - if (super.getSize() != UNKNOWN_SIZE) { - return super.getSize(); - } - - return this.file.length(); - } - - @Override - public FileInputStream getStream() throws IOException { - try { - return new FileInputStream(this.file); - } catch (FileNotFoundException fnfe) { - throw new IOException("Couldn't get the stream. File not found"); - } - } - - /** - * Note that this method relies on {@link #getStream()}. This stream is closed - * once fully read. - */ - @Override - public String getText() throws IOException { - return IoUtils.toString(getStream(), getCharacterSet()); - } - - /** - * Indicates if this file should be automatically deleted on release of the - * representation. - * - * @return True if this file should be automatically deleted on release of the - * representation. - */ - public boolean isAutoDeleting() { - return autoDeleting; - } - - /** - * Releases the file handle. - */ - @Override - public void release() { - if (isAutoDeleting() && getFile() != null) { - try { - IoUtils.delete(getFile(), true); - } catch (Exception e) { - } - } - - setFile(null); - super.release(); - } - - /** - * Indicates if this file should be automatically deleted on release of the - * representation. - * - * @param autoDeleting True if this file should be automatically deleted on - * release of the representation. - */ - public void setAutoDeleting(boolean autoDeleting) { - this.autoDeleting = autoDeleting; - } - - /** - * Sets the file handle. - * - * @param file The file handle. - */ - public void setFile(File file) { - this.file = file; - } - - @Override - public void write(OutputStream outputStream) throws IOException { - IoUtils.copy(getStream(), outputStream); - } - - @Override - public void write(Writer writer) throws IOException { - IoUtils.copy(getReader(), writer); - } - + /** + * Creates a new file by detecting if the name is a URI or a simple path name. + * + * @param path The path name or file URI of the represented file (either in system format or in + * 'file:///' format). + * @return The associated File instance. + */ + private static File createFile(String path) { + if (path.startsWith("file://")) { + return new LocalReference(path).getFile(); + } + + return new File(path); + } + + /** Indicates if this file should be automatically deleted on release of the representation. */ + private volatile boolean autoDeleting; + + /** The file handle. */ + private volatile File file; + + /** + * Constructor that does not set an expiration date for {@code file} + * + * @param file The represented file. + * @param mediaType The representation's media-type. + * @see #FileRepresentation(File, MediaType, int) + */ + public FileRepresentation(File file, MediaType mediaType) { + this(file, mediaType, -1); + } + + /** + * Constructor. If a positive "timeToLive" parameter is given, then the expiration date is set + * accordingly. If "timeToLive" is equal to zero, then the expiration date is set to the current + * date, meaning that it will immediately expire on the client. If -1 is given, then no + * expiration date is set. + * + * @param file The represented file. + * @param mediaType The representation's media-type. + * @param timeToLive The time to live before it expires (in seconds). + */ + public FileRepresentation(File file, MediaType mediaType, int timeToLive) { + super(mediaType); + this.file = file; + setModificationDate(new Date(file.lastModified())); + + if (timeToLive == 0) { + setExpirationDate(null); + } else if (timeToLive > 0) { + setExpirationDate(new Date(System.currentTimeMillis() + (1000L * timeToLive))); + } + + setMediaType(mediaType); + Disposition disposition = new Disposition(); + disposition.setFilename(file.getName()); + this.setDisposition(disposition); + } + + /** + * Constructor that does not set an expiration date for {@code path} + * + * @param path The path name or file URI of the represented file (either in system format or in + * 'file:///' format). + * @param mediaType The representation's media-type. + * @see #FileRepresentation(String, MediaType, int) + */ + public FileRepresentation(String path, MediaType mediaType) { + this(path, mediaType, -1); + } + + /** + * Constructor. + * + * @param path The path name or file URI of the represented file (either in system format or in + * 'file:///' format). + * @param mediaType The representation's media-type. + * @param timeToLive The time to live before it expires (in seconds). + * @see java.io.File#File(String) + */ + public FileRepresentation(String path, MediaType mediaType, int timeToLive) { + this(createFile(path), mediaType, timeToLive); + } + + /** + * Returns the file handle. + * + * @return the file handle. + */ + public File getFile() { + return this.file; + } + + @Override + public Reader getReader() throws IOException { + return new FileReader(this.file); + } + + @Override + public long getSize() { + if (super.getSize() != UNKNOWN_SIZE) { + return super.getSize(); + } + + return this.file.length(); + } + + @Override + public FileInputStream getStream() throws IOException { + try { + return new FileInputStream(this.file); + } catch (FileNotFoundException fnfe) { + throw new IOException("Couldn't get the stream. File not found"); + } + } + + /** + * Note that this method relies on {@link #getStream()}. This stream is closed once fully read. + */ + @Override + public String getText() throws IOException { + return IoUtils.toString(getStream(), getCharacterSet()); + } + + /** + * Indicates if this file should be automatically deleted on release of the representation. + * + * @return True if this file should be automatically deleted on release of the representation. + */ + public boolean isAutoDeleting() { + return autoDeleting; + } + + /** Releases the file handle. */ + @Override + public void release() { + if (isAutoDeleting() && getFile() != null) { + try { + IoUtils.delete(getFile(), true); + } catch (Exception ignored) { + } + } + + setFile(null); + super.release(); + } + + /** + * Indicates if this file should be automatically deleted on release of the representation. + * + * @param autoDeleting True if this file should be automatically deleted on release of the + * representation. + */ + public void setAutoDeleting(boolean autoDeleting) { + this.autoDeleting = autoDeleting; + } + + /** + * Sets the file handle. + * + * @param file The file handle. + */ + public void setFile(File file) { + this.file = file; + } + + @Override + public void write(OutputStream outputStream) throws IOException { + IoUtils.copy(getStream(), outputStream); + } + + @Override + public void write(Writer writer) throws IOException { + IoUtils.copy(getReader(), writer); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java index d23adf96a2..c1be206e85 100644 --- a/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java @@ -1,22 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.Context; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; /** * Transient representation based on a BIO input stream. @@ -25,89 +23,86 @@ */ public class InputRepresentation extends StreamRepresentation { - /** The representation's stream. */ - private volatile InputStream stream; - - /** - * Constructor. - * - * @param inputStream The representation's stream. - */ - public InputRepresentation(InputStream inputStream) { - this(inputStream, null); - } - - /** - * Constructor. - * - * @param inputStream The representation's stream. - * @param mediaType The representation's media type. - */ - public InputRepresentation(InputStream inputStream, MediaType mediaType) { - this(inputStream, mediaType, UNKNOWN_SIZE); - } - - /** - * Constructor. - * - * @param inputStream The representation's stream. - * @param mediaType The representation's media type. - * @param expectedSize The expected input stream size. - */ - public InputRepresentation(InputStream inputStream, MediaType mediaType, long expectedSize) { - super(mediaType); - setSize(expectedSize); - setTransient(true); - setStream(inputStream); - } - - @Override - public InputStream getStream() throws IOException { - final InputStream result = this.stream; - setStream(null); - return result; - } - - /** - * Note that this method relies on {@link #getStream()}. This stream is closed - * once fully read. - */ - @Override - public String getText() throws IOException { - return IoUtils.toString(getStream(), getCharacterSet()); - } - - /** - * Closes and releases the input stream. - */ - @Override - public void release() { - if (this.stream != null) { - try { - this.stream.close(); - } catch (IOException e) { - Context.getCurrentLogger().log(Level.WARNING, "Error while releasing the representation.", e); - } - - this.stream = null; - } - - super.release(); - } - - /** - * Sets the input stream to use. - * - * @param stream The input stream to use. - */ - public void setStream(InputStream stream) { - this.stream = stream; - setAvailable(stream != null); - } - - @Override - public void write(OutputStream outputStream) throws IOException { - IoUtils.copy(getStream(), outputStream); - } - + /** The representation's stream. */ + private volatile InputStream stream; + + /** + * Constructor. + * + * @param inputStream The representation's stream. + */ + public InputRepresentation(InputStream inputStream) { + this(inputStream, null); + } + + /** + * Constructor. + * + * @param inputStream The representation's stream. + * @param mediaType The representation's media-type. + */ + public InputRepresentation(InputStream inputStream, MediaType mediaType) { + this(inputStream, mediaType, UNKNOWN_SIZE); + } + + /** + * Constructor. + * + * @param inputStream The representation's stream. + * @param mediaType The representation's media-type. + * @param expectedSize The expected input stream size. + */ + public InputRepresentation(InputStream inputStream, MediaType mediaType, long expectedSize) { + super(mediaType); + setSize(expectedSize); + setTransient(true); + setStream(inputStream); + } + + @Override + public InputStream getStream() throws IOException { + final InputStream result = this.stream; + setStream(null); + return result; + } + + /** + * Note that this method relies on {@link #getStream()}. This stream is closed once fully read. + */ + @Override + public String getText() throws IOException { + return IoUtils.toString(getStream(), getCharacterSet()); + } + + /** Closes and releases the input stream. */ + @Override + public void release() { + if (this.stream != null) { + try { + this.stream.close(); + } catch (IOException e) { + Context.getCurrentLogger() + .log(Level.WARNING, "Error while releasing the representation.", e); + } + + this.stream = null; + } + + super.release(); + } + + /** + * Sets the input stream to use. + * + * @param stream The input stream to use. + */ + public void setStream(InputStream stream) { + this.stream = stream; + setAvailable(stream != null); + } + + @Override + public void write(OutputStream outputStream) throws IOException { + IoUtils.copy(getStream(), outputStream); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java index 890b33b1df..9969fd1399 100644 --- a/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; import java.io.IOException; @@ -15,7 +14,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; - import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.MultiPart; import org.eclipse.jetty.http.MultiPart.Part; @@ -28,35 +26,36 @@ import org.restlet.data.MediaType; /** - * Input representation that can either parse or generate a multipart form data - * representation depending on which constructor is invoked. - * + * Input representation that can either parse or generate a multipart form data representation + * depending on which constructor is invoked. + * * @author Jerome Louvel */ public class MultiPartRepresentation extends InputRepresentation { /** - * Creates a #{@link Part} object based on a {@link Representation} plus - * metadata. - * - * @param name The name of the part. - * @param fileName The client suggests file name for storing the part. + * Creates a #{@link Part} object based on a {@link Representation} plus metadata. + * + * @param name The name of the part. + * @param fileName The client's suggested file name for storing the part. * @param partContent The part content. * @return The Jetty #{@link Part} object created. * @throws IOException */ - public static Part createPart(String name, String fileName, - Representation partContent) throws IOException { - return new MultiPart.ContentSourcePart(name, fileName, HttpFields.EMPTY, + public static Part createPart(String name, String fileName, Representation partContent) + throws IOException { + return new MultiPart.ContentSourcePart( + name, + fileName, + HttpFields.EMPTY, new InputStreamContentSource(partContent.getStream())); } /** - * Returns the value of the first media-type parameter with "boundary" name. - * - * @param mediaType The media type that might contain a "boundary" - * parameter. - * @return The value of the first media-type parameter with "boundary" name. + * Returns the value of the first media-type parameter with the "boundary" name. + * + * @param mediaType The media type that might contain a "boundary" parameter. + * @return The value of the first media-type parameter with the "boundary" name. */ public static String getBoundary(MediaType mediaType) { final String result; @@ -71,11 +70,11 @@ public static String getBoundary(MediaType mediaType) { } /** - * Sets a boundary to an existing media type. If the original mediatype - * already has a "boundary" parameter, it will be erased. * - * + * Sets a boundary to an existing media type. If the original media-type already has a + * "boundary" parameter, it will be erased. * + * * @param mediaType The media type to update. - * @param boundary The boundary to add as a parameter. + * @param boundary The boundary to add as a parameter. * @return The updated media type. */ public static MediaType setBoundary(MediaType mediaType, String boundary) { @@ -83,8 +82,7 @@ public static MediaType setBoundary(MediaType mediaType, String boundary) { if (mediaType != null) { if (mediaType.getParameters().getFirst("boundary") != null) { - result = new MediaType(mediaType.getParent(), "boundary", - boundary); + result = new MediaType(mediaType.getParent(), "boundary", boundary); } else { result = new MediaType(mediaType, "boundary", boundary); } @@ -93,19 +91,16 @@ public static MediaType setBoundary(MediaType mediaType, String boundary) { return result; } - /** - * The boundary used to separate each part for the parsed or generated form. - */ + /** The boundary used to separate each part for the parsed or generated form. */ private volatile String boundary; /** The wrapped multipart form data either parsed or to be generated. */ private volatile List parts; /** - * Constructor that wraps multiple parts, set a random boundary, then - * GENERATES the content via {@link #getStream()} as a - * {@link MediaType#MULTIPART_FORM_DATA}. - * + * Constructor that wraps multiple parts, set a random boundary, then GENERATES the content via + * {@link #getStream()} as a {@link MediaType#MULTIPART_FORM_DATA}. + * * @param parts The source parts to use when generating the representation. */ public MultiPartRepresentation(List parts) { @@ -113,27 +108,23 @@ public MultiPartRepresentation(List parts) { } /** - * Constructor that wraps multiple parts, set a media type with a boundary, - * then GENERATES the content via {@link #getStream()} as a - * {@link MediaType#MULTIPART_FORM_DATA}. - * + * Constructor that wraps multiple parts, set a media type with a boundary, then GENERATES the + * content via {@link #getStream()} as a {@link MediaType#MULTIPART_FORM_DATA}. + * * @param mediaType The media type to set. - * @param boundary The boundary to add as a parameter. - * @param parts The source parts to use when generating the - * representation. + * @param boundary The boundary to add as a parameter. + * @param parts The source parts to use when generating the representation. */ - public MultiPartRepresentation(MediaType mediaType, String boundary, - List parts) { + public MultiPartRepresentation(MediaType mediaType, String boundary, List parts) { super(null, setBoundary(mediaType, boundary)); this.boundary = boundary; this.parts = parts; } /** - * Constructor that wraps multiple parts, set a random boundary, then - * GENERATES the content via {@link #getStream()} as a - * {@link MediaType#MULTIPART_FORM_DATA}. - * + * Constructor that wraps multiple parts, set a random boundary, then GENERATES the content via + * {@link #getStream()} as a {@link MediaType#MULTIPART_FORM_DATA}. + * * @param parts The source parts to use when generating the representation. */ public MultiPartRepresentation(Part... parts) { @@ -141,69 +132,60 @@ public MultiPartRepresentation(Part... parts) { } /** - * Constructor that PARSES the content based on a given configuration into - * {@link #getParts()}. - * - * @param multiPartEntity The multipart entity to parse which should have a - * media type based on - * {@link MediaType#MULTIPART_FORM_DATA}, with a - * "boundary" parameter. - * @param config The multipart configuration. + * Constructor that PARSES the content based on a given configuration into {@link #getParts()}. + * + * @param multiPartEntity The multipart entity to parse which should have a media type based on + * {@link MediaType#MULTIPART_FORM_DATA}, with a "boundary" parameter. + * @param config The multipart configuration. * @throws IOException */ - public MultiPartRepresentation(Representation multiPartEntity, - MultiPartConfig config) throws IOException { - this(multiPartEntity.getMediaType(), multiPartEntity.getStream(), - config); + public MultiPartRepresentation(Representation multiPartEntity, MultiPartConfig config) + throws IOException { + this(multiPartEntity.getMediaType(), multiPartEntity.getStream(), config); } /** - * Constructor that PARSES the content based on a given configuration into - * {@link #getParts()}. Uses a default {@link MultiPartConfig}. - * - * @param multiPartEntity The multipart entity to parse which should have a - * media type based on - * {@link MediaType#MULTIPART_FORM_DATA}, with a - * "boundary" parameter. - * @param storageLocation The location where parsed files are stored for - * easier access. + * Constructor that PARSES the content based on a given configuration into {@link #getParts()}. + * Uses a default {@link MultiPartConfig}. + * + * @param multiPartEntity The multipart entity to parse which should have a media type based on + * {@link MediaType#MULTIPART_FORM_DATA}, with a "boundary" parameter. + * @param storageLocation The location where parsed files are stored for easier access. * @throws IOException */ - public MultiPartRepresentation(Representation multiPartEntity, - Path storageLocation) throws IOException { - this(multiPartEntity, new MultiPartConfig.Builder() - .location(storageLocation).build()); + public MultiPartRepresentation(Representation multiPartEntity, Path storageLocation) + throws IOException { + this(multiPartEntity, new MultiPartConfig.Builder().location(storageLocation).build()); } /** - * Constructor that PARSES the content based on a given configuration into - * {@link #getParts()}. - * - * @param mediaType The media type that should be based on - * {@link MediaType#MULTIPART_FORM_DATA}, with a - * "boundary" parameter. + * Constructor that PARSES the content based on a given configuration into {@link #getParts()}. + * + * @param mediaType The media type that should be based on {@link + * MediaType#MULTIPART_FORM_DATA}, with a "boundary" parameter. * @param multiPartEntity The multipart entity to parse. - * @param config The multipart configuration. + * @param config The multipart configuration. * @throws IOException */ - public MultiPartRepresentation(MediaType mediaType, - InputStream multiPartEntity, MultiPartConfig config) + public MultiPartRepresentation( + MediaType mediaType, InputStream multiPartEntity, MultiPartConfig config) throws IOException { super(null, mediaType); if (MediaType.MULTIPART_FORM_DATA.equals(getMediaType(), true)) { - this.boundary = getMediaType().getParameters() - .getFirstValue("boundary"); + this.boundary = getMediaType().getParameters().getFirstValue("boundary"); if (this.boundary != null) { if (multiPartEntity != null) { - Content.Source contentSource = Content.Source - .from(multiPartEntity); + Content.Source contentSource = Content.Source.from(multiPartEntity); Attributes.Mapped attributes = new Attributes.Mapped(); // Convert the request content into parts. - MultiPartFormData.onParts(contentSource, attributes, - mediaType.toString(), config, + MultiPartFormData.onParts( + contentSource, + attributes, + mediaType.toString(), + config, new Promise.Invocable<>() { @Override public void failed(Throwable failure) { @@ -218,18 +200,18 @@ public InvocationType getInvocationType() { } @Override - public void succeeded( - MultiPartFormData.Parts parts) { + public void succeeded(MultiPartFormData.Parts parts) { // Store the resulting parts MultiPartRepresentation.this.parts = new ArrayList<>(); - parts.iterator().forEachRemaining( - part -> MultiPartRepresentation.this.parts - .add(part)); + parts.iterator() + .forEachRemaining( + part -> + MultiPartRepresentation.this.parts.add( + part)); } }); } else { - throw new IllegalArgumentException( - "The multipart entity can't be null"); + throw new IllegalArgumentException("The multipart entity can't be null"); } } else { throw new IllegalArgumentException( @@ -242,23 +224,20 @@ public void succeeded( } /** - * Constructor that wraps multiple parts, set a boundary, then GENERATES the - * content via {@link #getStream()} as a - * {@link MediaType#MULTIPART_FORM_DATA}. - * + * Constructor that wraps multiple parts, set a boundary, then GENERATES the content via {@link + * #getStream()} as a {@link MediaType#MULTIPART_FORM_DATA}. + * * @param boundary The boundary to add as a parameter. - * @param parts The source parts to use when generating the - * representation. + * @param parts The source parts to use when generating the representation. */ public MultiPartRepresentation(String boundary, List parts) { this(MediaType.MULTIPART_FORM_DATA, boundary, parts); } /** - * Constructor that wraps multiple parts, set a boundary, then GENERATES the - * content via {@link #getStream()} as a - * {@link MediaType#MULTIPART_FORM_DATA}. - * + * Constructor that wraps multiple parts, set a boundary, then GENERATES the content via {@link + * #getStream()} as a {@link MediaType#MULTIPART_FORM_DATA}. + * * @param parts The source parts to use when generating the representation. */ public MultiPartRepresentation(String boundary, Part... parts) { @@ -266,11 +245,9 @@ public MultiPartRepresentation(String boundary, Part... parts) { } /** - * Returns the boundary used to separate each part for the parsed or - * generated form. - * - * @return The boundary used to separate each part for the parsed or - * generated form. + * Returns the boundary used to separate each part for the parsed or generated form. + * + * @return The boundary used to separate each part for the parsed or generated form. */ public String getBoundary() { return boundary; @@ -286,10 +263,9 @@ public List getParts() { } /** - * Returns an input stream that generates the multipart form data - * serialization for the wrapped {@link #getParts()} object. The "boundary" - * must be non-null when invoking this method. - * + * Returns an input stream that generates the multipart form data serialization for the wrapped + * {@link #getParts()} object. The "boundary" must be non-null when invoking this method. + * * @return An input stream that generates the multipart form data. */ @Override @@ -298,8 +274,8 @@ public InputStream getStream() throws IOException { throw new IllegalArgumentException("The boundary can't be null"); } - MultiPartFormData.ContentSource content = new MultiPartFormData.ContentSource( - getBoundary()); + MultiPartFormData.ContentSource content = + new MultiPartFormData.ContentSource(getBoundary()); for (Part part : this.parts) { content.addPart(part); @@ -311,21 +287,18 @@ public InputStream getStream() throws IOException { } /** - * Sets the boundary used to separate each part for the parsed or generated - * form. It will also update the {@link MediaType}'s "boundary" attribute. - * - * @param boundary The boundary used to separate each part for the parsed or - * generated form. + * Sets the boundary used to separate each part for the parsed or generated form. It will also + * update the {@link MediaType}'s "boundary" attribute. + * + * @param boundary The boundary used to separate each part for the parsed or generated form. */ public void setBoundary(String boundary) { this.boundary = boundary; if (getMediaType() == null) { - setMediaType(new MediaType(MediaType.MULTIPART_FORM_DATA, - "boundary", boundary)); + setMediaType(new MediaType(MediaType.MULTIPART_FORM_DATA, "boundary", boundary)); } else { setMediaType(setBoundary(getMediaType(), boundary)); } } - } diff --git a/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java index fa55b389d9..5d90361eb3 100644 --- a/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java @@ -1,249 +1,254 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; import org.restlet.data.MediaType; -import java.io.*; - /** * Representation based on a serializable Java object.
- * It supports binary representations of JavaBeans using the - * {@link ObjectInputStream} and {@link ObjectOutputStream} classes. In this - * case, it handles representations having the following media type: - * {@link MediaType#APPLICATION_JAVA_OBJECT} - * ("application/x-java-serialized-object"). It also supports textual - * representations of JavaBeans using the {@link java.beans.XMLEncoder} and - * {@link java.beans.XMLDecoder} classes. In this case, it handles - * representations having the following media type: - * {@link MediaType#APPLICATION_JAVA_OBJECT_XML} + * It supports binary representations of JavaBeans using the {@link ObjectInputStream} and {@link + * ObjectOutputStream} classes. In this case, it handles representations having the following media + * type: {@link MediaType#APPLICATION_JAVA_OBJECT} ("application/x-java-serialized-object"). It also + * supports textual representations of JavaBeans using the {@link java.beans.XMLEncoder} and {@link + * java.beans.XMLDecoder} classes. In this case, it handles representations having the following + * media type: {@link MediaType#APPLICATION_JAVA_OBJECT_XML} * ("application/x-java-serialized-object+xml").
*
- * SECURITY WARNING: The usage of {@link java.beans.XMLDecoder} when - * deserializing XML presentations from untrusted sources can lead to malicious - * attacks. As pointed here, the {@link java.beans.XMLDecoder} is able to force the JVM to - * execute unwanted Java code described inside the XML file. Thus, the support - * of such format has been disabled by default. You can activate this support by - * turning on the following system property: + * SECURITY WARNING: The usage of {@link java.beans.XMLDecoder} when deserializing XML presentations + * from untrusted sources can lead to malicious attacks. As pointed here, the + * {@link java.beans.XMLDecoder} is able to force the JVM to execute unwanted Java code described + * inside the XML file. Thus, the support of such a format has been disabled by default. You can + * activate this support by turning on the following system property: * org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED.
*
- * SECURITY WARNING: The usage of {@link ObjectInputStream} when deserializing - * binary presentations from untrusted sources can lead to malicious attacks. As - * pointed - * here, the {@link ObjectInputStream} is able to force the JVM to execute - * unwanted Java code. Thus, the support of such format has been disabled by - * default. You can activate this support by turning on the following system - * property: "org.restlet.representation.ObjectRepresentation + * SECURITY WARNING: The usage of {@link ObjectInputStream} when deserializing binary presentations + * from untrusted sources can lead to malicious attacks. As pointed here, the {@link + * ObjectInputStream} is able to force the JVM to execute unwanted Java code. Thus, the support of + * such format has been disabled by default. You can activate this support by turning on the + * following system property: "org.restlet.representation.ObjectRepresentation * .VARIANT_OBJECT_BINARY_SUPPORTED". - * + * * @author Jerome Louvel * @param The class to serialize, see {@link Serializable} */ public class ObjectRepresentation extends OutputRepresentation { - /** Indicates whether the JavaBeans XML deserialization is supported or not. */ - public static boolean VARIANT_OBJECT_XML_SUPPORTED = Boolean - .getBoolean("org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED"); - - /** - * Indicates whether the JavaBeans binary deserialization is supported or not. - */ - public static boolean VARIANT_OBJECT_BINARY_SUPPORTED = Boolean - .getBoolean("org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED"); - - /** The serializable object. */ - private volatile T object; - - /** - * Constructor reading the object from a serialized representation. This - * representation must have the proper media type: - * "application/x-java-serialized-object". - * - * @param serializedRepresentation The serialized representation. - * @throws IOException - * @throws ClassNotFoundException - * @throws IllegalArgumentException - */ - public ObjectRepresentation(Representation serializedRepresentation) - throws IOException, ClassNotFoundException, IllegalArgumentException { - this(serializedRepresentation, null); - } - - /** - * Constructor reading the object from a serialized representation. This - * representation must have the proper media type: - * "application/x-java-serialized-object". - * - * @param serializedRepresentation The serialized representation. - * @param classLoader The class loader used to read the object. - * @throws IOException - * @throws ClassNotFoundException - * @throws IllegalArgumentException - */ - public ObjectRepresentation(Representation serializedRepresentation, final ClassLoader classLoader) - throws IOException, ClassNotFoundException, IllegalArgumentException { - this(serializedRepresentation, classLoader, VARIANT_OBJECT_BINARY_SUPPORTED, VARIANT_OBJECT_XML_SUPPORTED); - } - - /** - * Constructor reading the object from a serialized representation. This - * representation must have the proper media type: - * "application/x-java-serialized-object". - * - * @param serializedRepresentation The serialized representation. - * @param classLoader The class loader used to read the object. - * @param variantObjectBinarySupported Indicates whether the JavaBeans binary - * deserialization is supported or not. - * @param variantObjectXmlSupported Indicates whether the JavaBeans XML - * deserialization is supported or not. - * @throws IOException - * @throws ClassNotFoundException - * @throws IllegalArgumentException - */ - @SuppressWarnings("unchecked") - public ObjectRepresentation(Representation serializedRepresentation, final ClassLoader classLoader, - boolean variantObjectBinarySupported, boolean variantObjectXmlSupported) - throws IOException, ClassNotFoundException, IllegalArgumentException { - super(MediaType.APPLICATION_JAVA_OBJECT); - - if (MediaType.APPLICATION_JAVA_OBJECT.equals(serializedRepresentation.getMediaType())) { - if (!variantObjectBinarySupported) { - throw new IllegalArgumentException("SECURITY WARNING: The usage of ObjectInputStream when " - + "deserializing binary representations from unstrusted " - + "sources can lead to malicious attacks. As pointed " - + "here (https://github.com/restlet/restlet-framework-java/issues/778), " - + "the ObjectInputStream class is able to force the JVM to execute unwanted " - + "Java code. Thus, the support of such format has been disactivated " - + "by default. You can activate this support by turning on the following system property: " - + "org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED."); - } - setMediaType(MediaType.APPLICATION_JAVA_OBJECT); - InputStream is = serializedRepresentation.getStream(); - ObjectInputStream ois = null; - if (classLoader != null) { - ois = new ObjectInputStream(is) { - @Override - protected Class resolveClass(java.io.ObjectStreamClass desc) - throws java.io.IOException, java.lang.ClassNotFoundException { - return Class.forName(desc.getName(), false, classLoader); - } - }; - } else { - ois = new ObjectInputStream(is); - } - - this.object = (T) ois.readObject(); - - if (is.read() != -1) { - throw new IOException("The input stream has not been fully read."); - } - - ois.close(); - } else if (MediaType.APPLICATION_JAVA_OBJECT_XML.equals(serializedRepresentation.getMediaType())) { - if (!variantObjectXmlSupported) { - throw new IllegalArgumentException("SECURITY WARNING: The usage of XMLDecoder when " - + "deserializing XML representations from unstrusted " - + "sources can lead to malicious attacks. As pointed " - + "here (http://blog.diniscruz.com/2013/08/using-xmldecoder-to-execute-server-side.html), " - + "the XMLDecoder class is able to force the JVM to " - + "execute unwanted Java code described inside the XML " - + "file. Thus, the support of such format has been " - + "disabled by default. You can activate this " - + "support by turning on the following system property: " - + "org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED."); - } - setMediaType(MediaType.APPLICATION_JAVA_OBJECT_XML); - InputStream is = serializedRepresentation.getStream(); - java.beans.XMLDecoder decoder = new java.beans.XMLDecoder(is); - this.object = (T) decoder.readObject(); - - if (is.read() != -1) { - decoder.close(); - throw new IOException("The input stream has not been fully read."); - } - - decoder.close(); - } else { - throw new IllegalArgumentException("The serialized representation must have this media type: " - + MediaType.APPLICATION_JAVA_OBJECT.toString() + " or this one: " - + MediaType.APPLICATION_JAVA_OBJECT_XML.toString()); - } - } - - /** - * Constructor for the {@link MediaType#APPLICATION_JAVA_OBJECT} type. - * - * @param object The serializable object. - */ - public ObjectRepresentation(T object) { - super(MediaType.APPLICATION_JAVA_OBJECT); - this.object = object; - } - - /** - * Constructor for either the {@link MediaType#APPLICATION_JAVA_OBJECT} type or - * the {@link MediaType#APPLICATION_XML} type. In the first case, the Java - * Object Serialization mechanism is used, based on {@link ObjectOutputStream}. - * In the latter case, the JavaBeans XML serialization is used, based on - * {@link java.beans.XMLEncoder}. - * - * @param object The serializable object. - * @param mediaType The media type. - */ - public ObjectRepresentation(T object, MediaType mediaType) { - super((mediaType == null) ? MediaType.APPLICATION_JAVA_OBJECT : mediaType); - this.object = object; - } - - /** - * Returns the represented object. - * - * @return The represented object. - * @throws IOException - */ - public T getObject() throws IOException { - return this.object; - } - - /** - * Releases the represented object. - */ - @Override - public void release() { - setObject(null); - super.release(); - } - - /** - * Sets the represented object. - * - * @param object The represented object. - */ - public void setObject(T object) { - this.object = object; - } - - @Override - public void write(OutputStream outputStream) throws IOException { - if (MediaType.APPLICATION_JAVA_OBJECT.isCompatible(getMediaType())) { - ObjectOutputStream oos = new ObjectOutputStream(outputStream); - oos.writeObject(getObject()); - oos.flush(); - } else if (MediaType.APPLICATION_JAVA_OBJECT_XML.isCompatible(getMediaType())) { - java.beans.XMLEncoder encoder = new java.beans.XMLEncoder(outputStream); - encoder.writeObject(getObject()); - encoder.close(); - } - } - + /** Indicates whether the JavaBeans XML deserialization is supported or not. */ + public static boolean VARIANT_OBJECT_XML_SUPPORTED = + Boolean.getBoolean( + "org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED"); + + /** Indicates whether the JavaBeans binary deserialization is supported or not. */ + public static boolean VARIANT_OBJECT_BINARY_SUPPORTED = + Boolean.getBoolean( + "org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED"); + + /** The serializable object. */ + private volatile T object; + + /** + * Constructor reading the object from a serialized representation. This representation must + * have the proper media type: "application/x-java-serialized-object". + * + * @param serializedRepresentation The serialized representation. + * @throws IOException + * @throws ClassNotFoundException + * @throws IllegalArgumentException + */ + public ObjectRepresentation(Representation serializedRepresentation) + throws IOException, ClassNotFoundException, IllegalArgumentException { + this(serializedRepresentation, null); + } + + /** + * Constructor reading the object from a serialized representation. This representation must + * have the proper media type: "application/x-java-serialized-object". + * + * @param serializedRepresentation The serialized representation. + * @param classLoader The class loader used to read the object. + * @throws IOException + * @throws ClassNotFoundException + * @throws IllegalArgumentException + */ + public ObjectRepresentation( + Representation serializedRepresentation, final ClassLoader classLoader) + throws IOException, ClassNotFoundException, IllegalArgumentException { + this( + serializedRepresentation, + classLoader, + VARIANT_OBJECT_BINARY_SUPPORTED, + VARIANT_OBJECT_XML_SUPPORTED); + } + + /** + * Constructor reading the object from a serialized representation. This representation must + * have the proper media type: "application/x-java-serialized-object". + * + * @param serializedRepresentation The serialized representation. + * @param classLoader The class loader used to read the object. + * @param variantObjectBinarySupported Indicates whether the JavaBeans binary deserialization is + * supported or not. + * @param variantObjectXmlSupported Indicates whether the JavaBeans XML deserialization is + * supported or not. + * @throws IOException + * @throws ClassNotFoundException + * @throws IllegalArgumentException + */ + @SuppressWarnings("unchecked") + public ObjectRepresentation( + Representation serializedRepresentation, + final ClassLoader classLoader, + boolean variantObjectBinarySupported, + boolean variantObjectXmlSupported) + throws IOException, ClassNotFoundException, IllegalArgumentException { + super(MediaType.APPLICATION_JAVA_OBJECT); + + if (MediaType.APPLICATION_JAVA_OBJECT.equals(serializedRepresentation.getMediaType())) { + if (!variantObjectBinarySupported) { + throw new IllegalArgumentException( + "SECURITY WARNING: The usage of ObjectInputStream when " + + "deserializing binary representations from unstrusted " + + "sources can lead to malicious attacks. As pointed " + + "here (https://github.com/restlet/restlet-framework-java/issues/778), " + + "the ObjectInputStream class is able to force the JVM to execute unwanted " + + "Java code. Thus, the support of such format has been disactivated " + + "by default. You can activate this support by turning on the following system property: " + + "org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED."); + } + setMediaType(MediaType.APPLICATION_JAVA_OBJECT); + + final InputStream is = serializedRepresentation.getStream(); + + final ObjectInputStream ois; + if (classLoader != null) { + ois = + new ObjectInputStream(is) { + @Override + protected Class resolveClass(java.io.ObjectStreamClass desc) + throws java.io.IOException, java.lang.ClassNotFoundException { + return Class.forName(desc.getName(), false, classLoader); + } + }; + } else { + ois = new ObjectInputStream(is); + } + + this.object = (T) ois.readObject(); + + if (is.read() != -1) { + throw new IOException("The input stream has not been fully read."); + } + + ois.close(); + } else if (MediaType.APPLICATION_JAVA_OBJECT_XML.equals( + serializedRepresentation.getMediaType())) { + if (!variantObjectXmlSupported) { + throw new IllegalArgumentException( + "SECURITY WARNING: The usage of XMLDecoder when " + + "deserializing XML representations from unstrusted " + + "sources can lead to malicious attacks. As pointed " + + "here (http://blog.diniscruz.com/2013/08/using-xmldecoder-to-execute-server-side.html), " + + "the XMLDecoder class is able to force the JVM to " + + "execute unwanted Java code described inside the XML " + + "file. Thus, the support of such format has been " + + "disabled by default. You can activate this " + + "support by turning on the following system property: " + + "org.restlet.representation.ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED."); + } + setMediaType(MediaType.APPLICATION_JAVA_OBJECT_XML); + InputStream is = serializedRepresentation.getStream(); + java.beans.XMLDecoder decoder = new java.beans.XMLDecoder(is); + this.object = (T) decoder.readObject(); + + if (is.read() != -1) { + decoder.close(); + throw new IOException("The input stream has not been fully read."); + } + + decoder.close(); + } else { + throw new IllegalArgumentException( + "The serialized representation must have this media type: " + + MediaType.APPLICATION_JAVA_OBJECT.toString() + + " or this one: " + + MediaType.APPLICATION_JAVA_OBJECT_XML.toString()); + } + } + + /** + * Constructor for the {@link MediaType#APPLICATION_JAVA_OBJECT} type. + * + * @param object The serializable object. + */ + public ObjectRepresentation(T object) { + super(MediaType.APPLICATION_JAVA_OBJECT); + this.object = object; + } + + /** + * Constructor for either the {@link MediaType#APPLICATION_JAVA_OBJECT} type or the {@link + * MediaType#APPLICATION_XML} type. In the first case, the Java Object Serialization mechanism + * is used, based on {@link ObjectOutputStream}. In the latter case, the JavaBeans XML + * serialization is used, based on {@link java.beans.XMLEncoder}. + * + * @param object The serializable object. + * @param mediaType The media type. + */ + public ObjectRepresentation(T object, MediaType mediaType) { + super((mediaType == null) ? MediaType.APPLICATION_JAVA_OBJECT : mediaType); + this.object = object; + } + + /** + * Returns the represented object. + * + * @return The represented object. + * @throws IOException + */ + public T getObject() throws IOException { + return this.object; + } + + /** Releases the represented object. */ + @Override + public void release() { + setObject(null); + super.release(); + } + + /** + * Sets the represented object. + * + * @param object The represented object. + */ + public void setObject(T object) { + this.object = object; + } + + @Override + public void write(OutputStream outputStream) throws IOException { + if (MediaType.APPLICATION_JAVA_OBJECT.isCompatible(getMediaType())) { + ObjectOutputStream oos = new ObjectOutputStream(outputStream); + oos.writeObject(getObject()); + oos.flush(); + } else if (MediaType.APPLICATION_JAVA_OBJECT_XML.isCompatible(getMediaType())) { + java.beans.XMLEncoder encoder = new java.beans.XMLEncoder(outputStream); + encoder.writeObject(getObject()); + encoder.close(); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java index 3e2f18e8b1..af500de364 100644 --- a/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java @@ -1,61 +1,57 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; - import java.io.IOException; import java.io.InputStream; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; /** - * Representation based on a BIO output stream. This class is a good basis to - * write your own representations, especially for the dynamic and large - * ones.
+ * Representation based on a BIO output stream. This class is a good basis to write your own + * representations, especially for the dynamic and large ones.
*
* For this you just need to create a subclass and override the abstract - * Representation.write(OutputStream) method. This method will later be called - * back by the connectors when the actual representation's content is needed. - * + * Representation.write(OutputStream) method. This method will later be called back by the + * connectors when the actual representation's content is needed. + * * @author Jerome Louvel */ public abstract class OutputRepresentation extends StreamRepresentation { - /** - * Constructor. - * - * @param mediaType The representation's mediaType. - */ - public OutputRepresentation(MediaType mediaType) { - super(mediaType); - } - - /** - * Constructor. - * - * @param mediaType The representation's mediaType. - * @param expectedSize The expected input stream size. - */ - public OutputRepresentation(MediaType mediaType, long expectedSize) { - super(mediaType); - setSize(expectedSize); - } + /** + * Constructor. + * + * @param mediaType The representation's mediaType. + */ + public OutputRepresentation(MediaType mediaType) { + super(mediaType); + } - /** - * Returns a stream with the representation's content. Internally, it uses a - * writer thread and a pipe stream. - * - * @return A stream with the representation's content. - */ - @Override - public InputStream getStream() throws IOException { - return IoUtils.getStream(this); - } + /** + * Constructor. + * + * @param mediaType The representation's mediaType. + * @param expectedSize The expected input stream size. + */ + public OutputRepresentation(MediaType mediaType, long expectedSize) { + super(mediaType); + setSize(expectedSize); + } + /** + * Returns a stream with the representation's content. Internally, it uses a writer thread and a + * pipe stream. + * + * @return A stream with the representation's content. + */ + @Override + public InputStream getStream() throws IOException { + return IoUtils.getStream(this); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java index 89029f5a4d..7980dcba30 100644 --- a/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java @@ -1,112 +1,108 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.Context; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; - import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; /** * Transient representation based on a BIO characters reader. - * + * * @author Jerome Louvel */ public class ReaderRepresentation extends CharacterRepresentation { - /** The representation's reader. */ - private volatile Reader reader; + /** The representation's reader. */ + private volatile Reader reader; - /** - * Constructor. - * - * @param reader The representation's stream. - */ - public ReaderRepresentation(Reader reader) { - this(reader, null); - } + /** + * Constructor. + * + * @param reader The representation's stream. + */ + public ReaderRepresentation(Reader reader) { + this(reader, null); + } - /** - * Constructor. - * - * @param reader The representation's stream. - * @param mediaType The representation's media type. - */ - public ReaderRepresentation(Reader reader, MediaType mediaType) { - this(reader, mediaType, UNKNOWN_SIZE); - } + /** + * Constructor. + * + * @param reader The representation's stream. + * @param mediaType The representation's media-type. + */ + public ReaderRepresentation(Reader reader, MediaType mediaType) { + this(reader, mediaType, UNKNOWN_SIZE); + } - /** - * Constructor. - * - * @param reader The representation's stream. - * @param mediaType The representation's media type. - * @param expectedSize The expected reader size in bytes. - */ - public ReaderRepresentation(Reader reader, MediaType mediaType, long expectedSize) { - super(mediaType); - setSize(expectedSize); - setTransient(true); - setReader(reader); - } + /** + * Constructor. + * + * @param reader The representation's stream. + * @param mediaType The representation's media-type. + * @param expectedSize The expected reader size in bytes. + */ + public ReaderRepresentation(Reader reader, MediaType mediaType, long expectedSize) { + super(mediaType); + setSize(expectedSize); + setTransient(true); + setReader(reader); + } - @Override - public Reader getReader() throws IOException { - final Reader result = this.reader; - setReader(null); - return result; - } + @Override + public Reader getReader() throws IOException { + final Reader result = this.reader; + setReader(null); + return result; + } - /** - * Note that this method relies on {@link #getStream()}. This stream is closed - * once fully read. - */ - @Override - public String getText() throws IOException { - return IoUtils.toString(getStream(), getCharacterSet()); - } + /** + * Note that this method relies on {@link #getStream()}. This stream is closed once fully read. + */ + @Override + public String getText() throws IOException { + return IoUtils.toString(getStream(), getCharacterSet()); + } - /** - * Closes and releases the input stream. - */ - @Override - public void release() { - if (this.reader != null) { - try { - this.reader.close(); - } catch (IOException e) { - Context.getCurrentLogger().log(Level.WARNING, "Error while releasing the representation.", e); - } + /** Closes and releases the input stream. */ + @Override + public void release() { + if (this.reader != null) { + try { + this.reader.close(); + } catch (IOException e) { + Context.getCurrentLogger() + .log(Level.WARNING, "Error while releasing the representation.", e); + } - this.reader = null; - } + this.reader = null; + } - super.release(); - } + super.release(); + } - /** - * Sets the reader to use. - * - * @param reader The reader to use. - */ - public void setReader(Reader reader) { - this.reader = reader; - setAvailable(reader != null); - } + /** + * Sets the reader to use. + * + * @param reader The reader to use. + */ + public void setReader(Reader reader) { + this.reader = reader; + setAvailable(reader != null); + } - @Override - public void write(Writer writer) throws IOException { - IoUtils.copy(getReader(), writer); - } + @Override + public void write(Writer writer) throws IOException { + IoUtils.copy(getReader(), writer); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/Representation.java b/org.restlet/src/main/java/org/restlet/representation/Representation.java index 73fbfc818e..a6db42b130 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Representation.java +++ b/org.restlet/src/main/java/org/restlet/representation/Representation.java @@ -1,14 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.util.Date; import org.restlet.Request; import org.restlet.Response; import org.restlet.data.Disposition; @@ -18,496 +22,461 @@ import org.restlet.engine.io.IoUtils; import org.restlet.engine.util.DateUtils; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.util.Date; - /** - * Current or intended state of a resource. The content of a representation can - * be retrieved several times if there is a stable and accessible source, like a - * local file or a string. When the representation is obtained via a temporary - * source like a network socket, its content can only be retrieved once. The - * "transient" and "available" properties are available to help you figure out + * Current or intended state of a resource. The content of a representation can be retrieved several + * times if there is a stable and accessible source, like a local file or a string. When the + * representation is obtained via a temporary source like a network socket, its content can only be + * retrieved once. The "transient" and "available" properties are available to help you figure out * those aspects at runtime.
*
- * For performance purpose, it is essential that a minimal overhead occurs upon - * initialization. The main overhead must only occur during invocation of - * content processing methods (write, getStream, getChannel and toString).
+ * For performance purpose, it is essential that a minimal overhead occurs upon initialization. The + * main overhead must only occur during invocation of content processing methods (write, getStream, + * getChannel, and toString).
*
- * "REST components perform actions on a resource by using a representation to - * capture the current or intended state of that resource and transferring that - * representation between components. A representation is a sequence of bytes, - * plus representation metadata to describe those bytes. Other commonly used but - * less precise names for a representation include: document, file, and HTTP + * "REST components perform actions on a resource by using a representation to capture the current + * or intended state of that resource and transferring that representation between components. A + * representation is a sequence of bytes, plus representation metadata to describe those bytes. + * Other commonly used but less precise names for a representation include: document, file, and HTTP * message entity, instance, or variant." Roy T. Fielding - * - * @see Source dissertation + * + * @see Source + * dissertation * @author Jerome Louvel */ public abstract class Representation extends RepresentationInfo { - /** - * Indicates that the size of the representation can't be known in advance. - */ - public static final long UNKNOWN_SIZE = -1L; - - /** Indicates if the representation's content is potentially available. */ - private volatile boolean available; - - /** - * The representation's digest, if any. - */ - private volatile org.restlet.data.Digest digest; - - /** The disposition characteristics of the representation. */ - private volatile Disposition disposition; - - /** The expiration date. */ - private volatile Date expirationDate; - - /** Indicates if the representation's content is transient. */ - private volatile boolean isTransient; - - /** - * Indicates where in the full content the partial content available should be - * applied. - */ - private volatile Range range; - - /** - * The expected size. Dynamic representations can have any size, but sometimes - * we can know in advance the expected size. If this expected size is specified - * by the user, it has a higher priority than any size that can be guessed by - * the representation (like a file size). - */ - private volatile long size; - - /** - * Default constructor. - */ - public Representation() { - this(null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - */ - public Representation(MediaType mediaType) { - super(mediaType); - this.available = true; - this.disposition = null; - this.isTransient = false; - this.size = UNKNOWN_SIZE; - this.expirationDate = null; - this.digest = null; - this.range = null; - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param modificationDate The modification date. - */ - public Representation(MediaType mediaType, Date modificationDate) { - this(mediaType, modificationDate, null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param modificationDate The modification date. - * @param tag The tag. - */ - public Representation(MediaType mediaType, Date modificationDate, Tag tag) { - super(mediaType, modificationDate, tag); - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param tag The tag. - */ - public Representation(MediaType mediaType, Tag tag) { - this(mediaType, null, tag); - } - - /** - * Constructor from a variant. - * - * @param variant The variant to copy. - * @param modificationDate The modification date. - */ - public Representation(Variant variant, Date modificationDate) { - this(variant, modificationDate, null); - } - - /** - * Constructor from a variant. - * - * @param variant The variant to copy. - * @param modificationDate The modification date. - * @param tag The tag. - */ - public Representation(Variant variant, Date modificationDate, Tag tag) { - setCharacterSet(variant.getCharacterSet()); - setEncodings(variant.getEncodings()); - setLocationRef(variant.getLocationRef()); - setLanguages(variant.getLanguages()); - setMediaType(variant.getMediaType()); - setModificationDate(modificationDate); - setTag(tag); - } - - /** - * Constructor from a variant. - * - * @param variant The variant to copy. - * @param tag The tag. - */ - public Representation(Variant variant, Tag tag) { - this(variant, null, tag); - } - - /** - * Appends the representation to an appendable sequence of characters. This - * method is ensured to write the full content for each invocation unless it is - * a transient representation, in which case an exception is thrown.
- *
- * Note that {@link #getText()} is used by the default implementation. - * - * @param appendable The appendable sequence of characters. - * @throws IOException - */ - public void append(Appendable appendable) throws IOException { - appendable.append(getText()); - } - - /** - * Exhaust the content of the representation by reading it and silently - * discarding anything read. By default, it relies on {@link #getStream()} and - * closes the retrieved stream in the end. - * - * @return The number of bytes consumed or -1 if unknown. - * @throws IOException - */ - public long exhaust() throws IOException { - long result = -1L; - - if (isAvailable()) { - InputStream is = getStream(); - result = IoUtils.exhaust(is); - is.close(); - } - - return result; - } - - /** - * Returns the size effectively available. This returns the same value as - * {@link #getSize()} if no range is defined, otherwise it returns the size of - * the range using {@link Range#getSize()}. - * - * @return The available size. - */ - public long getAvailableSize() { - return IoUtils.getAvailableSize(this); - } - - /** - * Returns the representation digest if any.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-MD5" header. - * - * @return The representation digest or null. - */ - public org.restlet.data.Digest getDigest() { - return this.digest; - } - - /** - * Returns the disposition characteristics of the representation. - * - * @return The disposition characteristics of the representation. - */ - public Disposition getDisposition() { - return disposition; - } - - /** - * Returns the future date when this representation expire. If this information - * is not known, returns null.
- *
- * Note that when used with HTTP connectors, this property maps to the "Expires" - * header. - * - * @return The expiration date. - */ - public Date getExpirationDate() { - return this.expirationDate; - } - - /** - * Returns the range where in the full content the partial content available - * should be applied.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Range" header. - * - * @return The content range or null if the full content is available. - */ - public Range getRange() { - return this.range; - } - - /** - * Returns a character reader with the representation's content. This method is - * ensured to return a fresh reader for each invocation unless it is a transient - * representation, in which case null is returned. If the representation has no - * character set defined, the system's default one will be used. - * - * @return A reader with the representation's content. - * @throws IOException - */ - public abstract Reader getReader() throws IOException; - - /** - * Returns the total size in bytes if known, UNKNOWN_SIZE (-1) otherwise. When - * ranges are used, this might not be the actual size available. For this - * purpose, you can use the {@link #getAvailableSize()} method.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Length" header. - * - * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise. - * @see #isEmpty() - */ - public long getSize() { - return this.size; - } - - /** - * Returns a stream with the representation's content. This method is ensured to - * return a fresh stream for each invocation unless it is a transient - * representation, in which case null is returned. - * - * @return A stream with the representation's content. - * @throws IOException - */ - public abstract InputStream getStream() throws IOException; - - /** - * Converts the representation to a string value. Be careful when using this - * method as the conversion of large content to a string fully stored in memory - * can result in OutOfMemoryErrors being thrown. - * - * @return The representation as a string value. - * @throws IOException - */ - public String getText() throws IOException { - String result = null; - - if (isEmpty()) { - result = ""; - } else if (isAvailable()) { - java.io.StringWriter sw = new java.io.StringWriter(); - write(sw); - sw.flush(); - result = sw.toString(); - } - - return result; - } - - /** - * Indicates if the size of representation is known. It basically means that its - * size 0 or superior. - * - * @return True if the representation has content. - */ - public boolean hasKnownSize() { - return getSize() >= 0; - } - - /** - * Indicates if some fresh content is potentially available, without having to - * actually call one of the content manipulation method like getStream() that - * would actually consume it. Note that when the size of a representation is 0 - * is a not considered available. However, sometimes the size isn't known until - * a read attempt is made, so availability doesn't guarantee a non empty - * content.
- *
- * This is especially useful for transient representation whose content can only - * be accessed once and also when the size of the representation is not known in - * advance. - * - * @return True if some fresh content is available. - */ - public boolean isAvailable() { - return this.available && (getSize() != 0); - } - - /** - * Indicates if the representation is empty. It basically means that its size is - * 0. - * - * @return True if the representation has no content. - */ - public boolean isEmpty() { - return getSize() == 0; - } - - /** - * Indicates if the representation's content is transient, which means that it - * can be obtained only once. This is often the case with representations - * transmitted via network sockets for example. In such case, if you need to - * read the content several times, you need to cache it first, for example into - * memory or into a file. - * - * @return True if the representation's content is transient. - */ - public boolean isTransient() { - return this.isTransient; - } - - /** - * Releases the representation and all associated objects like streams, channels - * or files which are used to produce its content, transient or not. This method - * must be systematically called when the representation is no longer intended - * to be used. The framework automatically calls back this method via its - * connectors on the server-side when sending responses with an entity and on - * the client-side when sending a request with an entity. By default, it calls - * the {@link #setAvailable(boolean)} method with "false" as a value.
- *
- * Note that for transient socket-bound representations, calling this method - * after consuming the whole content shouldn't prevent the reuse of underlying - * socket via persistent connections for example. However, if the content hasn't - * been read, or has been partially read, the impact should be to discard the - * remaining content and to close the underlying connections.
- *
- * Therefore, if you are not interested in the content, or in the remaining - * content, you should first call the {@link #exhaust()} method or if this could - * be too costly, you should instead explicitly abort the parent request and the - * underlying connections using the {@link Request#abort()} method or a shortcut - * one like {@link org.restlet.resource.ServerResource#abort()} or - * {@link Response#abort()}. - */ - public void release() { - setAvailable(false); - } - - /** - * Indicates if some fresh content is available. - * - * @param available True if some fresh content is available. - */ - public void setAvailable(boolean available) { - this.available = available; - } - - /** - * Sets the representation digest.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-MD5" header. - * - * @param digest The representation digest. - */ - public void setDigest(org.restlet.data.Digest digest) { - this.digest = digest; - } - - /** - * Sets the disposition characteristics of the representation. - * - * @param disposition The disposition characteristics of the representation. - */ - public void setDisposition(Disposition disposition) { - this.disposition = disposition; - } - - /** - * Sets the future date when this representation expire. If this information is - * not known, pass null.
- *
- * Note that when used with HTTP connectors, this property maps to the "Expires" - * header. - * - * @param expirationDate The expiration date. - */ - public void setExpirationDate(Date expirationDate) { - this.expirationDate = DateUtils.unmodifiable(expirationDate); - } - - /** - * Sets the range where in the full content the partial content available should - * be applied.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Range" header. - * - * @param range The content range. - */ - public void setRange(Range range) { - this.range = range; - } - - /** - * Sets the expected size in bytes if known, -1 otherwise. For this purpose, you - * can use the {@link #getAvailableSize()} method.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Length" header. - * - * @param expectedSize The expected size in bytes if known, -1 otherwise. - */ - public void setSize(long expectedSize) { - this.size = expectedSize; - } - - /** - * Indicates if the representation's content is transient. - * - * @param isTransient True if the representation's content is transient. - */ - public void setTransient(boolean isTransient) { - this.isTransient = isTransient; - } - - /** - * Writes the representation to a characters writer. This method is ensured to - * write the full content for each invocation unless it is a transient - * representation, in which case an exception is thrown.
- *
- * Note that the class implementing this method shouldn't flush or close the - * given {@link java.io.Writer} after writing to it as this will be handled by - * the Restlet connectors automatically. - * - * @param writer The characters writer. - * @throws IOException - */ - public abstract void write(java.io.Writer writer) throws IOException; - - /** - * Writes the representation to a byte stream. This method is ensured to write - * the full content for each invocation unless it is a transient representation, - * in which case an exception is thrown.
- *
- * Note that the class implementing this method shouldn't flush or close the - * given {@link OutputStream} after writing to it as this will be handled by the - * Restlet connectors automatically. - * - * @param outputStream The output stream. - * @throws IOException - */ - public abstract void write(OutputStream outputStream) throws IOException; - + /** Indicates that the size of the representation can't be known in advance. */ + public static final long UNKNOWN_SIZE = -1L; + + /** Indicates if the representation's content is potentially available. */ + private volatile boolean available; + + /** The representation's digest, if any. */ + private volatile org.restlet.data.Digest digest; + + /** The disposition characteristics of the representation. */ + private volatile Disposition disposition; + + /** The expiration date. */ + private volatile Date expirationDate; + + /** Indicates if the representation's content is transient. */ + private volatile boolean isTransient; + + /** Indicates where in the full content the partial content available should be applied. */ + private volatile Range range; + + /** + * The expected size. Dynamic representations can have any size, but sometimes we can know in + * advance the expected size. If the user specifies this expected size, it has a higher priority + * than any size that can be guessed by the representation (like a file size). + */ + private volatile long size; + + /** Default constructor. */ + public Representation() { + this(null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + */ + public Representation(MediaType mediaType) { + super(mediaType); + this.available = true; + this.disposition = null; + this.isTransient = false; + this.size = UNKNOWN_SIZE; + this.expirationDate = null; + this.digest = null; + this.range = null; + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param modificationDate The modification date. + */ + public Representation(MediaType mediaType, Date modificationDate) { + this(mediaType, modificationDate, null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param modificationDate The modification date. + * @param tag The tag. + */ + public Representation(MediaType mediaType, Date modificationDate, Tag tag) { + super(mediaType, modificationDate, tag); + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param tag The tag. + */ + public Representation(MediaType mediaType, Tag tag) { + this(mediaType, null, tag); + } + + /** + * Constructor from a variant. + * + * @param variant The variant to copy. + * @param modificationDate The modification date. + */ + public Representation(Variant variant, Date modificationDate) { + this(variant, modificationDate, null); + } + + /** + * Constructor from a variant. + * + * @param variant The variant to copy. + * @param modificationDate The modification date. + * @param tag The tag. + */ + public Representation(Variant variant, Date modificationDate, Tag tag) { + setCharacterSet(variant.getCharacterSet()); + setEncodings(variant.getEncodings()); + setLocationRef(variant.getLocationRef()); + setLanguages(variant.getLanguages()); + setMediaType(variant.getMediaType()); + setModificationDate(modificationDate); + setTag(tag); + } + + /** + * Constructor from a variant. + * + * @param variant The variant to copy. + * @param tag The tag. + */ + public Representation(Variant variant, Tag tag) { + this(variant, null, tag); + } + + /** + * Appends the representation to an appendable sequence of characters. This method is ensured to + * write the full content for each invocation unless it is a transient representation, in which + * case an exception is thrown.
+ *
+ * Note that {@link #getText()} is used by the default implementation. + * + * @param appendable The appendable sequence of characters. + * @throws IOException + */ + public void append(Appendable appendable) throws IOException { + appendable.append(getText()); + } + + /** + * Exhaust the content of the representation by reading it and silently discarding anything + * read. By default, it relies on {@link #getStream()} and closes the retrieved stream in the + * end. + * + * @return The number of bytes consumed or -1 if unknown. + * @throws IOException + */ + public long exhaust() throws IOException { + long result = -1L; + + if (isAvailable()) { + InputStream is = getStream(); + result = IoUtils.exhaust(is); + is.close(); + } + + return result; + } + + /** + * Returns the size effectively available. This returns the same value as {@link #getSize()} if + * no range is defined, otherwise it returns the size of the range using {@link + * Range#getSize()}. + * + * @return The available size. + */ + public long getAvailableSize() { + return IoUtils.getAvailableSize(this); + } + + /** + * Returns the representation digest if any.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-MD5" header. + * + * @return The representation's digest or null. + */ + public org.restlet.data.Digest getDigest() { + return this.digest; + } + + /** + * Returns the disposition characteristics of the representation. + * + * @return The disposition characteristics of the representation. + */ + public Disposition getDisposition() { + return disposition; + } + + /** + * Returns the future date when this representation expires. If this information is not known, + * returns null.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Expires" header. + * + * @return The expiration date. + */ + public Date getExpirationDate() { + return this.expirationDate; + } + + /** + * Returns the range where in the full content the partial content available should be applied. + *
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Range" header. + * + * @return The content range or null if the full content is available. + */ + public Range getRange() { + return this.range; + } + + /** + * Returns a character reader with the representation's content. This method is ensured to + * return a fresh reader for each invocation unless it is a transient representation, in which + * case null is returned. If the representation has no character set defined, the system's + * default one will be used. + * + * @return A reader with the representation's content. + * @throws IOException + */ + public abstract Reader getReader() throws IOException; + + /** + * Returns the total size in bytes if known, UNKNOWN_SIZE (-1) otherwise. When ranges are used, + * this might not be the actual size available. For this purpose, you can use the {@link + * #getAvailableSize()} method.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Length" header. + * + * @return The size in bytes if known, UNKNOWN_SIZE (-1) otherwise. + * @see #isEmpty() + */ + public long getSize() { + return this.size; + } + + /** + * Returns a stream with the representation's content. This method is ensured to return a fresh + * stream for each invocation unless it is a transient representation, in which case null is + * returned. + * + * @return A stream with the representation's content. + * @throws IOException + */ + public abstract InputStream getStream() throws IOException; + + /** + * Converts the representation to a string value. Be careful when using this method as the + * conversion of large content to a string fully stored in memory can result in + * OutOfMemoryErrors being thrown. + * + * @return The representation as a string value. + * @throws IOException + */ + public String getText() throws IOException { + String result = null; + + if (isEmpty()) { + result = ""; + } else if (isAvailable()) { + java.io.StringWriter sw = new java.io.StringWriter(); + write(sw); + sw.flush(); + result = sw.toString(); + } + + return result; + } + + /** + * Indicates if the size of representation is known. It basically means that its size 0 or + * superior. + * + * @return True if the representation has content. + */ + public boolean hasKnownSize() { + return getSize() >= 0; + } + + /** + * Indicates if some fresh content is potentially available, without having to actually call one + * of the content manipulation methods like getStream() that would actually consume it. Note + * that when the size of a representation is 0, it is not considered as available. However, + * sometimes the size isn't known until a read attempt is made, so availability doesn't + * guarantee a non-empty content.
+ *
+ * This is especially useful for transient representation whose content can only be accessed + * once and also when the size of the representation is not known in advance. + * + * @return True if some fresh content is available. + */ + public boolean isAvailable() { + return this.available && (getSize() != 0); + } + + /** + * Indicates if the representation is empty. It basically means that its size is 0. + * + * @return True if the representation has no content. + */ + public boolean isEmpty() { + return getSize() == 0; + } + + /** + * Indicates if the representation's content is transient, which means that it can be obtained + * only once. This is often the case with representations transmitted via network sockets, for + * example. In such a case, if you need to read the content several times, you need to cache it + * first, for example, into memory or into a file. + * + * @return True if the representation's content is transient. + */ + public boolean isTransient() { + return this.isTransient; + } + + /** + * Releases the representation and all associated objects like streams, channels, or files that + * are used to produce its content, transient or not. This method must be systematically called + * when the representation is no longer intended to be used. The framework automatically calls + * back this method via its connectors on the server-side when sending responses with an entity + * and on the client-side when sending a request with an entity. By default, it calls the {@link + * #setAvailable(boolean)} method with "false" as a value.
+ *
+ * Note that for transient socket-bound representations, calling this method after consuming the + * whole content shouldn't prevent the reuse of the underlying socket via persistent + * connections, for example. However, if the content hasn't been read or has been partially + * read, the impact should be to discard the remaining content and to close the underlying + * connections.
+ *
+ * Therefore, if you are not interested in the content, or in the remaining content, you should + * first call the {@link #exhaust()} method or if this could be too costly, you should instead + * explicitly abort the parent request and the underlying connections using the {@link + * Request#abort()} method or a shortcut one like {@link + * org.restlet.resource.ServerResource#abort()} or {@link Response#abort()}. + */ + public void release() { + setAvailable(false); + } + + /** + * Indicates if some fresh content is available. + * + * @param available True if some fresh content is available. + */ + public void setAvailable(boolean available) { + this.available = available; + } + + /** + * Sets the representation digest.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-MD5" header. + * + * @param digest The representation digest. + */ + public void setDigest(org.restlet.data.Digest digest) { + this.digest = digest; + } + + /** + * Sets the disposition characteristics of the representation. + * + * @param disposition The disposition characteristics of the representation. + */ + public void setDisposition(Disposition disposition) { + this.disposition = disposition; + } + + /** + * Sets the future date when this representation expires. If this information is not known, pass + * null.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Expires" header. + * + * @param expirationDate The expiration date. + */ + public void setExpirationDate(Date expirationDate) { + this.expirationDate = DateUtils.unmodifiable(expirationDate); + } + + /** + * Sets the range where in the full content the partial content available should be applied.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Range" header. + * + * @param range The content range. + */ + public void setRange(Range range) { + this.range = range; + } + + /** + * Sets the expected size in bytes if known, -1 otherwise. For this purpose, you can use the + * {@link #getAvailableSize()} method.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Length" header. + * + * @param expectedSize The expected size in bytes if known, -1 otherwise. + */ + public void setSize(long expectedSize) { + this.size = expectedSize; + } + + /** + * Indicates if the representation's content is transient. + * + * @param isTransient True if the representation's content is transient. + */ + public void setTransient(boolean isTransient) { + this.isTransient = isTransient; + } + + /** + * Writes the representation to a characters' writer. This method is ensured to write the full + * content for each invocation unless it is a transient representation, in which case an + * exception is thrown.
+ *
+ * Note that the class implementing this method shouldn't flush or close the given {@link + * java.io.Writer} after writing to it as this will be handled by the Restlet connectors + * automatically. + * + * @param writer The characters' writer. + * @throws IOException + */ + public abstract void write(java.io.Writer writer) throws IOException; + + /** + * Writes the representation to a byte stream. This method is ensured to write the full content + * for each invocation unless it is a transient representation, in which case an exception is + * thrown.
+ *
+ * Note that the class implementing this method shouldn't flush or close the given {@link + * OutputStream} after writing to it as this will be handled by the Restlet connectors + * automatically. + * + * @param outputStream The output stream. + * @throws IOException + */ + public abstract void write(OutputStream outputStream) throws IOException; } diff --git a/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java b/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java index 7210106e77..2a57e63e7f 100644 --- a/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java +++ b/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java @@ -1,173 +1,162 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import java.util.Date; import org.restlet.data.MediaType; import org.restlet.data.Tag; import org.restlet.engine.util.DateUtils; -import java.util.Date; - /** - * Information about a representation. Those metadata don't belong to the parent - * {@link Variant} class, however they are important for conditional method - * processing. The advantage over the complete {@link Representation} class is - * that it is much lighter to create. - * - * @see Source dissertation + * Information about a representation. Those metadata don't belong to the parent {@link Variant} + * class, however they are important for conditional method processing. The advantage over the + * complete {@link Representation} class is that it is much lighter to create. + * + * @see Source + * dissertation * @author Jerome Louvel */ public class RepresentationInfo extends Variant { - /** The modification date. */ - private volatile Date modificationDate; - - /** The tag. */ - private volatile Tag tag; - - /** - * Default constructor. - */ - public RepresentationInfo() { - this(null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - */ - public RepresentationInfo(MediaType mediaType) { - this(mediaType, null, null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param modificationDate The modification date. - */ - public RepresentationInfo(MediaType mediaType, Date modificationDate) { - this(mediaType, modificationDate, null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param modificationDate The modification date. - * @param tag The tag. - */ - public RepresentationInfo(MediaType mediaType, Date modificationDate, Tag tag) { - super(mediaType); - this.modificationDate = modificationDate; - this.tag = tag; - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param tag The tag. - */ - public RepresentationInfo(MediaType mediaType, Tag tag) { - this(mediaType, null, tag); - } - - /** - * Constructor from a variant. - * - * @param variant The variant to copy. - * @param modificationDate The modification date. - */ - public RepresentationInfo(Variant variant, Date modificationDate) { - this(variant, modificationDate, null); - } - - /** - * Constructor from a variant. - * - * @param variant The variant to copy. - * @param modificationDate The modification date. - * @param tag The tag. - */ - public RepresentationInfo(Variant variant, Date modificationDate, Tag tag) { - setCharacterSet(variant.getCharacterSet()); - setEncodings(variant.getEncodings()); - setLocationRef(variant.getLocationRef()); - setLanguages(variant.getLanguages()); - setMediaType(variant.getMediaType()); - setModificationDate(modificationDate); - setTag(tag); - } - - /** - * Constructor from a variant. - * - * @param variant The variant to copy. - * @param tag The tag. - */ - public RepresentationInfo(Variant variant, Tag tag) { - this(variant, null, tag); - } - - /** - * Returns the last date when this representation was modified. If this - * information is not known, returns null.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Last-Modified" header. - * - * @return The modification date. - */ - public Date getModificationDate() { - return this.modificationDate; - } - - /** - * Returns the tag.
- *
- * Note that when used with HTTP connectors, this property maps to the "ETag" - * header. - * - * @return The tag. - */ - public Tag getTag() { - return this.tag; - } - - /** - * Sets the last date when this representation was modified. If this information - * is not known, pass null.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Last-Modified" header. - * - * @param modificationDate The modification date. - */ - public void setModificationDate(Date modificationDate) { - this.modificationDate = DateUtils.unmodifiable(modificationDate); - } - - /** - * Sets the tag.
- *
- * Note that when used with HTTP connectors, this property maps to the "ETag" - * header. - * - * @param tag The tag. - */ - public void setTag(Tag tag) { - this.tag = tag; - } - + /** The modification date. */ + private volatile Date modificationDate; + + /** The tag. */ + private volatile Tag tag; + + /** Default constructor. */ + public RepresentationInfo() { + this(null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + */ + public RepresentationInfo(MediaType mediaType) { + this(mediaType, null, null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param modificationDate The modification date. + */ + public RepresentationInfo(MediaType mediaType, Date modificationDate) { + this(mediaType, modificationDate, null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param modificationDate The modification date. + * @param tag The tag. + */ + public RepresentationInfo(MediaType mediaType, Date modificationDate, Tag tag) { + super(mediaType); + this.modificationDate = modificationDate; + this.tag = tag; + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param tag The tag. + */ + public RepresentationInfo(MediaType mediaType, Tag tag) { + this(mediaType, null, tag); + } + + /** + * Constructor from a variant. + * + * @param variant The variant to copy. + * @param modificationDate The modification date. + */ + public RepresentationInfo(Variant variant, Date modificationDate) { + this(variant, modificationDate, null); + } + + /** + * Constructor from a variant. + * + * @param variant The variant to copy. + * @param modificationDate The modification date. + * @param tag The tag. + */ + public RepresentationInfo(Variant variant, Date modificationDate, Tag tag) { + setCharacterSet(variant.getCharacterSet()); + setEncodings(variant.getEncodings()); + setLocationRef(variant.getLocationRef()); + setLanguages(variant.getLanguages()); + setMediaType(variant.getMediaType()); + setModificationDate(modificationDate); + setTag(tag); + } + + /** + * Constructor from a variant. + * + * @param variant The variant to copy. + * @param tag The tag. + */ + public RepresentationInfo(Variant variant, Tag tag) { + this(variant, null, tag); + } + + /** + * Returns the last date when this representation was modified. If this information is not + * known, returns null.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Last-Modified" header. + * + * @return The modification date. + */ + public Date getModificationDate() { + return this.modificationDate; + } + + /** + * Returns the tag.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "ETag" header. + * + * @return The tag. + */ + public Tag getTag() { + return this.tag; + } + + /** + * Sets the last date when this representation was modified. If this information is not known, + * pass null.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Last-Modified" header. + * + * @param modificationDate The modification date. + */ + public void setModificationDate(Date modificationDate) { + this.modificationDate = DateUtils.unmodifiable(modificationDate); + } + + /** + * Sets the tag.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "ETag" header. + * + * @param tag The tag. + */ + public void setTag(Tag tag) { + this.tag = tag; + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java index 462209e7d7..883f20fd16 100644 --- a/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java @@ -1,47 +1,44 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; - import java.io.IOException; import java.io.OutputStream; import java.io.Reader; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; /** * Representation based on a BIO stream. - * + * * @author Jerome Louvel */ public abstract class StreamRepresentation extends Representation { - /** - * Constructor. - * - * @param mediaType The media type. - */ - public StreamRepresentation(MediaType mediaType) { - super(mediaType); - } - - @Override - public Reader getReader() throws IOException { - return IoUtils.getReader(getStream(), getCharacterSet()); - } - - @Override - public void write(java.io.Writer writer) throws IOException { - OutputStream os = IoUtils.getStream(writer, getCharacterSet()); - write(os); - os.flush(); - } - + /** + * Constructor. + * + * @param mediaType The media type. + */ + public StreamRepresentation(MediaType mediaType) { + super(mediaType); + } + + @Override + public Reader getReader() throws IOException { + return IoUtils.getReader(getStream(), getCharacterSet()); + } + + @Override + public void write(java.io.Writer writer) throws IOException { + OutputStream os = IoUtils.getStream(writer, getCharacterSet()); + write(os); + os.flush(); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java index 07d7e4bef1..dabaa4fecf 100644 --- a/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java @@ -1,191 +1,192 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.data.CharacterSet; import org.restlet.data.Language; import org.restlet.data.MediaType; -import java.io.*; -import java.util.logging.Level; - /** - * Represents an Unicode string that can be converted to any character set - * supported by Java. - * + * Represents a Unicode string that can be converted to any character set supported by Java. + * * @author Jerome Louvel */ public class StringRepresentation extends CharacterRepresentation { - /** The string value. */ - private volatile CharSequence text; - - /** - * Constructor. The following metadata are used by default: "text/plain" media - * type, no language and the UTF-8 character set. - * - * @param chars The characters array. - */ - public StringRepresentation(char[] chars) { - this(new String(chars), MediaType.TEXT_PLAIN); - } - - /** - * Constructor. The following metadata are used by default: "text/plain" media - * type, no language and the UTF-8 character set. - * - * @param text The string value. - */ - public StringRepresentation(CharSequence text) { - this(text, MediaType.TEXT_PLAIN); - } - - /** - * Constructor. The following metadata are used by default: "text/plain" media - * type, no language and the UTF-8 character set. - * - * @param text The string value. - * @param language The language. - */ - public StringRepresentation(CharSequence text, Language language) { - this(text, MediaType.TEXT_PLAIN, language); - } - - /** - * Constructor. The following metadata are used by default: no language and the - * UTF-8 character set. - * - * @param text The string value. - * @param mediaType The media type. - */ - public StringRepresentation(CharSequence text, MediaType mediaType) { - this(text, mediaType, null); - } - - /** - * Constructor. The following metadata are used by default: UTF-8 character set. - * - * @param text The string value. - * @param mediaType The media type. - * @param language The language. - */ - public StringRepresentation(CharSequence text, MediaType mediaType, Language language) { - this(text, mediaType, language, CharacterSet.UTF_8); - } - - /** - * Constructor. - * - * @param text The string value. - * @param mediaType The media type. - * @param language The language. - * @param characterSet The character set. - */ - public StringRepresentation(CharSequence text, MediaType mediaType, Language language, CharacterSet characterSet) { - super(mediaType); - setMediaType(mediaType); - if (language != null) { - getLanguages().add(language); - } - - setCharacterSet(characterSet); - setText(text); - } - - @Override - public Reader getReader() throws IOException { - if (getText() != null) { - return new StringReader(getText()); - } - - return null; - } - - @Override - public InputStream getStream() throws IOException { - CharacterSet charset = getCharacterSet() == null ? CharacterSet.ISO_8859_1 : getCharacterSet(); - ByteArrayInputStream result = new ByteArrayInputStream(getText().getBytes(charset.getName())); - return result; - } - - @Override - public String getText() { - return (this.text == null) ? null : this.text.toString(); - } - - /** - * Closes and releases the input stream. - */ - @Override - public void release() { - setText(null); - super.release(); - } - - @Override - public void setCharacterSet(CharacterSet characterSet) { - super.setCharacterSet(characterSet); - updateSize(); - } - - /** - * Sets the string value. - * - * @param text The string value. - */ - public void setText(CharSequence text) { - this.text = text; - updateSize(); - } - - /** - * Sets the string value. - * - * @param text The string value. - */ - public void setText(String text) { - setText((CharSequence) text); - } - - @Override - public String toString() { - return getText(); - } - - /** - * Updates the expected size according to the current string value. - */ - protected void updateSize() { - if (getText() != null) { - try { - if (getCharacterSet() != null) { - setSize(getText().getBytes(getCharacterSet().getName()).length); - } else { - setSize(getText().getBytes().length); - } - } catch (UnsupportedEncodingException e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to update size", e); - setSize(UNKNOWN_SIZE); - } - } else { - setSize(UNKNOWN_SIZE); - } - } - - @Override - public void write(Writer writer) throws IOException { - if (getText() != null) { - writer.write(getText()); - writer.flush(); - } - } - + /** The string value. */ + private volatile CharSequence text; + + /** + * Constructor. The following metadata is used by default: "text/plain" media type, no language, + * and the UTF-8 character set. + * + * @param chars The characters array. + */ + public StringRepresentation(char[] chars) { + this(new String(chars), MediaType.TEXT_PLAIN); + } + + /** + * Constructor. The following metadata is used by default: "text/plain" media type, no language, + * and the UTF-8 character set. + * + * @param text The string value. + */ + public StringRepresentation(CharSequence text) { + this(text, MediaType.TEXT_PLAIN); + } + + /** + * Constructor. The following metadata is used by default: "text/plain" media type, no language, + * and the UTF-8 character set. + * + * @param text The string value. + * @param language The language. + */ + public StringRepresentation(CharSequence text, Language language) { + this(text, MediaType.TEXT_PLAIN, language); + } + + /** + * Constructor. The following metadata is used by default: no language and the UTF-8 character + * set. + * + * @param text The string value. + * @param mediaType The media type. + */ + public StringRepresentation(CharSequence text, MediaType mediaType) { + this(text, mediaType, null); + } + + /** + * Constructor. The following metadata is used by default: UTF-8 character set. + * + * @param text The string value. + * @param mediaType The media type. + * @param language The language. + */ + public StringRepresentation(CharSequence text, MediaType mediaType, Language language) { + this(text, mediaType, language, CharacterSet.UTF_8); + } + + /** + * Constructor. + * + * @param text The string value. + * @param mediaType The media type. + * @param language The language. + * @param characterSet The character set. + */ + public StringRepresentation( + CharSequence text, MediaType mediaType, Language language, CharacterSet characterSet) { + super(mediaType); + setMediaType(mediaType); + if (language != null) { + getLanguages().add(language); + } + + setCharacterSet(characterSet); + setText(text); + } + + @Override + public Reader getReader() throws IOException { + if (getText() != null) { + return new StringReader(getText()); + } + + return null; + } + + @Override + public InputStream getStream() throws IOException { + CharacterSet charset = + getCharacterSet() == null ? CharacterSet.ISO_8859_1 : getCharacterSet(); + ByteArrayInputStream result = + new ByteArrayInputStream(getText().getBytes(charset.getName())); + return result; + } + + @Override + public String getText() { + return (this.text == null) ? null : this.text.toString(); + } + + /** Closes and releases the input stream. */ + @Override + public void release() { + setText(null); + super.release(); + } + + @Override + public void setCharacterSet(CharacterSet characterSet) { + super.setCharacterSet(characterSet); + updateSize(); + } + + /** + * Sets the string value. + * + * @param text The string value. + */ + public void setText(CharSequence text) { + this.text = text; + updateSize(); + } + + /** + * Sets the string value. + * + * @param text The string value. + */ + public void setText(String text) { + setText((CharSequence) text); + } + + @Override + public String toString() { + return getText(); + } + + /** Updates the expected size according to the current string value. */ + protected void updateSize() { + if (getText() != null) { + try { + if (getCharacterSet() != null) { + setSize(getText().getBytes(getCharacterSet().getName()).length); + } else { + setSize(getText().getBytes().length); + } + } catch (UnsupportedEncodingException e) { + Context.getCurrentLogger().log(Level.WARNING, "Unable to update size", e); + setSize(UNKNOWN_SIZE); + } + } else { + setSize(UNKNOWN_SIZE); + } + } + + @Override + public void write(Writer writer) throws IOException { + if (getText() != null) { + writer.write(getText()); + writer.flush(); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/Variant.java b/org.restlet/src/main/java/org/restlet/representation/Variant.java index ca352585ca..fba3ab1fbd 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Variant.java +++ b/org.restlet/src/main/java/org/restlet/representation/Variant.java @@ -1,476 +1,486 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.data.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import org.restlet.data.CharacterSet; +import org.restlet.data.ClientInfo; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Preference; +import org.restlet.data.Reference; import org.restlet.engine.util.SystemUtils; import org.restlet.util.WrapperList; -import java.util.*; - /** - * Descriptor for available representations of a resource. It contains all the - * important metadata about a representation but is not able to actually serve - * the representation's content itself.
+ * Descriptor for available representations of a resource. It contains all the important metadata + * about a representation but is not able to actually serve the representation's content itself.
*
- * For this, you need to use on of the {@link Representation} subclasses. - * + * For this, you need to use one of the {@link Representation} subclasses. + * * @author Jerome Louvel */ public class Variant { - /** The character set or null if not applicable. */ - private volatile CharacterSet characterSet; - - /** The additional content codings applied to the entity-body. */ - private volatile List encodings; - - /** The location reference. */ - private volatile Reference locationRef; - - /** The natural language(s) of the intended audience for this variant. */ - private volatile List languages; - - /** The media type. */ - private volatile MediaType mediaType; - - /** - * Default constructor. - */ - public Variant() { - this(null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - */ - public Variant(MediaType mediaType) { - this(mediaType, null); - } - - /** - * Constructor. - * - * @param mediaType The media type. - * @param language The language. - */ - public Variant(MediaType mediaType, Language language) { - this.characterSet = null; - this.encodings = null; - - if (language != null) { - getLanguages().add(language); - } else { - this.languages = null; - } - - this.mediaType = mediaType; - this.locationRef = null; - } - - /** - * Creates a {@link ClientInfo} instance with preferences matching exactly the - * current variant. - * - * @return The new {@link ClientInfo} instance. - */ - public ClientInfo createClientInfo() { - ClientInfo result = new ClientInfo(); - - if (getCharacterSet() != null) { - result.getAcceptedCharacterSets().add(new Preference(getCharacterSet())); - } - - if (getEncodings() != null) { - for (Encoding encoding : getEncodings()) { - result.getAcceptedEncodings().add(new Preference(encoding)); - } - } - - if (getLanguages() != null) { - for (Language language : getLanguages()) { - result.getAcceptedLanguages().add(new Preference(language)); - } - } - - if (getMediaType() != null) { - result.getAcceptedMediaTypes().add(new Preference(getMediaType())); - } - - return result; - } - - /** - * Indicates if the current variant is equal to the given variant. - * - * @param other The other variant. - * @return True if the current variant includes the other. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof Variant)) { - return false; - } - - Variant that = (Variant) other; - - return Objects.equals(getCharacterSet(), that.getCharacterSet()) - && Objects.equals(getMediaType(), that.getMediaType()) && getLanguages().equals(that.getLanguages()) - && getEncodings().equals(that.getEncodings()) - && Objects.equals(getLocationRef(), that.getLocationRef()); - } - - /** - * Returns the character set or null if not applicable. Note that when used with - * HTTP connectors, this property maps to the "Content-Type" header. - * - * @return The character set or null if not applicable. - */ - public CharacterSet getCharacterSet() { - return this.characterSet; - } - - /** - * Returns the modifiable list of encodings applied to the entity-body. Creates - * a new instance if no one has been set. An "IllegalArgumentException" - * exception is thrown when adding a null encoding to this list.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Encoding" header. - * - * @return The list of encodings applied to the entity-body. - */ - public List getEncodings() { - if (this.encodings == null) { - this.encodings = new WrapperList() { - - @Override - public boolean add(Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.add(element); - } - - @Override - public void add(int index, Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - super.add(index, element); - } - - @Override - public boolean addAll(Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = elements.iterator(); !addNull - && iterator.hasNext();) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(elements); - } - - @Override - public boolean addAll(int index, Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = elements.iterator(); !addNull - && iterator.hasNext();) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(index, elements); - } - }; - } - - return this.encodings; - } - - /** - * Returns the modifiable list of languages. Creates a new instance if no one - * has been set. An "IllegalArgumentException" exception is thrown when adding a - * null language to this list.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Language" header. - * - * @return The list of languages. - */ - public List getLanguages() { - if (this.languages == null) { - this.languages = new WrapperList() { - - @Override - public void add(int index, Language element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - super.add(index, element); - } - - @Override - public boolean add(Language element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - return super.add(element); - } - - @Override - public boolean addAll(Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = elements.iterator(); !addNull - && iterator.hasNext();) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - return super.addAll(elements); - } - - @Override - public boolean addAll(int index, Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = elements.iterator(); !addNull - && iterator.hasNext();) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - return super.addAll(index, elements); - } - - }; - } - return this.languages; - } - - /** - * Returns an optional location reference. This is useful when the - * representation is accessible from a location separate from the - * representation's resource URI, for example when content negotiation - * occurs.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Location" header. - * - * @return The identifier. - */ - public Reference getLocationRef() { - return this.locationRef; - } - - /** - * Returns the media type.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Type" header. - * - * @return The media type. - */ - public MediaType getMediaType() { - return this.mediaType; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(super.hashCode(), characterSet, encodings, locationRef, languages, mediaType); - } - - /** - * Indicates if the current variant includes the given variant. - * - * @param other The other variant. - * @return True if the current variant includes the other. - */ - public boolean includes(Variant other) { - boolean result = other != null; - - // Compare the character set - if (result) { - result = (getCharacterSet() == null) || getCharacterSet().includes(other.getCharacterSet()); - } - - // Compare the media type - if (result) { - result = (getMediaType() == null) || getMediaType().includes(other.getMediaType()); - } - - // Compare the languages - if (result) { - result = (getLanguages().isEmpty()) || getLanguages().contains(Language.ALL) - || new HashSet<>(getLanguages()).containsAll(other.getLanguages()); - } - - // Compare the encodings - if (result) { - result = (getEncodings().isEmpty()) || getEncodings().contains(Encoding.ALL) - || new HashSet<>(getEncodings()).containsAll(other.getEncodings()); - } - - return result; - } - - /** - * Indicates if the current variant is compatible with the given variant. - * - * @param other The other variant. - * @return True if the current variant is compatible with the other. - */ - public boolean isCompatible(Variant other) { - return (other != null) && (includes(other) || other.includes(this)); - } - - /** - * Sets the character set or null if not applicable.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Type" header. - * - * @param characterSet The character set or null if not applicable. - */ - public void setCharacterSet(CharacterSet characterSet) { - this.characterSet = characterSet; - } - - /** - * Sets the list of encodings applied to the entity-body.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Encoding" header. - * - * @param encodings The list of encodings applied to the entity-body. - */ - public void setEncodings(List encodings) { - this.encodings = encodings; - } - - /** - * Sets the list of languages.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Language" header. - * - * @param languages The list of languages. - */ - public void setLanguages(List languages) { - this.languages = languages; - } - - /** - * Sets the optional identifier. This is useful when the representation is - * accessible from a location separate from the representation's resource URI, - * for example when content negotiation occurs.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Location" header. - * - * @param location The location reference. - */ - public void setLocationRef(Reference location) { - this.locationRef = location; - } - - /** - * Sets the identifier from a URI string.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Location" header. - * - * @param locationUri The location URI to parse. - */ - public void setLocationRef(String locationUri) { - setLocationRef(new Reference(locationUri)); - } - - /** - * Sets the media type.
- *
- * Note that when used with HTTP connectors, this property maps to the - * "Content-Type" header. - * - * @param mediaType The media type. - */ - public void setMediaType(MediaType mediaType) { - this.mediaType = mediaType; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("["); - boolean first = true; - - if (getMediaType() != null) { - first = false; - sb.append(getMediaType()); - } - - if (getCharacterSet() != null) { - if (!first) { - sb.append(","); - } else { - first = false; - } - - sb.append(getCharacterSet()); - } - - if (!getLanguages().isEmpty()) { - if (!first) { - sb.append(","); - } else { - first = false; - } - - sb.append(getLanguages()); - } - - if (!getEncodings().isEmpty()) { - if (!first) { - sb.append(","); - } else { - first = false; - } - - sb.append(getEncodings()); - } - - sb.append("]"); - return sb.toString(); - } + /** The character set or null if not applicable. */ + private volatile CharacterSet characterSet; + + /** The additional content codings applied to the entity-body. */ + private volatile List encodings; + + /** The location reference. */ + private volatile Reference locationRef; + + /** The natural language(s) of the intended audience for this variant. */ + private volatile List languages; + + /** The media type. */ + private volatile MediaType mediaType; + + /** Default constructor. */ + public Variant() { + this(null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + */ + public Variant(MediaType mediaType) { + this(mediaType, null); + } + + /** + * Constructor. + * + * @param mediaType The media type. + * @param language The language. + */ + public Variant(MediaType mediaType, Language language) { + this.characterSet = null; + this.encodings = null; + + if (language != null) { + getLanguages().add(language); + } else { + this.languages = null; + } + + this.mediaType = mediaType; + this.locationRef = null; + } + + /** + * Creates a {@link ClientInfo} instance with preferences matching exactly the current variant. + * + * @return The new {@link ClientInfo} instance. + */ + public ClientInfo createClientInfo() { + ClientInfo result = new ClientInfo(); + + if (getCharacterSet() != null) { + result.getAcceptedCharacterSets().add(new Preference<>(getCharacterSet())); + } + + if (getEncodings() != null) { + for (Encoding encoding : getEncodings()) { + result.getAcceptedEncodings().add(new Preference<>(encoding)); + } + } + + if (getLanguages() != null) { + for (Language language : getLanguages()) { + result.getAcceptedLanguages().add(new Preference<>(language)); + } + } + + if (getMediaType() != null) { + result.getAcceptedMediaTypes().add(new Preference<>(getMediaType())); + } + + return result; + } + + /** + * Indicates if the current variant is equal to the given variant. + * + * @param other The other variant. + * @return True if the current variant includes the other. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof final Variant that)) { + return false; + } + + return Objects.equals(getCharacterSet(), that.getCharacterSet()) + && Objects.equals(getMediaType(), that.getMediaType()) + && getLanguages().equals(that.getLanguages()) + && getEncodings().equals(that.getEncodings()) + && Objects.equals(getLocationRef(), that.getLocationRef()); + } + + /** + * Returns the character set or null if not applicable. Note that when used with HTTP + * connectors, this property maps to the "Content-Type" header. + * + * @return The character set or null if not applicable. + */ + public CharacterSet getCharacterSet() { + return this.characterSet; + } + + /** + * Returns the modifiable list of encodings applied to the entity-body. Creates a new instance + * if no one has been set. An "IllegalArgumentException" exception is thrown when adding a null + * encoding to this list.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Encoding" + * header. + * + * @return The list of encodings applied to the entity-body. + */ + public List getEncodings() { + if (this.encodings == null) { + this.encodings = + new WrapperList<>() { + + @Override + public boolean add(Encoding element) { + if (element == null) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + return super.add(element); + } + + @Override + public void add(int index, Encoding element) { + if (element == null) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + super.add(index, element); + } + + @Override + public boolean addAll(Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (final Iterator iterator = + elements.iterator(); + !addNull && iterator.hasNext(); ) { + addNull = (iterator.next() == null); + } + } + if (addNull) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + return super.addAll(elements); + } + + @Override + public boolean addAll(int index, Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (final Iterator iterator = + elements.iterator(); + !addNull && iterator.hasNext(); ) { + addNull = (iterator.next() == null); + } + } + if (addNull) { + throw new IllegalArgumentException("Cannot add a null encoding."); + } + + return super.addAll(index, elements); + } + }; + } + + return this.encodings; + } + + /** + * Returns the modifiable list of languages. Creates a new instance if no one has been set. An + * "IllegalArgumentException" exception is thrown when adding a null language to this list.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Language" + * header. + * + * @return The list of languages. + */ + public List getLanguages() { + if (this.languages == null) { + this.languages = + new WrapperList<>() { + + @Override + public void add(int index, Language element) { + if (element == null) { + throw new IllegalArgumentException("Cannot add a null language."); + } + + super.add(index, element); + } + + @Override + public boolean add(Language element) { + if (element == null) { + throw new IllegalArgumentException("Cannot add a null language."); + } + + return super.add(element); + } + + @Override + public boolean addAll(Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (final Iterator iterator = + elements.iterator(); + !addNull && iterator.hasNext(); ) { + addNull = (iterator.next() == null); + } + } + if (addNull) { + throw new IllegalArgumentException("Cannot add a null language."); + } + + return super.addAll(elements); + } + + @Override + public boolean addAll(int index, Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (final Iterator iterator = + elements.iterator(); + !addNull && iterator.hasNext(); ) { + addNull = (iterator.next() == null); + } + } + if (addNull) { + throw new IllegalArgumentException("Cannot add a null language."); + } + + return super.addAll(index, elements); + } + }; + } + return this.languages; + } + + /** + * Returns an optional location reference. This is useful when the representation is accessible + * from a location separate from the representation's resource URI, for example, when content + * negotiation occurs.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Location" + * header. + * + * @return The identifier. + */ + public Reference getLocationRef() { + return this.locationRef; + } + + /** + * Returns the media type.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Type" header. + * + * @return The media type. + */ + public MediaType getMediaType() { + return this.mediaType; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode( + super.hashCode(), characterSet, encodings, locationRef, languages, mediaType); + } + + /** + * Indicates if the current variant includes the given variant. + * + * @param other The other variant. + * @return True if the current variant includes the other. + */ + public boolean includes(Variant other) { + boolean result = other != null; + + // Compare the character set + if (result) { + result = + (getCharacterSet() == null) + || getCharacterSet().includes(other.getCharacterSet()); + } + + // Compare the media type + if (result) { + result = (getMediaType() == null) || getMediaType().includes(other.getMediaType()); + } + + // Compare the languages + if (result) { + result = + (getLanguages().isEmpty()) + || getLanguages().contains(Language.ALL) + || new HashSet<>(getLanguages()).containsAll(other.getLanguages()); + } + + // Compare the encodings + if (result) { + result = + (getEncodings().isEmpty()) + || getEncodings().contains(Encoding.ALL) + || new HashSet<>(getEncodings()).containsAll(other.getEncodings()); + } + + return result; + } + + /** + * Indicates if the current variant is compatible with the given variant. + * + * @param other The other variant. + * @return True if the current variant is compatible with the other. + */ + public boolean isCompatible(Variant other) { + return (other != null) && (includes(other) || other.includes(this)); + } + + /** + * Sets the character set or null if not applicable.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Type" header. + * + * @param characterSet The character set or null if not applicable. + */ + public void setCharacterSet(CharacterSet characterSet) { + this.characterSet = characterSet; + } + + /** + * Sets the list of encodings applied to the entity-body.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Encoding" + * header. + * + * @param encodings The list of encodings applied to the entity-body. + */ + public void setEncodings(List encodings) { + this.encodings = encodings; + } + + /** + * Sets the list of languages.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Language" + * header. + * + * @param languages The list of languages. + */ + public void setLanguages(List languages) { + this.languages = languages; + } + + /** + * Sets the optional identifier. This is useful when the representation is accessible from a + * location separate from the representation's resource URI, for example, when content + * negotiation occurs.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Location" + * header. + * + * @param location The location reference. + */ + public void setLocationRef(Reference location) { + this.locationRef = location; + } + + /** + * Sets the identifier from a URI string.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Location" + * header. + * + * @param locationUri The location URI to parse. + */ + public void setLocationRef(String locationUri) { + setLocationRef(new Reference(locationUri)); + } + + /** + * Sets the media type.
+ *
+ * Note that when used with HTTP connectors, this property maps to the "Content-Type" header. + * + * @param mediaType The media type. + */ + public void setMediaType(MediaType mediaType) { + this.mediaType = mediaType; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("["); + boolean first = true; + + if (getMediaType() != null) { + first = false; + sb.append(getMediaType()); + } + + if (getCharacterSet() != null) { + if (!first) { + sb.append(","); + } else { + first = false; + } + + sb.append(getCharacterSet()); + } + + if (!getLanguages().isEmpty()) { + if (!first) { + sb.append(","); + } else { + first = false; + } + + sb.append(getLanguages()); + } + + if (!getEncodings().isEmpty()) { + if (!first) { + sb.append(","); + } else { + first = false; + } + + sb.append(getEncodings()); + } + + sb.append("]"); + return sb.toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java index 0c8ddd8216..488e272bb5 100644 --- a/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java @@ -1,56 +1,52 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.restlet.data.MediaType; -import org.restlet.engine.io.IoUtils; - import java.io.IOException; import java.io.Reader; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; /** - * Representation based on a BIO characters writer. This class is a good basis - * to write your own representations, especially for the dynamic and large ones. - *
+ * Representation based on a BIO characters writer. This class is a good basis to write your own + * representations, especially for the dynamic and large ones.
*
- * For this you just need to create a subclass and override the abstract - * Representation.write(Writer) method. This method will later be called back by - * the connectors when the actual representation's content is needed. - * + * For this you need to create a subclass and override the abstract Representation.write(Writer) + * method. This method will later be called back by the connectors when the actual representation's + * content is needed. + * * @author Jerome Louvel */ public abstract class WriterRepresentation extends CharacterRepresentation { - /** - * Constructor. - * - * @param mediaType The representation's mediaType. - */ - public WriterRepresentation(MediaType mediaType) { - super(mediaType); - } - - /** - * Constructor. - * - * @param mediaType The representation's mediaType. - * @param expectedSize The expected writer size in bytes. - */ - public WriterRepresentation(MediaType mediaType, long expectedSize) { - super(mediaType); - setSize(expectedSize); - } - - @Override - public Reader getReader() throws IOException { - return IoUtils.getReader(this); - } - + /** + * Constructor. + * + * @param mediaType The representation's mediaType. + */ + public WriterRepresentation(MediaType mediaType) { + super(mediaType); + } + + /** + * Constructor. + * + * @param mediaType The representation's mediaType. + * @param expectedSize The expected writer size in bytes. + */ + public WriterRepresentation(MediaType mediaType, long expectedSize) { + super(mediaType); + setSize(expectedSize); + } + + @Override + public Reader getReader() throws IOException { + return IoUtils.getReader(this); + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java b/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java index f6d3a253b6..a527a7373f 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java +++ b/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java @@ -1,29 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** - * Marker interface for RESTful resource proxies. This allows you to retrieve - * and manipulate the underlying {@link ClientResource} of a dynamic client - * proxy generated by the {@link ClientResource#create(String, Class)} method - * for example, or by {@link ClientResource#getChild(String, Class)}. - * + * Marker interface for RESTful resource proxies. This allows you to retrieve and manipulate the + * underlying {@link ClientResource} of a dynamic client proxy generated by the {@link + * ClientResource#create(String, Class)} method for example, or by {@link + * ClientResource#getChild(String, Class)}. + * * @author Jerome Louvel */ public interface ClientProxy { - /** - * Returns the wrapped client resource. - * - * @return The wrapped client resource. - */ - ClientResource getClientResource(); - + /** + * Returns the wrapped client resource. + * + * @return The wrapped client resource. + */ + ClientResource getClientResource(); } diff --git a/org.restlet/src/main/java/org/restlet/resource/ClientResource.java b/org.restlet/src/main/java/org/restlet/resource/ClientResource.java index 6dc199624b..4386d92b29 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ClientResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ClientResource.java @@ -1,1852 +1,1845 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; -import org.restlet.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.Uniform; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.ClientInfo; +import org.restlet.data.Conditions; +import org.restlet.data.Cookie; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Method; +import org.restlet.data.Parameter; +import org.restlet.data.Protocol; +import org.restlet.data.Range; +import org.restlet.data.Reference; import org.restlet.data.Status; -import org.restlet.data.*; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import org.restlet.representation.Variant; import org.restlet.util.Series; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - /** * Client-side resource. Acts like a proxy of a target resource.
- * This class changes the semantics of the {@link Resource#getRequest()} and - * {@link Resource#getResponse()} methods. Since a clientResource may receive - * severals responses for a single request (in case of interim response), the - * {@link #getResponse()} method returns the last received response object. The - * Request object returned by the {@link #getRequest()} is actually a prototype - * which is cloned (except the representation) just before the {@link #handle()} - * method is called.
- * Users must be aware that by most representations can only be read or written - * once. Some others, such as {@link StringRepresentation} stored the entity in - * memory which can be read several times but has the drawback to consume - * memory.
- * Concurrency note: instances of the class are not designed to be shared among - * several threads. If thread-safety is necessary, consider using the - * lower-level {@link Client} class instead. - * + * This class changes the semantics of the {@link Resource#getRequest()} and {@link + * Resource#getResponse()} methods. Since a clientResource may receive severals responses for a + * single request (in case of interim response), the {@link #getResponse()} method returns the last + * received response object. The Request object returned by the {@link #getRequest()} is actually a + * prototype which is cloned (except the representation) just before the {@link #handle()} method is + * called.
+ * Users must be aware that by most representations can only be read or written once. Some others, + * such as {@link StringRepresentation} stored the entity in memory which can be read several times + * but has the drawback to consume memory.
+ * Concurrency note: instances of the class are not designed to be shared among several threads. If + * thread-safety is necessary, consider using the lower-level {@link Client} class instead. + * * @author Jerome Louvel */ public class ClientResource extends Resource { - /** - * Creates a client resource that proxy calls to the given Java interface into - * Restlet method calls. It basically creates a new instance of - * {@link ClientResource} and invokes the {@link #wrap(Class)} method. - * - * @param The proxified interface. - * @param context The context. - * @param reference The target reference. - * @param resourceInterface The annotated resource interface class to proxy. - * @return The proxy instance. - */ - public static T create(Context context, Reference reference, Class resourceInterface) { - ClientResource clientResource = new ClientResource(context, reference); - return clientResource.wrap(resourceInterface); - } - - /** - * Creates a client resource that proxy calls to the given Java interface into - * Restlet method calls. It basically creates a new instance of - * {@link ClientResource} and invokes the {@link #wrap(Class)} method. - * - * @param The proxified interface. - * @param reference The target reference. - * @param resourceInterface The annotated resource interface class to proxy. - * @return The proxy instance. - */ - public static T create(Reference reference, Class resourceInterface) { - return create(null, reference, resourceInterface); - } - - /** - * Creates a client resource that proxy calls to the given Java interface into - * Restlet method calls. It basically creates a new instance of - * {@link ClientResource} and invokes the {@link #wrap(Class)} method. - * - * @param The proxified interface. - * @param uri The target URI. - * @param resourceInterface The annotated resource interface class to proxy. - * @return The proxy instance. - */ - public static T create(String uri, Class resourceInterface) { - return create(null, new Reference(uri), resourceInterface); - } - - /** Indicates if redirections should be automatically followed. */ - private volatile boolean followingRedirects; - - /** - * Indicates the maximum number of redirections that can be automatically - * followed for a single call. - */ - private volatile int maxRedirects; - - /** The next Restlet. */ - private volatile Uniform next; - - /** Indicates if the next Restlet has been created. */ - private volatile boolean nextCreated; - - /** - * Indicates if transient or unknown size request entities should be buffered - * before being sent. - */ - private volatile boolean requestEntityBuffering; - - /** - * Indicates if transient or unknown size response entities should be buffered - * after being received. - */ - private volatile boolean responseEntityBuffering; - - /** Number of retry attempts before reporting an error. */ - private volatile int retryAttempts; - - /** Delay in milliseconds between two retry attempts. */ - private volatile long retryDelay; - - /** Indicates if idempotent requests should be retried on error. */ - private volatile boolean retryOnError; - - /** - * Empty constructor. - */ - protected ClientResource() { - } - - /** - * Constructor. - * - * @param resource The client resource to copy. - */ - public ClientResource(ClientResource resource) { - Request request = new Request(resource.getRequest()); - Response response = new Response(request); - this.next = resource.getNext(); - this.maxRedirects = resource.getMaxRedirects(); - this.retryOnError = resource.isRetryOnError(); - this.retryDelay = resource.getRetryDelay(); - this.retryAttempts = resource.getRetryAttempts(); - - this.followingRedirects = resource.isFollowingRedirects(); - this.requestEntityBuffering = resource.isRequestEntityBuffering(); - this.responseEntityBuffering = resource.isResponseEntityBuffering(); - setApplication(resource.getApplication()); - - init(resource.getContext(), request, response); - } - - /** - * Constructor. - * - * @param context The context. - * @param uri The target URI. - */ - public ClientResource(Context context, java.net.URI uri) { - this(context, Method.GET, uri); - } - - /** - * Constructor. - * - * @param context The context. - * @param method The method to call. - * @param uri The target URI. - */ - public ClientResource(Context context, Method method, java.net.URI uri) { - this(context, method, new Reference(uri)); - } - - /** - * Constructor. - * - * @param context The context. - * @param method The method to call. - * @param reference The target reference. - */ - public ClientResource(Context context, Method method, Reference reference) { - this(context, new Request(method, reference), new Response(null)); - } - - /** - * Constructor. - * - * @param context The context. - * @param method The method to call. - * @param uri The target URI. - */ - public ClientResource(Context context, Method method, String uri) { - this(context, method, new Reference(uri)); - } - - /** - * Constructor. - * - * @param context The context. - * @param reference The target reference. - */ - public ClientResource(Context context, Reference reference) { - this(context, Method.GET, reference); - } - - /** - * Constructor. - * - * @param context The current context. - * @param request The handled request. - */ - public ClientResource(Context context, Request request) { - this(context, request, null); - } - - /** - * Constructor. - * - * @param context The current context. - * @param request The handled request. - * @param response The handled response. - */ - public ClientResource(Context context, Request request, Response response) { - if (context == null) { - context = Context.getCurrent(); - } - - // Don't remove this line. - // See other constructor ClientResource(Context, Method, Reference) - response.setRequest(request); - - this.maxRedirects = 10; - this.retryOnError = true; - this.retryDelay = 2000L; - this.retryAttempts = 2; - this.followingRedirects = true; - this.requestEntityBuffering = false; - this.responseEntityBuffering = false; - init(context, request, response); - } - - /** - * Constructor. - * - * @param context The context. - * @param uri The target URI. - */ - public ClientResource(Context context, String uri) { - this(context, Method.GET, uri); - } - - /** - * Constructor. - * - * @param uri The target URI. - */ - public ClientResource(java.net.URI uri) { - this(Context.getCurrent(), null, uri); - } - - /** - * Constructor. - * - * @param method The method to call. - * @param uri The target URI. - */ - public ClientResource(Method method, java.net.URI uri) { - this(Context.getCurrent(), method, uri); - } - - /** - * Constructor. - * - * @param method The method to call. - * @param reference The target reference. - */ - public ClientResource(Method method, Reference reference) { - this(Context.getCurrent(), method, reference); - } - - /** - * Constructor. - * - * @param method The method to call. - * @param uri The target URI. - */ - public ClientResource(Method method, String uri) { - this(Context.getCurrent(), method, uri); - } - - /** - * Constructor. - * - * @param reference The target reference. - */ - public ClientResource(Reference reference) { - this(Context.getCurrent(), null, reference); - } - - /** - * Constructor. - * - * @param request The handled request. - */ - public ClientResource(Request request) { - this(request, new Response(request)); - } - - /** - * Constructor. - * - * @param request The handled request. - * @param response The handled response. - */ - public ClientResource(Request request, Response response) { - this(Context.getCurrent(), request, response); - } - - /** - * Constructor. - * - * @param uri The target URI. - */ - public ClientResource(String uri) { - this(Context.getCurrent(), Method.GET, uri); - } - - /** - * Updates the client preferences to accept the given metadata (media types, - * character sets, etc.) with a 1.0 quality in addition to existing ones. - * - * @param metadata The metadata to accept. - * @see ClientInfo#accept(Metadata...) - */ - public void accept(Metadata... metadata) { - getClientInfo().accept(metadata); - } - - /** - * Updates the client preferences to accept the given metadata (media types, - * character sets, etc.) with a given quality in addition to existing ones. - * - * @param metadata The metadata to accept. - * @param quality The quality to set. - * @see ClientInfo#accept(Metadata, float) - */ - public void accept(Metadata metadata, float quality) { - getClientInfo().accept(metadata, quality); - } - - /** - * Adds a parameter to the query component. The name and value are automatically - * encoded if necessary. - * - * @param parameter The parameter to add. - * @return The updated reference. - * @see Reference#addQueryParameter(Parameter) - */ - public Reference addQueryParameter(Parameter parameter) { - return getReference().addQueryParameter(parameter); - } - - /** - * Adds a parameter to the query component. The name and value are automatically - * encoded if necessary. - * - * @param name The parameter name. - * @param value The optional parameter value. - * @return The updated reference. - * @see Reference#addQueryParameter(String, String) - */ - public Reference addQueryParameter(String name, String value) { - return getReference().addQueryParameter(name, value); - } - - /** - * Adds several parameters to the query component. The name and value are - * automatically encoded if necessary. - * - * @param parameters The parameters to add. - * @return The updated reference. - * @see Reference#addQueryParameters(Iterable) - */ - public Reference addQueryParameters(Iterable parameters) { - return getReference().addQueryParameters(parameters); - } - - /** - * Adds a segment at the end of the path. If the current path doesn't end with a - * slash character, one is inserted before the new segment value. The value is - * automatically encoded if necessary. - * - * @param value The segment value to add. - * @return The updated reference. - * @see Reference#addSegment(String) - */ - public Reference addSegment(String value) { - return getReference().addSegment(value); - } - - /** - * Creates a next Restlet is no one is set. By default, it creates a new - * {@link Client} based on the protocol of the resource's URI reference. - * - * @return The created next Restlet or null. - */ - protected Uniform createNext() { - Uniform result = null; - - // Prefer the outbound root - result = getApplication().getOutboundRoot(); - - if ((result == null) && (getContext() != null)) { - // Try using directly the client dispatcher - result = getContext().getClientDispatcher(); - } - - if (result == null) { - // As a final option, try creating a client connector - Protocol rProtocol = getProtocol(); - Reference rReference = getReference(); - Protocol protocol = (rProtocol != null) ? rProtocol - : (rReference != null) ? rReference.getSchemeProtocol() : null; - - if (protocol != null) { - org.restlet.engine.util.TemplateDispatcher dispatcher = new org.restlet.engine.util.TemplateDispatcher(); - dispatcher.setContext(getContext()); - dispatcher.setNext(new Client(protocol)); - result = dispatcher; - } - } - - return result; - } - - /** - * Creates a new request by cloning the one wrapped by this class. - * - * @return The new response. - * @see #getRequest() - */ - public Request createRequest() { - return new Request(getRequest()); - } - - /** - * Creates a new response for the given request. - * - * @param request The associated request. - * @return The new response. - */ - protected Response createResponse(Request request) { - return new Response(request); - } - - /** - * Deletes the target resource and all its representations. If a success status - * is not returned, then a resource exception is thrown. - * - * @return The optional response entity. - * @see HTTP - * DELETE method - */ - public Representation delete() throws ResourceException { - return handle(Method.DELETE); - } - - /** - * Deletes the target resource and all its representations. If a success status - * is not returned, then a resource exception is thrown. - * - * @param The expected type for the response entity. - * @param resultClass The expected class for the response entity object. - * @return The response entity object. - * @see HTTP - * DELETE method - */ - public T delete(Class resultClass) throws ResourceException { - return handle(Method.DELETE, resultClass); - } - - /** - * Deletes the target resource and all its representations. If a success status - * is not returned, then a resource exception is thrown. - * - * @param mediaType The media type of the representation to retrieve. - * @return The representation matching the given media type. - * @throws ResourceException - * @see HTTP - * DELETE method - */ - public Representation delete(MediaType mediaType) throws ResourceException { - return handle(Method.DELETE, mediaType); - } - - /** - * By default, it throws a new resource exception. Call - * {@link #doError(org.restlet.data.Status, org.restlet.Request, org.restlet.Response)}. - * - * @param request The associated request. - * @param response The associated response. - */ - public void doError(Request request, Response response) { - doError(response.getStatus(), request, response); - } - - /** - * By default, it throws a new resource exception. Call - * {@link #doError(org.restlet.data.Status, org.restlet.Request, org.restlet.Response)}. - * - * @param errorStatus The error status received. - */ - @Override - public void doError(Status errorStatus) { - doError(errorStatus, getRequest(), getResponse()); - } - - /** - * By default, it throws a new resource exception. This can be overridden to - * provide a different behavior. - * - * @param errorStatus The error status received. - * @param request The associated request. - * @param response The associated response. - */ - public void doError(Status errorStatus, Request request, Response response) { - throw new ResourceException(errorStatus, request, response); - } - - /** - * Releases the resource by stopping any connector automatically created and - * associated to the "next" property (see {@link #getNext()} method. - */ - @Override - protected void doRelease() throws ResourceException { - if ((getNext() != null) && this.nextCreated) { - if (getNext() instanceof Restlet) { - try { - ((Restlet) getNext()).stop(); - } catch (Exception e) { - throw new ResourceException(e); - } - } - - setNext(null); - } - } - - /** - * Attempts to {@link #release()} the resource. - */ - @Override - protected void finalize() throws Throwable { - release(); - super.finalize(); - } - - /** - * Represents the resource using content negotiation to select the best variant - * based on the client preferences. Note that the client preferences will be - * automatically adjusted, but only for this request. If you want to change them - * once for all, you can use the {@link #getClientInfo()} method.
- *
- * If a success status is not returned, then a resource exception is thrown. - * - * @return The best representation. - * @throws ResourceException - * @see HTTP GET - * method - */ - public Representation get() throws ResourceException { - return handle(Method.GET); - } - - /** - * Represents the resource in the given object class. Note that the client - * preferences will be automatically adjusted, but only for this request. If you - * want to change them once for all, you can use the {@link #getClientInfo()} - * method.
- *
- * If a success status is not returned, then a resource exception is thrown. - * - * @param The expected type for the response entity. - * @param resultClass The expected class for the response entity object. - * @return The response entity object. - * @throws ResourceException - * @see HTTP GET - * method - */ - public T get(Class resultClass) throws ResourceException { - return handle(Method.GET, resultClass); - } - - /** - * Represents the resource using a given media type. Note that the client - * preferences will be automatically adjusted, but only for this request. If you - * want to change them once for all, you can use the {@link #getClientInfo()} - * method.
- *
- * If a success status is not returned, then a resource exception is thrown. - * - * @param mediaType The media type of the representation to retrieve. - * @return The representation matching the given media type. - * @throws ResourceException - * @see HTTP GET - * method - */ - public Representation get(MediaType mediaType) throws ResourceException { - return handle(Method.GET, mediaType); - } - - /** - * Returns the attribute value by looking up the given name in the response - * attributes maps. The toString() method is then invoked on the attribute - * value. - * - * @param name The attribute name. - * @return The response attribute value. - */ - public String getAttribute(String name) { - Object value = getResponseAttributes().get(name); - return (value == null) ? null : value.toString(); - } - - /** - * Returns the child resource defined by its URI relatively to the current - * resource. The child resource is defined in the sense of hierarchical URIs. If - * the resource URI is not hierarchical, then an exception is thrown. - * - * @param relativeRef The URI reference of the child resource relatively to the - * current resource seen as the parent resource. - * @return The child resource. - * @throws ResourceException - */ - public ClientResource getChild(Reference relativeRef) throws ResourceException { - ClientResource result = null; - - if ((relativeRef != null) && relativeRef.isRelative()) { - result = new ClientResource(this); - result.setReference(new Reference(getReference().getTargetRef(), relativeRef).getTargetRef()); - } else { - doError(Status.CLIENT_ERROR_BAD_REQUEST, "The child URI is not relative."); - } - - return result; - } - - /** - * Wraps the child client resource to proxy calls to the given Java interface - * into Restlet method calls. The child resource is defined in the sense of - * hierarchical URIs. If the resource URI is not hierarchical, then an exception - * is thrown. - * - * @param The proxified interface. - * @param relativeRef The URI reference of the child resource relatively - * to the current resource seen as the parent resource. - * @param resourceInterface The annotated resource interface class to proxy. - * @return The proxy instance. - */ - public T getChild(Reference relativeRef, Class resourceInterface) throws ResourceException { - T result = null; - ClientResource childResource = getChild(relativeRef); - - if (childResource != null) { - result = childResource.wrap(resourceInterface); - } - - return result; - } - - /** - * Returns the child resource defined by its URI relatively to the current - * resource. The child resource is defined in the sense of hierarchical URIs. If - * the resource URI is not hierarchical, then an exception is thrown. - * - * @param relativeUri The URI of the child resource relatively to the current - * resource seen as the parent resource. - * @return The child resource. - * @throws ResourceException - */ - public ClientResource getChild(String relativeUri) throws ResourceException { - return getChild(new Reference(relativeUri)); - } - - /** - * Wraps the child client resource to proxy calls to the given Java interface - * into Restlet method calls. The child resource is defined in the sense of - * hierarchical URIs. If the resource URI is not hierarchical, then an exception - * is thrown. - * - * @param The proxified interface. - * @param relativeUri The URI of the child resource relatively to the - * current resource seen as the parent resource. - * @param resourceInterface The annotated resource interface class to proxy. - * @return The proxy instance. - */ - public T getChild(String relativeUri, Class resourceInterface) throws ResourceException { - return getChild(new Reference(relativeUri), resourceInterface); - } - - /** - * Returns the maximum number of redirections that can be automatically followed - * for a single call. Default value is 10. - * - * @return The maximum number of redirections that can be automatically followed - * for a single call. - */ - public int getMaxRedirects() { - return maxRedirects; - } - - /** - * Returns the next Restlet. By default, it is the client dispatcher if a - * context is available. - * - * @return The next Restlet or null. - */ - public Uniform getNext() { - Uniform result = this.next; - - if (result == null) { - synchronized (this) { - if (result == null) { - result = createNext(); - - if (result != null) { - setNext(result); - this.nextCreated = true; - } - } - } - } - - return result; - } - - /** - * Returns the callback invoked on response reception. If the value is not null, - * then the associated request will be executed asynchronously. - * - * @return The callback invoked on response reception. - */ - public Uniform getOnResponse() { - return getRequest().getOnResponse(); - } - - /** - * Returns the callback invoked after sending the request. - * - * @return The callback invoked after sending the request. - */ - public Uniform getOnSent() { - return getRequest().getOnSent(); - } - - /** - * Returns the parent resource. The parent resource is defined in the sense of - * hierarchical URIs. If the resource URI is not hierarchical, then an exception - * is thrown. - * - * @return The parent resource. - */ - public ClientResource getParent() throws ResourceException { - ClientResource result = null; - - if (getReference().isHierarchical()) { - result = new ClientResource(this); - result.setReference(getReference().getParentRef()); - } else { - doError(Status.CLIENT_ERROR_BAD_REQUEST, "The resource URI is not hierarchical."); - } - - return result; - } - - /** - * Wraps the parent client resource to proxy calls to the given Java interface - * into Restlet method calls. The parent resource is defined in the sense of - * hierarchical URIs. If the resource URI is not hierarchical, then an exception - * is thrown. - * - * @param The proxified interface. - * @param resourceInterface The annotated resource interface class to proxy. - * @return The proxy instance. - */ - public T getParent(Class resourceInterface) throws ResourceException { - T result = null; - - ClientResource parentResource = getParent(); - if (parentResource != null) { - result = parentResource.wrap(resourceInterface); - } - - return result; - } - - /** - * Returns the number of retry attempts before reporting an error. Default value - * is 2. - * - * @return The number of retry attempts before reporting an error. - */ - public int getRetryAttempts() { - return retryAttempts; - } - - /** - * Returns the delay in milliseconds between two retry attempts. Default value - * is 2 seconds. - * - * @return The delay in milliseconds between two retry attempts. - */ - public long getRetryDelay() { - return retryDelay; - } - - /** - * Handles the call by invoking the next handler. The prototype request is - * retrieved via {@link #getRequest()} and cloned and the response is set as the - * latest with {@link #setResponse(Response)}. If necessary the - * {@link #setNext(Uniform)} method is called as well with a {@link Client} - * instance matching the request protocol. - * - * @return The optional response entity. - * @see #getNext() - */ - @Override - public Representation handle() { - Response response = handleOutbound(createRequest()); - return (response == null) ? null : response.getEntity(); - } - - /** - * Handles the call by cloning the prototype request, setting the method and - * entity. - * - * @param method The request method to use. - * @return The optional response entity. - */ - protected Representation handle(Method method) { - return handle(method, (Representation) null); - } - - /** - * Handles the call by cloning the prototype request, setting the method and - * entity. - * - * @param The expected type for the response entity. - * @param method The request method to use. - * @param resultClass The expected class for the response entity object. - * @return The response entity object. - * @throws ResourceException - */ - protected T handle(Method method, Class resultClass) throws ResourceException { - return handle(method, null, resultClass); - } - - /** - * Handles the call by cloning the prototype request, setting the method and - * entity. - * - * @param method The request method to use. - * @param mediaType The preferred result media type. - * @return The optional response entity. - */ - protected Representation handle(Method method, MediaType mediaType) { - return handle(method, null, mediaType); - } - - /** - * Handles an object entity. Automatically serializes the object using the - * {@link org.restlet.service.ConverterService}. - * - * @param The expected type for the response entity. - * @param method The request method to use. - * @param entity The object entity to post. - * @param resultClass The class of the response entity. - * @return The response object entity. - * @throws ResourceException - */ - protected T handle(Method method, Object entity, Class resultClass) throws ResourceException { - org.restlet.service.ConverterService cs = getConverterService(); - ClientInfo clientInfo = getClientInfo(); - - if (clientInfo.getAcceptedMediaTypes().isEmpty()) { - cs.updatePreferences(clientInfo.getAcceptedMediaTypes(), resultClass); - } - - // Prepare the request by cloning the prototype request - Request request = createRequest(); - request.setMethod(method); - request.setClientInfo(clientInfo); - - if (entity != null) { - List entityVariants; - try { - entityVariants = cs.getVariants(entity.getClass(), null); - request.setEntity(toRepresentation(entity, - getConnegService().getPreferredVariant(entityVariants, request, getMetadataService()))); - } catch (IOException e) { - throw new ResourceException(e); - } - } else { - request.setEntity(null); - } - - // Actually handle the call - Response response = handleOutbound(request); - Representation responseEntity = handleInbound(response); - return toObject(responseEntity, resultClass); - } - - /** - * Handles the call by cloning the prototype request, setting the method and - * entity. - * - * @param method The request method to use. - * @param entity The request entity to set. - * @return The optional response entity. - */ - protected Representation handle(Method method, Representation entity) { - return handle(method, entity, getClientInfo()); - } - - /** - * Handles the call by cloning the prototype request, setting the method and - * entity. - * - * @param method The request method to use. - * @param entity The request entity to set. - * @param clientInfo The client preferences. - * @return The optional response entity. - */ - protected Representation handle(Method method, Representation entity, ClientInfo clientInfo) { - // Prepare the request by cloning the prototype request - Request request = createRequest(); - request.setMethod(method); - request.setEntity(entity); - request.setClientInfo(clientInfo); - - // Actually handle the call - Response response = handleOutbound(request); - return handleInbound(response); - } - - /** - * Handles the call by cloning the prototype request, setting the method and - * entity. - * - * @param method The request method to use. - * @param entity The request entity to set. - * @param mediaType The preferred result media type. - * @return The optional response entity. - */ - protected Representation handle(Method method, Representation entity, MediaType mediaType) { - return handle(method, entity, new ClientInfo(mediaType)); - } - - /** - * Handle the call and follow redirection for safe methods. - * - * @param request The request to send. - * @param response The response to update. - * @param references The references that caused a redirection to prevent - * infinite loops. - * @param retryAttempt The number of remaining attempts. - * @param next The next handler handling the call. - */ - protected void handle(Request request, Response response, List references, int retryAttempt, - Uniform next) { - if (next != null) { - // Check if request entity buffering must be done - if (isRequestEntityBuffering()) { - request.bufferEntity(); - } - - // Actually handle the call - next.handle(request, response); - - if (isRetryOnError() && response.getStatus().isRecoverableError() && request.getMethod().isIdempotent() - && (retryAttempt < getRetryAttempts()) - && ((request.getEntity() == null) || request.getEntity().isAvailable())) { - retry(request, response, references, retryAttempt, next); - } else if (isFollowingRedirects() && response.getStatus().isRedirection() - && (response.getLocationRef() != null)) { - boolean doRedirection = false; - - if (request.getMethod().isSafe()) { - doRedirection = true; - } else { - if (Status.REDIRECTION_SEE_OTHER.equals(response.getStatus())) { - // The user agent is redirected using the GET method - request.setMethod(Method.GET); - request.setEntity(null); - doRedirection = true; - } else if (Status.REDIRECTION_USE_PROXY.equals(response.getStatus())) { - doRedirection = true; - } - } - - if (doRedirection) { - redirect(request, response, references, retryAttempt, next); - } else { - getLogger().fine("Unable to redirect the client call after a response" + response); - } - } - - // Check if response entity buffering must be done - if (isResponseEntityBuffering()) { - response.bufferEntity(); - } - } else { - getLogger().log(Level.WARNING, "Request ignored as no next Restlet is available"); - } - } - - /** - * Handles the inbound call. Note that only synchronous calls are processed. - * - * @param response - * @return The response's entity, if any. - */ - public Representation handleInbound(Response response) { - if (response == null) { - return null; - } - - // Verify that the request was synchronous - if (response.getRequest().isSynchronous()) { - if (response.getStatus().isError()) { - doError(response.getStatus()); - return null; - } - return response.getEntity(); - } - - return null; - } - - /** - * Handles the outbound call by invoking the next handler. - * - * @param request The request to handle. - * @return The response created. - * @see #getNext() - */ - public Response handleOutbound(Request request) { - Response response = createResponse(request); - Uniform next = getNext(); - - if (next != null) { - // Effectively handle the call - handle(request, response, null, 0, next); - - // Update the last received response. - setResponse(response); - } else { - getLogger().warning("Unable to process the call for a client resource. No next Restlet has been provided."); - } - - return response; - } - - /** - * Indicates if there is a next Restlet. - * - * @return True if there is a next Restlet. - */ - public boolean hasNext() { - return getNext() != null; - } - - /** - * Represents the resource using content negotiation to select the best variant - * based on the client preferences. This method is identical to {@link #get()} - * but doesn't return the actual content of the representation, only its - * metadata.
- *
- * Note that the client preferences will be automatically adjusted, but only for - * this request. If you want to change them once for all, you can use the - * {@link #getClientInfo()} method.
- *
- * If a success status is not returned, then a resource exception is thrown. - * - * @return The best representation. - * @throws ResourceException - * @see HTTP HEAD - * method - */ - public Representation head() throws ResourceException { - return handle(Method.HEAD); - } - - /** - * Represents the resource using a given media type. This method is identical to - * {@link #get(MediaType)} but doesn't return the actual content of the - * representation, only its metadata.
- *
- * Note that the client preferences will be automatically adjusted, but only for - * this request. If you want to change them once for all, you can use the - * {@link #getClientInfo()} method.
- *
- * If a success status is not returned, then a resource exception is thrown. - * - * @param mediaType The media type of the representation to retrieve. - * @return The representation matching the given media type. - * @throws ResourceException - * @see HTTP HEAD - * method - */ - public Representation head(MediaType mediaType) throws ResourceException { - return handle(Method.HEAD, mediaType); - } - - /** - * Indicates if redirections are followed. - * - * @return True if redirections are followed. - */ - public boolean isFollowingRedirects() { - return followingRedirects; - } - - /** - * Indicates if transient or unknown size response entities should be buffered - * after being received. This is useful to increase the chance of being able to - * resubmit a failed request due to network error, or to prevent chunked - * encoding from being used an HTTP connector. - * - * @return True if transient response entities should be buffered after being - * received. - */ - public boolean isRequestEntityBuffering() { - return requestEntityBuffering; - } - - /** - * Indicates if transient or unknown size response entities should be buffered - * after being received. This is useful to be able to systematically reuse and - * process a response entity several times after retrieval. - * - * @return True if transient response entities should be buffered after being - * received. - */ - public boolean isResponseEntityBuffering() { - return responseEntityBuffering; - } - - /** - * Indicates if idempotent requests should be retried on error. Default value is - * true. - * - * @return True if idempotent requests should be retried on error. - */ - public boolean isRetryOnError() { - return retryOnError; - } - - /** - * Describes the resource using content negotiation to select the best variant - * based on the client preferences. If a success status is not returned, then a - * resource exception is thrown. - * - * @return The best description. - * @throws ResourceException - * @see HTTP - * OPTIONS method - */ - public Representation options() throws ResourceException { - return handle(Method.OPTIONS); - } - - /** - * Describes the resource using a given media type. If a success status is not - * returned, then a resource exception is thrown. - * - * @param The expected type for the response entity. - * @param resultClass The expected class for the response entity object. - * @return The response entity object. - * @throws ResourceException - * @see HTTP - * OPTIONS method - */ - public T options(Class resultClass) throws ResourceException { - return handle(Method.OPTIONS, resultClass); - } - - /** - * Describes the resource using a given media type. If a success status is not - * returned, then a resource exception is thrown. - * - * @param mediaType The media type of the representation to retrieve. - * @return The matched description or null. - * @throws ResourceException - * @see HTTP - * OPTIONS method - */ - public Representation options(MediaType mediaType) throws ResourceException { - return handle(Method.OPTIONS, mediaType); - } - - /** - * Patches a resource with the given object as delta state. Automatically - * serializes the object using the {@link org.restlet.service.ConverterService}. - * - * @param entity The object entity containing the patch. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP PATCH method - */ - public Representation patch(Object entity) throws ResourceException { - try { - return patch(toRepresentation(entity)); - } catch (IOException e) { - throw new ResourceException(e); - } - } - - /** - * Patches a resource with the given object as delta state. Automatically - * serializes the object using the {@link org.restlet.service.ConverterService}. - * - * @param The expected type for the response entity. - * @param entity The object entity containing the patch. - * @param resultClass The class of the response entity. - * @return The response object entity. - * @throws ResourceException - * @see HTTP PATCH method - */ - public T patch(Object entity, Class resultClass) throws ResourceException { - return handle(Method.PATCH, entity, resultClass); - } - - /** - * Patches a resource with the given object as delta state. Automatically - * serializes the object using the {@link org.restlet.service.ConverterService}. - * - * @param entity The object entity containing the patch. - * @param mediaType The media type of the representation to retrieve. - * @return The response object entity. - * @throws ResourceException - * @see HTTP PATCH method - */ - public Representation patch(Object entity, MediaType mediaType) throws ResourceException { - try { - return handle(Method.PATCH, toRepresentation(entity), mediaType); - } catch (IOException e) { - throw new ResourceException(e); - } - } - - /** - * Patches a resource with the given representation as delta state. If a success - * status is not returned, then a resource exception is thrown. - * - * @param entity The request entity containing the patch. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP PATCH method - */ - public Representation patch(Representation entity) throws ResourceException { - return handle(Method.PATCH, entity); - } - - /** - * Posts an object entity. Automatically serializes the object using the - * {@link org.restlet.service.ConverterService}. - * - * @param entity The object entity to post. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP POST - * method - */ - public Representation post(Object entity) throws ResourceException { - try { - return post(toRepresentation(entity)); - } catch (IOException e) { - throw new ResourceException(e); - } - } - - /** - * Posts an object entity. Automatically serializes the object using the - * {@link org.restlet.service.ConverterService}. - * - * @param The expected type for the response entity. - * @param entity The object entity to post. - * @param resultClass The class of the response entity. - * @return The response object entity. - * @throws ResourceException - * @see HTTP POST - * method - */ - public T post(Object entity, Class resultClass) throws ResourceException { - return handle(Method.POST, entity, resultClass); - } - - /** - * Posts an object entity. Automatically serializes the object using the - * {@link org.restlet.service.ConverterService}. - * - * @param entity The object entity to post. - * @param mediaType The media type of the representation to retrieve. - * @return The response object entity. - * @throws ResourceException - * @see HTTP POST - * method - */ - public Representation post(Object entity, MediaType mediaType) throws ResourceException { - try { - return handle(Method.POST, toRepresentation(entity), mediaType); - } catch (IOException e) { - throw new ResourceException(e); - } - } - - /** - * Posts a representation. If a success status is not returned, then a resource - * exception is thrown. - * - * @param entity The posted entity. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP POST - * method - */ - public Representation post(Representation entity) throws ResourceException { - return handle(Method.POST, entity); - } - - /** - * Puts an object entity. Automatically serializes the object using the - * {@link org.restlet.service.ConverterService}. - * - * @param entity The object entity to put. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP PUT - * method - */ - public Representation put(Object entity) throws ResourceException { - try { - return put(toRepresentation(entity)); - } catch (IOException e) { - throw new ResourceException(e); - } - } - - /** - * Puts an object entity. Automatically serializes the object using the - * {@link org.restlet.service.ConverterService}. - * - * @param The expected type for the response entity. - * @param entity The object entity to put. - * @param resultClass The class of the response entity. - * @return The response object entity. - * @throws ResourceException - * @see HTTP PUT - * method - */ - public T put(Object entity, Class resultClass) throws ResourceException { - return handle(Method.PUT, entity, resultClass); - } - - /** - * Puts an object entity. Automatically serializes the object using the - * {@link org.restlet.service.ConverterService}. - * - * @param entity The object entity to post. - * @param mediaType The media type of the representation to retrieve. - * @return The response object entity. - * @throws ResourceException - * @see HTTP PUT - * method - */ - public Representation put(Object entity, MediaType mediaType) throws ResourceException { - try { - return handle(Method.PUT, toRepresentation(entity), mediaType); - } catch (IOException e) { - throw new ResourceException(e); - } - } - - /** - * Creates or updates a resource with the given representation as new state to - * be stored. If a success status is not returned, then a resource exception is - * thrown. - * - * @param entity The request entity to store. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP PUT - * method - */ - public Representation put(Representation entity) throws ResourceException { - return handle(Method.PUT, entity); - } - - /** - * Effectively redirects a client call. By default, it checks for infinite loops - * and unavailable entities, the references list is updated and the - * {@link #handle(Request, Response, List, int, Uniform)} method invoked. - * - * @param request The request to send. - * @param response The response to update. - * @param references The references that caused a redirection to prevent - * infinite loops. - * @param retryAttempt The number of remaining attempts. - * @param next The next handler handling the call. - */ - protected void redirect(Request request, Response response, List references, int retryAttempt, - Uniform next) { - Reference newTargetRef = response.getLocationRef(); - - if ((references != null) && references.contains(newTargetRef)) { - getLogger().warning("Infinite redirection loop detected with URI: " + newTargetRef); - } else if (request.getEntity() != null && !request.isEntityAvailable()) { - getLogger().warning("Unable to follow the redirection because the request entity isn't available anymore."); - } else { - if (references == null) { - references = new ArrayList(); - } - - if (references.size() >= getMaxRedirects()) { - getLogger().warning( - "Unable to follow the redirection because the request the maximum number of redirections for a single call has been reached."); - } else { - // Add to the list of redirection reference - // to prevent infinite loops - references.add(request.getResourceRef()); - request.setResourceRef(newTargetRef); - handle(request, response, references, 0, next); - } - } - } - - /** - * Effectively retries a failed client call. By default, it sleeps before the - * retry attempt and increments the number of retries. - * - * @param request The request to send. - * @param response The response to update. - * @param references The references that caused a redirection to prevent - * infinite loops. - * @param retryAttempt The number of remaining attempts. - * @param next The next handler handling the call. - */ - protected void retry(Request request, Response response, List references, int retryAttempt, - Uniform next) { - getLogger().log(Level.INFO, "A recoverable error was detected (" + response.getStatus().getCode() - + "), attempting again in " + getRetryDelay() + " ms."); - - // Wait before attempting again - if (getRetryDelay() > 0) { - try { - Thread.sleep(getRetryDelay()); - } catch (InterruptedException e) { - getLogger().log(Level.FINE, "Retry delay sleep was interrupted", e); - // MITRE, CWE-391 - Unchecked Error Condition - Thread.currentThread().interrupt(); - } - } - - // Retry the call - handle(request, response, references, ++retryAttempt, next); - } - - /** - * Sets the request attribute value. - * - * @param name The attribute name. - * @param value The attribute to set. - */ - public void setAttribute(String name, Object value) { - getRequestAttributes().put(name, value); - } - - /** - * Sets the authentication response sent by a client to an origin server. - * - * @param challengeResponse The authentication response sent by a client to an - * origin server. - * @see Request#setChallengeResponse(ChallengeResponse) - */ - public void setChallengeResponse(ChallengeResponse challengeResponse) { - getRequest().setChallengeResponse(challengeResponse); - } - - /** - * Sets the authentication response sent by a client to an origin server given a - * scheme, identifier and secret. - * - * @param scheme The challenge scheme. - * @param identifier The user identifier, such as a login name or an access key. - * @param secret The user secret, such as a password or a secret key. - */ - public void setChallengeResponse(ChallengeScheme scheme, final String identifier, String secret) { - setChallengeResponse(new ChallengeResponse(scheme, identifier, secret)); - } - - /** - * Sets the client-specific information. - * - * @param clientInfo The client-specific information. - * @see Request#setClientInfo(ClientInfo) - */ - public void setClientInfo(ClientInfo clientInfo) { - getRequest().setClientInfo(clientInfo); - } - - /** - * Sets the conditions applying to this request. - * - * @param conditions The conditions applying to this request. - * @see Request#setConditions(Conditions) - */ - public void setConditions(Conditions conditions) { - getRequest().setConditions(conditions); - } - - /** - * Sets the cookies provided by the client. - * - * @param cookies The cookies provided by the client. - * @see Request#setCookies(Series) - */ - public void setCookies(Series cookies) { - getRequest().setCookies(cookies); - } - - /** - * Indicates if transient entities should be buffered after being received or - * before being sent. - * - * @param entityBuffering True if transient entities should be buffered. - * @see ClientResource#setRequestEntityBuffering(boolean) - * @see #setResponseEntityBuffering(boolean) - */ - public void setEntityBuffering(boolean entityBuffering) { - setRequestEntityBuffering(entityBuffering); - setResponseEntityBuffering(entityBuffering); - } - - /** - * Indicates if redirections are followed. - * - * @param followingRedirects True if redirections are followed. - */ - public void setFollowingRedirects(boolean followingRedirects) { - this.followingRedirects = followingRedirects; - } - - /** - * Sets the host reference. - * - * @param hostRef The host reference. - * @see Request#setHostRef(Reference) - */ - public void setHostRef(Reference hostRef) { - getRequest().setHostRef(hostRef); - } - - /** - * Sets the host reference using an URI string. - * - * @param hostUri The host URI. - * @see Request#setHostRef(String) - */ - public void setHostRef(String hostUri) { - getRequest().setHostRef(hostUri); - } - - /** - * Indicates if the call is loggable - * - * @param loggable True if the call is loggable - */ - public void setLoggable(boolean loggable) { - getRequest().setLoggable(loggable); - } - - /** - * Sets the maximum number of redirections that can be automatically followed - * for a single call. - * - * @param maxRedirects The maximum number of redirections that can be - * automatically followed for a single call. - */ - public void setMaxRedirects(int maxRedirects) { - this.maxRedirects = maxRedirects; - } - - /** - * Sets the method called. - * - * @param method The method called. - * @see Request#setMethod(Method) - */ - public void setMethod(Method method) { - getRequest().setMethod(method); - } - - /** - * Sets the next handler such as a Restlet or a Filter. - * - * In addition, this method will set the context of the next Restlet if it is - * null by passing a reference to its own context. - * - * @param next The next handler. - */ - public void setNext(org.restlet.Uniform next) { - if (next instanceof Restlet) { - Restlet nextRestlet = (Restlet) next; - - if (nextRestlet.getContext() == null) { - nextRestlet.setContext(getContext()); - } - } - - this.next = next; - - // If true, it must be updated after calling this method - this.nextCreated = false; - } - - /** - * Sets the callback invoked on response reception. If the value is not null, - * then the associated request will be executed asynchronously. - * - * @param onResponseCallback The callback invoked on response reception. - */ - public void setOnResponse(Uniform onResponseCallback) { - getRequest().setOnResponse(onResponseCallback); - } - - /** - * Sets the callback invoked after sending the request. - * - * @param onSentCallback The callback invoked after sending the request. - */ - public void setOnSent(Uniform onSentCallback) { - getRequest().setOnSent(onSentCallback); - } - - /** - * Sets the original reference requested by the client. - * - * @param originalRef The original reference. - * @see Request#setOriginalRef(Reference) - */ - public void setOriginalRef(Reference originalRef) { - getRequest().setOriginalRef(originalRef); - } - - /** - * Sets the protocol used or to be used. - * - * @param protocol The protocol used or to be used. - */ - public void setProtocol(Protocol protocol) { - getRequest().setProtocol(protocol); - } - - /** - * Sets the proxy authentication response sent by a client to an origin server. - * - * @param challengeResponse The proxy authentication response sent by a client - * to an origin server. - * @see Request#setProxyChallengeResponse(ChallengeResponse) - */ - public void setProxyChallengeResponse(ChallengeResponse challengeResponse) { - getRequest().setProxyChallengeResponse(challengeResponse); - } - - /** - * Sets the proxy authentication response sent by a client to an origin server - * given a scheme, identifier and secret. - * - * @param scheme The challenge scheme. - * @param identifier The user identifier, such as a login name or an access key. - * @param secret The user secret, such as a password or a secret key. - */ - public void setProxyChallengeResponse(ChallengeScheme scheme, final String identifier, String secret) { - setProxyChallengeResponse(new ChallengeResponse(scheme, identifier, secret)); - } - - /** - * Sets the ranges to return from the target resource's representation. - * - * @param ranges The ranges. - * @see Request#setRanges(List) - */ - public void setRanges(List ranges) { - getRequest().setRanges(ranges); - } - - /** - * Sets the resource's reference. If the reference is relative, it will be - * resolved as an absolute reference. Also, the context's base reference will be - * reset. Finally, the reference will be normalized to ensure a consistent - * handling of the call. - * - * @param reference The resource reference. - * @see Request#setResourceRef(Reference) - */ - public void setReference(Reference reference) { - getRequest().setResourceRef(reference); - } - - /** - * Sets the resource's reference using an URI string. Note that the URI can be - * either absolute or relative to the context's base reference. - * - * @param uri The resource URI. - * @see Request#setResourceRef(String) - */ - public void setReference(String uri) { - getRequest().setResourceRef(uri); - } - - /** - * Sets the referrer reference if available. - * - * @param referrerRef The referrer reference. - * @see Request#setReferrerRef(Reference) - */ - public void setReferrerRef(Reference referrerRef) { - getRequest().setReferrerRef(referrerRef); - } - - /** - * Sets the referrer reference if available using an URI string. - * - * @param referrerUri The referrer URI. - * @see Request#setReferrerRef(String) - */ - public void setReferrerRef(String referrerUri) { - getRequest().setReferrerRef(referrerUri); - } - - /** - * Indicates if transient or unknown size response entities should be buffered - * after being received. This is useful to increase the chance of being able to - * resubmit a failed request due to network error, or to prevent chunked - * encoding from being used an HTTP connector. - * - * @param requestEntityBuffering True if transient request entities should be - * buffered after being received. - */ - public void setRequestEntityBuffering(boolean requestEntityBuffering) { - this.requestEntityBuffering = requestEntityBuffering; - } - - /** - * Indicates if transient or unknown size response entities should be buffered - * after being received. This is useful to be able to systematically reuse and - * process a response entity several times after retrieval. - * - * @param responseEntityBuffering True if transient response entities should be - * buffered after being received. - */ - public void setResponseEntityBuffering(boolean responseEntityBuffering) { - this.responseEntityBuffering = responseEntityBuffering; - } - - /** - * Sets the number of retry attempts before reporting an error. - * - * @param retryAttempts The number of retry attempts before reporting an error. - */ - public void setRetryAttempts(int retryAttempts) { - this.retryAttempts = retryAttempts; - } - - /** - * Sets the delay in milliseconds between two retry attempts. The default value - * is two seconds. - * - * @param retryDelay The delay in milliseconds between two retry attempts. - */ - public void setRetryDelay(long retryDelay) { - this.retryDelay = retryDelay; - } - - /** - * Indicates if idempotent requests should be retried on error. - * - * @param retryOnError True if idempotent requests should be retried on error. - */ - public void setRetryOnError(boolean retryOnError) { - this.retryOnError = retryOnError; - } - - /** - * Wraps the client resource to proxy calls to the given Java interface into - * Restlet method calls. Use the {@link org.restlet.engine.Engine} classloader - * in order to generate the proxy. - * - * @param - * @param resourceInterface The annotated resource interface class to proxy. - * @return The proxy instance. - */ - public T wrap(Class resourceInterface) { - return wrap(resourceInterface, org.restlet.engine.Engine.getInstance().getClassLoader()); - } - - /** - * Wraps the client resource to proxy calls to the given Java interface into - * Restlet method calls. - * - * @param - * @param resourceInterface The annotated resource interface class to proxy. - * @param classLoader The classloader used to instantiate the dynamic - * proxy. - * @return The proxy instance. - */ - @SuppressWarnings("unchecked") - public T wrap(Class resourceInterface, ClassLoader classLoader) { - T result = null; - - // Create the client resource proxy - java.lang.reflect.InvocationHandler h = new org.restlet.engine.resource.ClientInvocationHandler(this, - resourceInterface); - - // Instantiate our dynamic proxy - result = (T) java.lang.reflect.Proxy.newProxyInstance(classLoader, - new Class[] { ClientProxy.class, resourceInterface }, h); - - return result; - } + /** + * Creates a client resource that proxy calls to the given Java interface into Restlet method + * calls. It basically creates a new instance of {@link ClientResource} and invokes the {@link + * #wrap(Class)} method. + * + * @param The proxied interface. + * @param context The context. + * @param reference The target reference. + * @param resourceInterface The annotated resource interface class to proxy. + * @return The proxy instance. + */ + public static T create( + Context context, Reference reference, Class resourceInterface) { + ClientResource clientResource = new ClientResource(context, reference); + return clientResource.wrap(resourceInterface); + } + + /** + * Creates a client resource that proxy calls to the given Java interface into Restlet method + * calls. It basically creates a new instance of {@link ClientResource} and invokes the {@link + * #wrap(Class)} method. + * + * @param The proxied interface. + * @param reference The target reference. + * @param resourceInterface The annotated resource interface class to proxy. + * @return The proxy instance. + */ + public static T create(Reference reference, Class resourceInterface) { + return create(null, reference, resourceInterface); + } + + /** + * Creates a client resource that proxy calls to the given Java interface into Restlet method + * calls. It basically creates a new instance of {@link ClientResource} and invokes the {@link + * #wrap(Class)} method. + * + * @param The proxied interface. + * @param uri The target URI. + * @param resourceInterface The annotated resource interface class to proxy. + * @return The proxy instance. + */ + public static T create(String uri, Class resourceInterface) { + return create(null, new Reference(uri), resourceInterface); + } + + /** Indicates if redirections should be automatically followed. */ + private volatile boolean followingRedirects; + + /** + * Indicates the maximum number of redirections that can be automatically followed for a single + * call. + */ + private volatile int maxRedirects; + + /** The next Restlet. */ + private volatile Uniform next; + + /** Indicates if the next Restlet has been created. */ + private volatile boolean nextCreated; + + /** + * Indicates if transient or unknown size request entities should be buffered before being sent. + */ + private volatile boolean requestEntityBuffering; + + /** + * Indicates if transient or unknown size response entities should be buffered after being + * received. + */ + private volatile boolean responseEntityBuffering; + + /** Number of retry attempts before reporting an error. */ + private volatile int retryAttempts; + + /** Delay in milliseconds between two retry attempts. */ + private volatile long retryDelay; + + /** Indicates if idempotent requests should be retried on error. */ + private volatile boolean retryOnError; + + /** Empty constructor. */ + protected ClientResource() {} + + /** + * Constructor. + * + * @param resource The client resource to copy. + */ + public ClientResource(ClientResource resource) { + Request request = new Request(resource.getRequest()); + Response response = new Response(request); + this.next = resource.getNext(); + this.maxRedirects = resource.getMaxRedirects(); + this.retryOnError = resource.isRetryOnError(); + this.retryDelay = resource.getRetryDelay(); + this.retryAttempts = resource.getRetryAttempts(); + + this.followingRedirects = resource.isFollowingRedirects(); + this.requestEntityBuffering = resource.isRequestEntityBuffering(); + this.responseEntityBuffering = resource.isResponseEntityBuffering(); + setApplication(resource.getApplication()); + + init(resource.getContext(), request, response); + } + + /** + * Constructor. + * + * @param context The context. + * @param uri The target URI. + */ + public ClientResource(Context context, java.net.URI uri) { + this(context, Method.GET, uri); + } + + /** + * Constructor. + * + * @param context The context. + * @param method The method to call. + * @param uri The target URI. + */ + public ClientResource(Context context, Method method, java.net.URI uri) { + this(context, method, new Reference(uri)); + } + + /** + * Constructor. + * + * @param context The context. + * @param method The method to call. + * @param reference The target reference. + */ + public ClientResource(Context context, Method method, Reference reference) { + this(context, new Request(method, reference), new Response(null)); + } + + /** + * Constructor. + * + * @param context The context. + * @param method The method to call. + * @param uri The target URI. + */ + public ClientResource(Context context, Method method, String uri) { + this(context, method, new Reference(uri)); + } + + /** + * Constructor. + * + * @param context The context. + * @param reference The target reference. + */ + public ClientResource(Context context, Reference reference) { + this(context, Method.GET, reference); + } + + /** + * Constructor. + * + * @param context The current context. + * @param request The handled request. + */ + public ClientResource(Context context, Request request) { + this(context, request, null); + } + + /** + * Constructor. + * + * @param context The current context. + * @param request The handled request. + * @param response The handled response. + */ + public ClientResource(Context context, Request request, Response response) { + if (context == null) { + context = Context.getCurrent(); + } + + // Don't remove this line. + // See the constructor ClientResource(Context, Method, Reference) + response.setRequest(request); + + this.maxRedirects = 10; + this.retryOnError = true; + this.retryDelay = 2000L; + this.retryAttempts = 2; + this.followingRedirects = true; + this.requestEntityBuffering = false; + this.responseEntityBuffering = false; + init(context, request, response); + } + + /** + * Constructor. + * + * @param context The context. + * @param uri The target URI. + */ + public ClientResource(Context context, String uri) { + this(context, Method.GET, uri); + } + + /** + * Constructor. + * + * @param uri The target URI. + */ + public ClientResource(java.net.URI uri) { + this(Context.getCurrent(), null, uri); + } + + /** + * Constructor. + * + * @param method The method to call. + * @param uri The target URI. + */ + public ClientResource(Method method, java.net.URI uri) { + this(Context.getCurrent(), method, uri); + } + + /** + * Constructor. + * + * @param method The method to call. + * @param reference The target reference. + */ + public ClientResource(Method method, Reference reference) { + this(Context.getCurrent(), method, reference); + } + + /** + * Constructor. + * + * @param method The method to call. + * @param uri The target URI. + */ + public ClientResource(Method method, String uri) { + this(Context.getCurrent(), method, uri); + } + + /** + * Constructor. + * + * @param reference The target reference. + */ + public ClientResource(Reference reference) { + this(Context.getCurrent(), null, reference); + } + + /** + * Constructor. + * + * @param request The handled request. + */ + public ClientResource(Request request) { + this(request, new Response(request)); + } + + /** + * Constructor. + * + * @param request The handled request. + * @param response The handled response. + */ + public ClientResource(Request request, Response response) { + this(Context.getCurrent(), request, response); + } + + /** + * Constructor. + * + * @param uri The target URI. + */ + public ClientResource(String uri) { + this(Context.getCurrent(), Method.GET, uri); + } + + /** + * Updates the client preferences to accept the given metadata (media types, character sets, + * etc.) with a 1.0 quality in addition to existing ones. + * + * @param metadata The metadata to accept. + * @see ClientInfo#accept(Metadata...) + */ + public void accept(Metadata... metadata) { + getClientInfo().accept(metadata); + } + + /** + * Updates the client preferences to accept the given metadata (media types, character sets, + * etc.) with a given quality in addition to existing ones. + * + * @param metadata The metadata to accept. + * @param quality The quality to set. + * @see ClientInfo#accept(Metadata, float) + */ + public void accept(Metadata metadata, float quality) { + getClientInfo().accept(metadata, quality); + } + + /** + * Adds a parameter to the query component. The name and value are automatically encoded if + * necessary. + * + * @param parameter The parameter to add. + * @return The updated reference. + * @see Reference#addQueryParameter(Parameter) + */ + public Reference addQueryParameter(Parameter parameter) { + return getReference().addQueryParameter(parameter); + } + + /** + * Adds a parameter to the query component. The name and value are automatically encoded if + * necessary. + * + * @param name The parameter name. + * @param value The optional parameter value. + * @return The updated reference. + * @see Reference#addQueryParameter(String, String) + */ + public Reference addQueryParameter(String name, String value) { + return getReference().addQueryParameter(name, value); + } + + /** + * Adds several parameters to the query component. The name and value are automatically encoded + * if necessary. + * + * @param parameters The parameters to add. + * @return The updated reference. + * @see Reference#addQueryParameters(Iterable) + */ + public Reference addQueryParameters(Iterable parameters) { + return getReference().addQueryParameters(parameters); + } + + /** + * Adds a segment at the end of the path. If the current path doesn't end with a slash + * character, one is inserted before the new segment value. The value is automatically encoded + * if necessary. + * + * @param value The segment value to add. + * @return The updated reference. + * @see Reference#addSegment(String) + */ + public Reference addSegment(String value) { + return getReference().addSegment(value); + } + + /** + * Creates a next Restlet is no one is set. By default, it creates a new {@link Client} based on + * the protocol of the resource's URI reference. + * + * @return The created next Restlet or null. + */ + protected Uniform createNext() { + Uniform result = null; + + // Prefer the outbound root + result = getApplication().getOutboundRoot(); + + if ((result == null) && (getContext() != null)) { + // Try using directly the client dispatcher + result = getContext().getClientDispatcher(); + } + + if (result == null) { + // As a final option, try creating a client connector + Protocol rProtocol = getProtocol(); + Reference rReference = getReference(); + Protocol protocol = + (rProtocol != null) + ? rProtocol + : (rReference != null) ? rReference.getSchemeProtocol() : null; + + if (protocol != null) { + org.restlet.engine.util.TemplateDispatcher dispatcher = + new org.restlet.engine.util.TemplateDispatcher(); + dispatcher.setContext(getContext()); + dispatcher.setNext(new Client(protocol)); + result = dispatcher; + } + } + + return result; + } + + /** + * Creates a new request by cloning the one wrapped by this class. + * + * @return The new response. + * @see #getRequest() + */ + public Request createRequest() { + return new Request(getRequest()); + } + + /** + * Creates a new response for the given request. + * + * @param request The associated request. + * @return The new response. + */ + protected Response createResponse(Request request) { + return new Response(request); + } + + /** + * Deletes the target resource and all its representations. If a success status is not returned, + * then a resource exception is thrown. + * + * @return The optional response entity. + * @see HTTP DELETE + * method + */ + public Representation delete() throws ResourceException { + return handle(Method.DELETE); + } + + /** + * Deletes the target resource and all its representations. If a success status is not returned, + * then a resource exception is thrown. + * + * @param The expected type for the response entity. + * @param resultClass The expected class for the response entity object. + * @return The response entity object. + * @see HTTP DELETE + * method + */ + public T delete(Class resultClass) throws ResourceException { + return handle(Method.DELETE, resultClass); + } + + /** + * Deletes the target resource and all its representations. If a success status is not returned, + * then a resource exception is thrown. + * + * @param mediaType The media type of the representation to retrieve. + * @return The representation matching the given media type. + * @throws ResourceException + * @see HTTP DELETE + * method + */ + public Representation delete(MediaType mediaType) throws ResourceException { + return handle(Method.DELETE, mediaType); + } + + /** + * By default, it throws a new resource exception. Call {@link #doError(org.restlet.data.Status, + * org.restlet.Request, org.restlet.Response)}. + * + * @param request The associated request. + * @param response The associated response. + */ + public void doError(Request request, Response response) { + doError(response.getStatus(), request, response); + } + + /** + * By default, it throws a new resource exception. Call {@link #doError(org.restlet.data.Status, + * org.restlet.Request, org.restlet.Response)}. + * + * @param errorStatus The error status received. + */ + @Override + public void doError(Status errorStatus) { + doError(errorStatus, getRequest(), getResponse()); + } + + /** + * By default, it throws a new resource exception. This can be overridden to provide a different + * behavior. + * + * @param errorStatus The error status received. + * @param request The associated request. + * @param response The associated response. + */ + public void doError(Status errorStatus, Request request, Response response) { + throw new ResourceException(errorStatus, request, response); + } + + /** + * Releases the resource by stopping any connector automatically created and associated with the + * "next" property (see {@link #getNext()} method. + */ + @Override + protected void doRelease() throws ResourceException { + if ((getNext() != null) && this.nextCreated) { + if (getNext() instanceof Restlet) { + try { + ((Restlet) getNext()).stop(); + } catch (Exception e) { + throw new ResourceException(e); + } + } + + setNext(null); + } + } + + /** Attempts to {@link #release()} the resource. */ + @Override + protected void finalize() throws Throwable { + release(); + super.finalize(); + } + + /** + * Represents the resource using content negotiation to select the best variant based on the + * client preferences. Note that the client preferences will be automatically adjusted, but only + * for this request. If you want to change them once for all, you can use the {@link + * #getClientInfo()} method.
+ *
+ * If a success status is not returned, then a resource exception is thrown. + * + * @return The best representation. + * @throws ResourceException + * @see HTTP GET + * method + */ + public Representation get() throws ResourceException { + return handle(Method.GET); + } + + /** + * Represents the resource in the given object class. Note that the client preferences will be + * automatically adjusted, but only for this request. If you want to change them once for all, + * you can use the {@link #getClientInfo()} method.
+ *
+ * If a success status is not returned, then a resource exception is thrown. + * + * @param The expected type for the response entity. + * @param resultClass The expected class for the response entity object. + * @return The response entity object. + * @throws ResourceException + * @see HTTP GET + * method + */ + public T get(Class resultClass) throws ResourceException { + return handle(Method.GET, resultClass); + } + + /** + * Represents the resource using a given media type. Note that the client preferences will be + * automatically adjusted, but only for this request. If you want to change them once for all, + * you can use the {@link #getClientInfo()} method.
+ *
+ * If a success status is not returned, then a resource exception is thrown. + * + * @param mediaType The media type of the representation to retrieve. + * @return The representation matching the given media type. + * @throws ResourceException + * @see HTTP GET + * method + */ + public Representation get(MediaType mediaType) throws ResourceException { + return handle(Method.GET, mediaType); + } + + /** + * Returns the attribute value by looking up the given name in the response attributes maps. The + * toString() method is then invoked on the attribute value. + * + * @param name The attribute name. + * @return The response attribute value. + */ + public String getAttribute(String name) { + Object value = getResponseAttributes().get(name); + return (value == null) ? null : value.toString(); + } + + /** + * Returns the child resource defined by its URI relatively to the current resource. The child + * resource is defined in the sense of hierarchical URIs. If the resource URI is not + * hierarchical, then an exception is thrown. + * + * @param relativeRef The URI reference of the child resource relatively to the current resource + * seen as the parent resource. + * @return The child resource. + * @throws ResourceException + */ + public ClientResource getChild(Reference relativeRef) throws ResourceException { + ClientResource result = null; + + if ((relativeRef != null) && relativeRef.isRelative()) { + result = new ClientResource(this); + result.setReference( + new Reference(getReference().getTargetRef(), relativeRef).getTargetRef()); + } else { + doError(Status.CLIENT_ERROR_BAD_REQUEST, "The child URI is not relative."); + } + + return result; + } + + /** + * Wraps the child client resource to proxy call to the given Java interface into Restlet method + * calls. The child resource is defined in the sense of hierarchical URIs. If the resource URI + * is not hierarchical, then an exception is thrown. + * + * @param The proxied interface. + * @param relativeRef The URI reference of the child resource relatively to the current resource + * seen as the parent resource. + * @param resourceInterface The annotated resource interface class to proxy. + * @return The proxy instance. + */ + public T getChild(Reference relativeRef, Class resourceInterface) + throws ResourceException { + T result = null; + ClientResource childResource = getChild(relativeRef); + + if (childResource != null) { + result = childResource.wrap(resourceInterface); + } + + return result; + } + + /** + * Returns the child resource defined by its URI relatively to the current resource. The child + * resource is defined in the sense of hierarchical URIs. If the resource URI is not + * hierarchical, then an exception is thrown. + * + * @param relativeUri The URI of the child resource relatively to the current resource seen as + * the parent resource. + * @return The child resource. + * @throws ResourceException + */ + public ClientResource getChild(String relativeUri) throws ResourceException { + return getChild(new Reference(relativeUri)); + } + + /** + * Wraps the child client resource to proxy call to the given Java interface into Restlet method + * calls. The child resource is defined in the sense of hierarchical URIs. If the resource URI + * is not hierarchical, then an exception is thrown. + * + * @param The proxied interface. + * @param relativeUri The URI of the child resource relatively to the current resource seen as + * the parent resource. + * @param resourceInterface The annotated resource interface class to proxy. + * @return The proxy instance. + */ + public T getChild(String relativeUri, Class resourceInterface) + throws ResourceException { + return getChild(new Reference(relativeUri), resourceInterface); + } + + /** + * Returns the maximum number of redirections that can be automatically followed for a single + * call. The default value is 10. + * + * @return The maximum number of redirections that can be automatically followed for a single + * call. + */ + public int getMaxRedirects() { + return maxRedirects; + } + + /** + * Returns the next Restlet. By default, it is the client dispatcher if a context is available. + * + * @return The next Restlet or null. + */ + public Uniform getNext() { + Uniform result = this.next; + + if (result == null) { + synchronized (this) { + if (result == null) { + result = createNext(); + + if (result != null) { + setNext(result); + this.nextCreated = true; + } + } + } + } + + return result; + } + + /** + * Returns the callback invoked on response reception. If the value is not null, then the + * associated request will be executed asynchronously. + * + * @return The callback invoked on response reception. + */ + public Uniform getOnResponse() { + return getRequest().getOnResponse(); + } + + /** + * Returns the callback invoked after sending the request. + * + * @return The callback invoked after sending the request. + */ + public Uniform getOnSent() { + return getRequest().getOnSent(); + } + + /** + * Returns the parent resource. The parent resource is defined in the sense of hierarchical + * URIs. If the resource URI is not hierarchical, then an exception is thrown. + * + * @return The parent resource. + */ + public ClientResource getParent() throws ResourceException { + ClientResource result = null; + + if (getReference().isHierarchical()) { + result = new ClientResource(this); + result.setReference(getReference().getParentRef()); + } else { + doError(Status.CLIENT_ERROR_BAD_REQUEST, "The resource URI is not hierarchical."); + } + + return result; + } + + /** + * Wraps the parent client resource to proxy call to the given Java interface into Restlet + * method calls. The parent resource is defined in the sense of hierarchical URIs. If the + * resource URI is not hierarchical, then an exception is thrown. + * + * @param The proxied interface. + * @param resourceInterface The annotated resource interface class to proxy. + * @return The proxy instance. + */ + public T getParent(Class resourceInterface) throws ResourceException { + T result = null; + + ClientResource parentResource = getParent(); + if (parentResource != null) { + result = parentResource.wrap(resourceInterface); + } + + return result; + } + + /** + * Returns the number of retry attempts before reporting an error. The default value is 2. + * + * @return The number of retry attempts before reporting an error. + */ + public int getRetryAttempts() { + return retryAttempts; + } + + /** + * Returns the delay in milliseconds between two retry attempts. The default value is 2 seconds. + * + * @return The delay in milliseconds between two retry attempts. + */ + public long getRetryDelay() { + return retryDelay; + } + + /** + * Handles the call by invoking the next handler. The prototype request is retrieved via {@link + * #getRequest()} and cloned and the response is set as the latest with {@link + * #setResponse(Response)}. If necessary the {@link #setNext(Uniform)} method is called as well + * with a {@link Client} instance matching the request protocol. + * + * @return The optional response entity. + * @see #getNext() + */ + @Override + public Representation handle() { + Response response = handleOutbound(createRequest()); + return (response == null) ? null : response.getEntity(); + } + + /** + * Handles the call by cloning the prototype request, setting the method and entity. + * + * @param method The request method to use. + * @return The optional response entity. + */ + protected Representation handle(Method method) { + return handle(method, (Representation) null); + } + + /** + * Handles the call by cloning the prototype request, setting the method and entity. + * + * @param The expected type for the response entity. + * @param method The request method to use. + * @param resultClass The expected class for the response entity object. + * @return The response entity object. + * @throws ResourceException + */ + protected T handle(Method method, Class resultClass) throws ResourceException { + return handle(method, null, resultClass); + } + + /** + * Handles the call by cloning the prototype request, setting the method and entity. + * + * @param method The request method to use. + * @param mediaType The preferred result media type. + * @return The optional response entity. + */ + protected Representation handle(Method method, MediaType mediaType) { + return handle(method, null, mediaType); + } + + /** + * Handles an object entity. Automatically serializes the object using the {@link + * org.restlet.service.ConverterService}. + * + * @param The expected type for the response entity. + * @param method The request method to use. + * @param entity The object entity to post. + * @param resultClass The class of the response entity. + * @return The response object entity. + * @throws ResourceException + */ + protected T handle(Method method, Object entity, Class resultClass) + throws ResourceException { + org.restlet.service.ConverterService cs = getConverterService(); + ClientInfo clientInfo = getClientInfo(); + + if (clientInfo.getAcceptedMediaTypes().isEmpty()) { + cs.updatePreferences(clientInfo.getAcceptedMediaTypes(), resultClass); + } + + // Prepare the request by cloning the prototype request + Request request = createRequest(); + request.setMethod(method); + request.setClientInfo(clientInfo); + + if (entity != null) { + List entityVariants; + try { + entityVariants = cs.getVariants(entity.getClass(), null); + request.setEntity( + toRepresentation( + entity, + getConnegService() + .getPreferredVariant( + entityVariants, request, getMetadataService()))); + } catch (IOException e) { + throw new ResourceException(e); + } + } else { + request.setEntity(null); + } + + // Actually handle the call + Response response = handleOutbound(request); + Representation responseEntity = handleInbound(response); + return toObject(responseEntity, resultClass); + } + + /** + * Handles the call by cloning the prototype request, setting the method and entity. + * + * @param method The request method to use. + * @param entity The request entity to set. + * @return The optional response entity. + */ + protected Representation handle(Method method, Representation entity) { + return handle(method, entity, getClientInfo()); + } + + /** + * Handles the call by cloning the prototype request, setting the method and entity. + * + * @param method The request method to use. + * @param entity The request entity to set. + * @param clientInfo The client preferences. + * @return The optional response entity. + */ + protected Representation handle(Method method, Representation entity, ClientInfo clientInfo) { + // Prepare the request by cloning the prototype request + Request request = createRequest(); + request.setMethod(method); + request.setEntity(entity); + request.setClientInfo(clientInfo); + + // Actually handle the call + Response response = handleOutbound(request); + return handleInbound(response); + } + + /** + * Handles the call by cloning the prototype request, setting the method and entity. + * + * @param method The request method to use. + * @param entity The request entity to set. + * @param mediaType The preferred result media type. + * @return The optional response entity. + */ + protected Representation handle(Method method, Representation entity, MediaType mediaType) { + return handle(method, entity, new ClientInfo(mediaType)); + } + + /** + * Handle the call and follow redirection for safe methods. + * + * @param request The request to send. + * @param response The response to update. + * @param references The references that caused a redirection to prevent infinite loops. + * @param retryAttempt The number of remaining attempts. + * @param next The next handler handling the call. + */ + protected void handle( + Request request, + Response response, + List references, + int retryAttempt, + Uniform next) { + if (next != null) { + // Check if request entity buffering must be done + if (isRequestEntityBuffering()) { + request.bufferEntity(); + } + + // Actually handle the call + next.handle(request, response); + + if (isRetryOnError() + && response.getStatus().isRecoverableError() + && request.getMethod().isIdempotent() + && (retryAttempt < getRetryAttempts()) + && ((request.getEntity() == null) || request.getEntity().isAvailable())) { + retry(request, response, references, retryAttempt, next); + } else if (isFollowingRedirects() + && response.getStatus().isRedirection() + && (response.getLocationRef() != null)) { + boolean doRedirection = false; + + if (request.getMethod().isSafe()) { + doRedirection = true; + } else { + if (Status.REDIRECTION_SEE_OTHER.equals(response.getStatus())) { + // The user agent is redirected using the GET method + request.setMethod(Method.GET); + request.setEntity(null); + doRedirection = true; + } else if (Status.REDIRECTION_USE_PROXY.equals(response.getStatus())) { + doRedirection = true; + } + } + + if (doRedirection) { + redirect(request, response, references, retryAttempt, next); + } else { + getLogger() + .fine("Unable to redirect the client call after a response" + response); + } + } + + // Check if response entity buffering must be done + if (isResponseEntityBuffering()) { + response.bufferEntity(); + } + } else { + getLogger().log(Level.WARNING, "Request ignored as no next Restlet is available"); + } + } + + /** + * Handles the inbound call. Note that only synchronous calls are processed. + * + * @param response + * @return The response's entity, if any. + */ + public Representation handleInbound(Response response) { + if (response == null) { + return null; + } + + // Verify that the request was synchronous + if (response.getRequest().isSynchronous()) { + if (response.getStatus().isError()) { + doError(response.getStatus()); + return null; + } + return response.getEntity(); + } + + return null; + } + + /** + * Handles the outbound call by invoking the next handler. + * + * @param request The request to handle. + * @return The response created. + * @see #getNext() + */ + public Response handleOutbound(Request request) { + Response response = createResponse(request); + Uniform next = getNext(); + + if (next != null) { + // Effectively handle the call + handle(request, response, null, 0, next); + + // Update the last received response. + setResponse(response); + } else { + getLogger() + .warning( + "Unable to process the call for a client resource. No next Restlet has been provided."); + } + + return response; + } + + /** + * Indicates if there is a next Restlet. + * + * @return True if there is a next Restlet. + */ + public boolean hasNext() { + return getNext() != null; + } + + /** + * Represents the resource using content negotiation to select the best variant based on the + * client preferences. This method is identical to {@link #get()} but doesn't return the actual + * content of the representation, only its metadata.
+ *
+ * Note that the client preferences will be automatically adjusted, but only for this request. + * If you want to change them once for all, you can use the {@link #getClientInfo()} method.
+ *
+ * If a success status is not returned, then a resource exception is thrown. + * + * @return The best representation. + * @throws ResourceException + * @see HTTP HEAD + * method + */ + public Representation head() throws ResourceException { + return handle(Method.HEAD); + } + + /** + * Represents the resource using a given media type. This method is identical to {@link + * #get(MediaType)} but doesn't return the actual content of the representation, only its + * metadata.
+ *
+ * Note that the client preferences will be automatically adjusted, but only for this request. + * If you want to change them once for all, you can use the {@link #getClientInfo()} method.
+ *
+ * If a success status is not returned, then a resource exception is thrown. + * + * @param mediaType The media type of the representation to retrieve. + * @return The representation matching the given media type. + * @throws ResourceException + * @see HTTP HEAD + * method + */ + public Representation head(MediaType mediaType) throws ResourceException { + return handle(Method.HEAD, mediaType); + } + + /** + * Indicates if redirections are followed. + * + * @return True if redirections are followed. + */ + public boolean isFollowingRedirects() { + return followingRedirects; + } + + /** + * Indicates if transient or unknown size response entities should be buffered after being + * received. This is useful to increase the chance of being able to resubmit a failed request + * due to network error, or to prevent chunked encoding from being used an HTTP connector. + * + * @return True if transient response entities should be buffered after being received. + */ + public boolean isRequestEntityBuffering() { + return requestEntityBuffering; + } + + /** + * Indicates if transient or unknown size response entities should be buffered after being + * received. This is useful to be able to systematically reuse and process a response entity + * several times after retrieval. + * + * @return True if transient response entities should be buffered after being received. + */ + public boolean isResponseEntityBuffering() { + return responseEntityBuffering; + } + + /** + * Indicates if idempotent requests should be retried on error. Default value is true. + * + * @return True if idempotent requests should be retried on error. + */ + public boolean isRetryOnError() { + return retryOnError; + } + + /** + * Describes the resource using content negotiation to select the best variant based on the + * client preferences. If a success status is not returned, then a resource exception is thrown. + * + * @return The best description. + * @throws ResourceException + * @see HTTP OPTIONS + * method + */ + public Representation options() throws ResourceException { + return handle(Method.OPTIONS); + } + + /** + * Describes the resource using a given media type. If a success status is not returned, then a + * resource exception is thrown. + * + * @param The expected type for the response entity. + * @param resultClass The expected class for the response entity object. + * @return The response entity object. + * @throws ResourceException + * @see HTTP OPTIONS + * method + */ + public T options(Class resultClass) throws ResourceException { + return handle(Method.OPTIONS, resultClass); + } + + /** + * Describes the resource using a given media type. If a success status is not returned, then a + * resource exception is thrown. + * + * @param mediaType The media type of the representation to retrieve. + * @return The matched description or null. + * @throws ResourceException + * @see HTTP OPTIONS + * method + */ + public Representation options(MediaType mediaType) throws ResourceException { + return handle(Method.OPTIONS, mediaType); + } + + /** + * Patches a resource with the given object as delta state. Automatically serializes the object + * using the {@link org.restlet.service.ConverterService}. + * + * @param entity The object entity containing the patch. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP PATCH method + */ + public Representation patch(Object entity) throws ResourceException { + try { + return patch(toRepresentation(entity)); + } catch (IOException e) { + throw new ResourceException(e); + } + } + + /** + * Patches a resource with the given object as delta state. Automatically serializes the object + * using the {@link org.restlet.service.ConverterService}. + * + * @param The expected type for the response entity. + * @param entity The object entity containing the patch. + * @param resultClass The class of the response entity. + * @return The response object entity. + * @throws ResourceException + * @see HTTP PATCH method + */ + public T patch(Object entity, Class resultClass) throws ResourceException { + return handle(Method.PATCH, entity, resultClass); + } + + /** + * Patches a resource with the given object as delta state. Automatically serializes the object + * using the {@link org.restlet.service.ConverterService}. + * + * @param entity The object entity containing the patch. + * @param mediaType The media type of the representation to retrieve. + * @return The response object entity. + * @throws ResourceException + * @see HTTP PATCH method + */ + public Representation patch(Object entity, MediaType mediaType) throws ResourceException { + try { + return handle(Method.PATCH, toRepresentation(entity), mediaType); + } catch (IOException e) { + throw new ResourceException(e); + } + } + + /** + * Patches a resource with the given representation as delta state. If a success status is not + * returned, then a resource exception is thrown. + * + * @param entity The request entity containing the patch. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP PATCH method + */ + public Representation patch(Representation entity) throws ResourceException { + return handle(Method.PATCH, entity); + } + + /** + * Posts an object entity. Automatically serializes the object using the {@link + * org.restlet.service.ConverterService}. + * + * @param entity The object entity to post. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP POST + * method + */ + public Representation post(Object entity) throws ResourceException { + try { + return post(toRepresentation(entity)); + } catch (IOException e) { + throw new ResourceException(e); + } + } + + /** + * Posts an object entity. Automatically serializes the object using the {@link + * org.restlet.service.ConverterService}. + * + * @param The expected type for the response entity. + * @param entity The object entity to post. + * @param resultClass The class of the response entity. + * @return The response object entity. + * @throws ResourceException + * @see HTTP POST + * method + */ + public T post(Object entity, Class resultClass) throws ResourceException { + return handle(Method.POST, entity, resultClass); + } + + /** + * Posts an object entity. Automatically serializes the object using the {@link + * org.restlet.service.ConverterService}. + * + * @param entity The object entity to post. + * @param mediaType The media type of the representation to retrieve. + * @return The response object entity. + * @throws ResourceException + * @see HTTP POST + * method + */ + public Representation post(Object entity, MediaType mediaType) throws ResourceException { + try { + return handle(Method.POST, toRepresentation(entity), mediaType); + } catch (IOException e) { + throw new ResourceException(e); + } + } + + /** + * Posts a representation. If a success status is not returned, then a resource exception is + * thrown. + * + * @param entity The posted entity. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP POST + * method + */ + public Representation post(Representation entity) throws ResourceException { + return handle(Method.POST, entity); + } + + /** + * Puts an object entity. Automatically serializes the object using the {@link + * org.restlet.service.ConverterService}. + * + * @param entity The object entity to put. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP PUT + * method + */ + public Representation put(Object entity) throws ResourceException { + try { + return put(toRepresentation(entity)); + } catch (IOException e) { + throw new ResourceException(e); + } + } + + /** + * Puts an object entity. Automatically serializes the object using the {@link + * org.restlet.service.ConverterService}. + * + * @param The expected type for the response entity. + * @param entity The object entity to put. + * @param resultClass The class of the response entity. + * @return The response object entity. + * @throws ResourceException + * @see HTTP PUT + * method + */ + public T put(Object entity, Class resultClass) throws ResourceException { + return handle(Method.PUT, entity, resultClass); + } + + /** + * Puts an object entity. Automatically serializes the object using the {@link + * org.restlet.service.ConverterService}. + * + * @param entity The object entity to post. + * @param mediaType The media type of the representation to retrieve. + * @return The response object entity. + * @throws ResourceException + * @see HTTP PUT + * method + */ + public Representation put(Object entity, MediaType mediaType) throws ResourceException { + try { + return handle(Method.PUT, toRepresentation(entity), mediaType); + } catch (IOException e) { + throw new ResourceException(e); + } + } + + /** + * Creates or updates a resource with the given representation as new state to be stored. If a + * success status is not returned, then a resource exception is thrown. + * + * @param entity The request entity to store. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP PUT + * method + */ + public Representation put(Representation entity) throws ResourceException { + return handle(Method.PUT, entity); + } + + /** + * Effectively redirects a client call. By default, it checks for infinite loops and unavailable + * entities, the references list is updated and the {@link #handle(Request, Response, List, int, + * Uniform)} method invoked. + * + * @param request The request to send. + * @param response The response to update. + * @param references The references that caused a redirection to prevent infinite loops. + * @param retryAttempt The number of remaining attempts. + * @param next The next handler handling the call. + */ + protected void redirect( + Request request, + Response response, + List references, + int retryAttempt, + Uniform next) { + Reference newTargetRef = response.getLocationRef(); + + if ((references != null) && references.contains(newTargetRef)) { + getLogger().warning("Infinite redirection loop detected with URI: " + newTargetRef); + } else if (request.getEntity() != null && !request.isEntityAvailable()) { + getLogger() + .warning( + "Unable to follow the redirection because the request entity isn't available anymore."); + } else { + if (references == null) { + references = new ArrayList(); + } + + if (references.size() >= getMaxRedirects()) { + getLogger() + .warning( + "Unable to follow the redirection because the request the maximum number of redirections for a single call has been reached."); + } else { + // Add to the list of redirection reference + // to prevent infinite loops + references.add(request.getResourceRef()); + request.setResourceRef(newTargetRef); + handle(request, response, references, 0, next); + } + } + } + + /** + * Effectively retries a failed client call. By default, it sleeps before the retry attempt and + * increments the number of retries. + * + * @param request The request to send. + * @param response The response to update. + * @param references The references that caused a redirection to prevent infinite loops. + * @param retryAttempt The number of remaining attempts. + * @param next The next handler handling the call. + */ + protected void retry( + Request request, + Response response, + List references, + int retryAttempt, + Uniform next) { + getLogger() + .log( + Level.INFO, + "A recoverable error was detected (" + + response.getStatus().getCode() + + "), attempting again in " + + getRetryDelay() + + " ms."); + + // Wait before attempting again + if (getRetryDelay() > 0) { + try { + Thread.sleep(getRetryDelay()); + } catch (InterruptedException e) { + getLogger().log(Level.FINE, "Retry delay sleep was interrupted", e); + // MITRE, CWE-391 - Unchecked Error Condition + Thread.currentThread().interrupt(); + } + } + + // Retry the call + handle(request, response, references, ++retryAttempt, next); + } + + /** + * Sets the request attribute value. + * + * @param name The attribute name. + * @param value The attribute to set. + */ + public void setAttribute(String name, Object value) { + getRequestAttributes().put(name, value); + } + + /** + * Sets the authentication response sent by a client to an origin server. + * + * @param challengeResponse The authentication response sent by a client to an origin server. + * @see Request#setChallengeResponse(ChallengeResponse) + */ + public void setChallengeResponse(ChallengeResponse challengeResponse) { + getRequest().setChallengeResponse(challengeResponse); + } + + /** + * Sets the authentication response sent by a client to an origin server given a scheme, + * identifier, and secret. + * + * @param scheme The challenge scheme. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret, such as a password or a secret key. + */ + public void setChallengeResponse( + ChallengeScheme scheme, final String identifier, String secret) { + setChallengeResponse(new ChallengeResponse(scheme, identifier, secret)); + } + + /** + * Sets the client-specific information. + * + * @param clientInfo The client-specific information. + * @see Request#setClientInfo(ClientInfo) + */ + public void setClientInfo(ClientInfo clientInfo) { + getRequest().setClientInfo(clientInfo); + } + + /** + * Sets the conditions applying to this request. + * + * @param conditions The conditions applying to this request. + * @see Request#setConditions(Conditions) + */ + public void setConditions(Conditions conditions) { + getRequest().setConditions(conditions); + } + + /** + * Sets the cookies provided by the client. + * + * @param cookies The cookies provided by the client. + * @see Request#setCookies(Series) + */ + public void setCookies(Series cookies) { + getRequest().setCookies(cookies); + } + + /** + * Indicates if transient entities should be buffered after being received or before being sent. + * + * @param entityBuffering True if transient entities should be buffered. + * @see ClientResource#setRequestEntityBuffering(boolean) + * @see #setResponseEntityBuffering(boolean) + */ + public void setEntityBuffering(boolean entityBuffering) { + setRequestEntityBuffering(entityBuffering); + setResponseEntityBuffering(entityBuffering); + } + + /** + * Indicates if redirections are followed. + * + * @param followingRedirects True if redirections are followed. + */ + public void setFollowingRedirects(boolean followingRedirects) { + this.followingRedirects = followingRedirects; + } + + /** + * Sets the host reference. + * + * @param hostRef The host reference. + * @see Request#setHostRef(Reference) + */ + public void setHostRef(Reference hostRef) { + getRequest().setHostRef(hostRef); + } + + /** + * Sets the host reference using a URI string. + * + * @param hostUri The host URI. + * @see Request#setHostRef(String) + */ + public void setHostRef(String hostUri) { + getRequest().setHostRef(hostUri); + } + + /** + * Indicates if the call is loggable + * + * @param loggable True if the call is loggable + */ + public void setLoggable(boolean loggable) { + getRequest().setLoggable(loggable); + } + + /** + * Sets the maximum number of redirections that can be automatically followed for a single call. + * + * @param maxRedirects The maximum number of redirections that can be automatically followed for + * a single call. + */ + public void setMaxRedirects(int maxRedirects) { + this.maxRedirects = maxRedirects; + } + + /** + * Sets the method called. + * + * @param method The method called. + * @see Request#setMethod(Method) + */ + public void setMethod(Method method) { + getRequest().setMethod(method); + } + + /** + * Sets the next handler such as a Restlet or a Filter. + * + *

In addition, this method will set the context of the next Restlet if it is null by passing + * a reference to its own context. + * + * @param next The next handler. + */ + public void setNext(org.restlet.Uniform next) { + if (next instanceof final Restlet nextRestlet) { + + if (nextRestlet.getContext() == null) { + nextRestlet.setContext(getContext()); + } + } + + this.next = next; + + // If true, it must be updated after calling this method + this.nextCreated = false; + } + + /** + * Sets the callback invoked on response reception. If the value is not null, then the + * associated request will be executed asynchronously. + * + * @param onResponseCallback The callback invoked on response reception. + */ + public void setOnResponse(Uniform onResponseCallback) { + getRequest().setOnResponse(onResponseCallback); + } + + /** + * Sets the callback invoked after sending the request. + * + * @param onSentCallback The callback invoked after sending the request. + */ + public void setOnSent(Uniform onSentCallback) { + getRequest().setOnSent(onSentCallback); + } + + /** + * Sets the original reference requested by the client. + * + * @param originalRef The original reference. + * @see Request#setOriginalRef(Reference) + */ + public void setOriginalRef(Reference originalRef) { + getRequest().setOriginalRef(originalRef); + } + + /** + * Sets the protocol used or to be used. + * + * @param protocol The protocol used or to be used. + */ + public void setProtocol(Protocol protocol) { + getRequest().setProtocol(protocol); + } + + /** + * Sets the proxy authentication response sent by a client to an origin server. + * + * @param challengeResponse The proxy authentication response sent by a client to an origin + * server. + * @see Request#setProxyChallengeResponse(ChallengeResponse) + */ + public void setProxyChallengeResponse(ChallengeResponse challengeResponse) { + getRequest().setProxyChallengeResponse(challengeResponse); + } + + /** + * Sets the proxy authentication response sent by a client to an origin server given a scheme, + * identifier, and secret. + * + * @param scheme The challenge scheme. + * @param identifier The user identifier, such as a login name or an access key. + * @param secret The user secret, such as a password or a secret key. + */ + public void setProxyChallengeResponse( + ChallengeScheme scheme, final String identifier, String secret) { + setProxyChallengeResponse(new ChallengeResponse(scheme, identifier, secret)); + } + + /** + * Sets the ranges to return from the target resource's representation. + * + * @param ranges The ranges. + * @see Request#setRanges(List) + */ + public void setRanges(List ranges) { + getRequest().setRanges(ranges); + } + + /** + * Sets the resource's reference. If the reference is relative, it will be resolved as an + * absolute reference. Also, the context's base reference will be reset. Finally, the reference + * will be normalized to ensure a consistent handling of the call. + * + * @param reference The resource reference. + * @see Request#setResourceRef(Reference) + */ + public void setReference(Reference reference) { + getRequest().setResourceRef(reference); + } + + /** + * Sets the resource's reference using a URI string. Note that the URI can be either absolute or + * relative to the context's base reference. + * + * @param uri The resource URI. + * @see Request#setResourceRef(String) + */ + public void setReference(String uri) { + getRequest().setResourceRef(uri); + } + + /** + * Sets the referrer reference if available. + * + * @param referrerRef The referrer reference. + * @see Request#setReferrerRef(Reference) + */ + public void setReferrerRef(Reference referrerRef) { + getRequest().setReferrerRef(referrerRef); + } + + /** + * Sets the referrer reference if available using a URI string. + * + * @param referrerUri The referrer URI. + * @see Request#setReferrerRef(String) + */ + public void setReferrerRef(String referrerUri) { + getRequest().setReferrerRef(referrerUri); + } + + /** + * Indicates if transient or unknown size response entities should be buffered after being + * received. This is useful to increase the chance of being able to resubmit a failed request + * due to network error, or to prevent chunked encoding from being used an HTTP connector. + * + * @param requestEntityBuffering True if transient request entities should be buffered after + * being received. + */ + public void setRequestEntityBuffering(boolean requestEntityBuffering) { + this.requestEntityBuffering = requestEntityBuffering; + } + + /** + * Indicates if transient or unknown size response entities should be buffered after being + * received. This is useful to be able to systematically reuse and process a response entity + * several times after retrieval. + * + * @param responseEntityBuffering True if transient response entities should be buffered after + * being received. + */ + public void setResponseEntityBuffering(boolean responseEntityBuffering) { + this.responseEntityBuffering = responseEntityBuffering; + } + + /** + * Sets the number of retry attempts before reporting an error. + * + * @param retryAttempts The number of retry attempts before reporting an error. + */ + public void setRetryAttempts(int retryAttempts) { + this.retryAttempts = retryAttempts; + } + + /** + * Sets the delay in milliseconds between two retry attempts. The default value is two seconds. + * + * @param retryDelay The delay in milliseconds between two retry attempts. + */ + public void setRetryDelay(long retryDelay) { + this.retryDelay = retryDelay; + } + + /** + * Indicates if idempotent requests should be retried on error. + * + * @param retryOnError True if idempotent requests should be retried on error. + */ + public void setRetryOnError(boolean retryOnError) { + this.retryOnError = retryOnError; + } + + /** + * Wraps the client resource to proxy calls to the given Java interface into Restlet method + * calls. Use the {@link org.restlet.engine.Engine} classloader to generate the proxy. + * + * @param + * @param resourceInterface The annotated resource interface class to proxy. + * @return The proxy instance. + */ + public T wrap(Class resourceInterface) { + return wrap(resourceInterface, org.restlet.engine.Engine.getInstance().getClassLoader()); + } + + /** + * Wraps the client resource to proxy calls to the given Java interface into Restlet method + * calls. + * + * @param + * @param resourceInterface The annotated resource interface class to proxy. + * @param classLoader The classloader used to instantiate the dynamic proxy. + * @return The proxy instance. + */ + @SuppressWarnings("unchecked") + public T wrap(Class resourceInterface, ClassLoader classLoader) { + final T result; + + // Create the client resource proxy + java.lang.reflect.InvocationHandler h = + new org.restlet.engine.resource.ClientInvocationHandler(this, resourceInterface); + + // Instantiate our dynamic proxy + result = + (T) + java.lang.reflect.Proxy.newProxyInstance( + classLoader, + new Class[] {ClientProxy.class, resourceInterface}, + h); + + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/Delete.java b/org.restlet/src/main/java/org/restlet/resource/Delete.java index 63870f41cc..0550830bb6 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Delete.java +++ b/org.restlet/src/main/java/org/restlet/resource/Delete.java @@ -1,42 +1,44 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.restlet.engine.connector.Method; import org.restlet.service.MetadataService; -import java.lang.annotation.*; - /** - * Annotation for methods that remove representations. Its semantics is - * equivalent to an HTTP DELETE method.
+ * Annotation for methods that remove representations. Its semantics are equivalent to an HTTP + * DELETE method.
*
* Example: - * + * *

  * @Delete()
  * public void removeAll();
- * 
+ *
  * @Delete("xml|json")
  * public Representation removeAll();
- * 
+ *
  * @Delete("json?param=val")
  * public Representation removeAllWithParam();
- * 
+ *
  * @Delete("json?param")
  * public Representation removeAllWithParam();
- * 
+ *
  * @Delete("?param")
  * public Representation removeAllWithParam();
  * 
- * + * * @author Jerome Louvel */ @Documented @@ -45,18 +47,15 @@ @Method("DELETE") public @interface Delete { - /** - * Specifies the media type extension of the response entity. If several media - * types are supported, their extension can be specified separated by "|" - * characters. Note that this isn't the full MIME type value, just the extension - * name declared in {@link MetadataService}. For a list of all predefined - * extensions, please check {@link MetadataService#addCommonExtensions()}. New - * extension can be registered using - * {@link MetadataService#addExtension(String, org.restlet.data.Metadata)} - * method. - * - * @return The result media types. - */ - String value() default ""; - + /** + * Specifies the media type extension of the response entity. If several media types are + * supported, their extension can be specified separated by "|" characters. Note that this isn't + * the full MIME type value, just the extension name declared in {@link MetadataService}. For a + * list of all predefined extensions, please check {@link + * MetadataService#addCommonExtensions()}. New extension can be registered using {@link + * MetadataService#addExtension(String, org.restlet.data.Metadata)} method. + * + * @return The result media types. + */ + String value() default ""; } diff --git a/org.restlet/src/main/java/org/restlet/resource/Directory.java b/org.restlet/src/main/java/org/restlet/resource/Directory.java index 6411a01d85..00565d0819 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Directory.java +++ b/org.restlet/src/main/java/org/restlet/resource/Directory.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -21,325 +23,299 @@ import org.restlet.representation.Representation; import org.restlet.representation.Variant; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - /** - * Finder mapping a directory of local resources. Those resources have - * representations accessed by the file system, the class loaders or other URI - * accessible protocols. Here is some sample code illustrating how to attach a - * directory to a router:
- * + * Finder mapping a directory of local resources. Those resources have representations accessed by + * the file system, the class loaders, or other URI-accessible protocols. Here is some sample code + * illustrating how to attach a directory to a router:
+ * *

  * Directory directory = new Directory(getContext(), "file:///user/data/files/");
  * Router router = new Router(getContext());
  * router.attach("/static/", directory);
  * 
- * - * An automatic content negotiation mechanism (similar to the one in Apache HTTP - * server) is used to select the best representation of a resource based on the - * available variants and on the client capabilities and preferences.
+ * + * An automatic content negotiation mechanism (similar to the one in Apache HTTP Server) is used to + * select the best representation of a resource based on the available variants and on the client + * capabilities and preferences.
*
- * The directory can be used in read-only or modifiable mode. In the latter - * case, you just need to set the "modifiable" property to true. The currently - * supported methods are PUT and DELETE.
+ * The directory can be used in read-only or modifiable mode. In the latter case, you need to set + * the "modifiable" property to true. The currently supported methods are PUT and DELETE.
*
- * When no index is available in a given directory, a representation can be - * automatically generated by the - * {@link #getIndexRepresentation(Variant, ReferenceList)} method, unless the - * "listingAllowed" property is turned off. You can even customize the way the - * index entries are sorted by using the {@link #setComparator(Comparator)} - * method. The default sorting uses the friendly Alphanum algorithm based on - * David Koelle's original - * idea, using a different and faster implementation contributed by Rob - * Heittman.
+ * When no index is available in a given directory, a representation can be automatically generated + * by the {@link #getIndexRepresentation(Variant, ReferenceList)} method, unless the + * "listingAllowed" property is turned off. You can even customize the way the index entries are + * sorted by using the {@link #setComparator(Comparator)} method. The default sorting uses the + * friendly Alphanum algorithm based on David Koelle's original idea, using a different and faster + * implementation contributed by Rob Heittman.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class Directory extends Finder { - /** The reference comparator to sort index pages. */ - private volatile Comparator comparator; - - /** - * Indicates if the subdirectories are deeply accessible (true by default). - */ - private volatile boolean deeplyAccessible; - - /** The index name, without extensions (ex: "index" or "home"). */ - private volatile String indexName; - - /** - * Indicates if the display of directory listings is allowed when no index file - * is found. - */ - private volatile boolean listingAllowed; - - /** - * Indicates if modifications to local resources are allowed (false by default). - */ - private volatile boolean modifiable; - - /** Indicates if the best content is automatically negotiated. */ - private volatile boolean negotiatingContent; - - /** The absolute root reference (file, clap URI). */ - private volatile Reference rootRef; - - /** - * Constructor. - * - * @param context The context. - * @param rootLocalReference The root URI. - */ - public Directory(Context context, Reference rootLocalReference) { - super(context); - - // First, let's normalize the root reference to prevent any issue with - // relative paths inside the reference leading to listing issues. - final String rootIdentifier = rootLocalReference.getTargetRef().getIdentifier(); - - if (rootIdentifier.endsWith("/")) { - this.rootRef = new Reference(rootIdentifier); - } else { - // We don't take the risk of exposing directory "file:///C:/AA" - // if only "file:///C:/A" was intended - this.rootRef = new Reference(rootIdentifier + "/"); - } - - this.comparator = new AlphaNumericComparator(); - this.deeplyAccessible = true; - this.indexName = "index"; - this.listingAllowed = false; - this.modifiable = false; - this.negotiatingContent = true; - setTargetClass(DirectoryServerResource.class); - setName("Directory"); - } - - /** - * Constructor. - * - * @param context The context. - * @param rootUri The absolute root URI.
- *
- * If you serve files from the file system, use file:// URIs and - * make sure that you register a FILE connector with your parent - * Component. On Windows, make sure that you add enough slash - * characters at the beginning, for example: - * file:///c:/dir/file
- *
- * If you serve files from a class loader, use clap:// URIs and - * make sure that you register a CLAP connector with your parent - * Component.
- *
- */ - public Directory(Context context, String rootUri) { - this(context, new Reference(rootUri)); - } - - /** - * Returns the reference comparator used to sort index pages. The default - * implementation used a friendly alphanum sorting. - * - * @return The reference comparator. - * @see AlphaNumericComparator - */ - public Comparator getComparator() { - return this.comparator; - } - - /** - * Returns the index name, without extensions. Returns "index" by default. - * - * @return The index name. - */ - public String getIndexName() { - return this.indexName; - } - - /** - * Returns an actual index representation for a given variant. - * - * @param variant The selected variant. - * @param indexContent The directory index to represent. - * @return The actual index representation. - */ - public Representation getIndexRepresentation(Variant variant, ReferenceList indexContent) { - Representation result = null; - if (variant.getMediaType().equals(MediaType.TEXT_HTML)) { - result = indexContent.getWebRepresentation(); - } else if (variant.getMediaType().equals(MediaType.TEXT_URI_LIST)) { - result = indexContent.getTextRepresentation(); - } - return result; - } - - /** - * Returns the variant representations of a directory index. This method can be - * subclassed to provide alternative representations. - * - * By default, it returns a simple HTML document and a textual URI list as - * variants. Note that a new instance of the list is created for each call. - * - * @param indexContent The list of references contained in the directory index. - * @return The variant representations of a directory. - */ - public List getIndexVariants(ReferenceList indexContent) { - final List result = new ArrayList<>(); - result.add(new Variant(MediaType.TEXT_HTML)); - result.add(new Variant(MediaType.TEXT_URI_LIST)); - return result; - } - - /** - * Returns the root URI from which the relative resource URIs will be looked up. - * - * @return The root URI. - */ - public Reference getRootRef() { - return this.rootRef; - } - - @Override - public void handle(Request request, Response response) { - request.getAttributes().put("org.restlet.directory", this); - super.handle(request, response); - } - - /** - * Indicates if the subdirectories are deeply accessible (true by default). - * - * @return True if the subdirectories are deeply accessible. - */ - public boolean isDeeplyAccessible() { - return this.deeplyAccessible; - } - - /** - * Indicates if the display of directory listings is allowed when no index file - * is found. - * - * @return True if the display of directory listings is allowed when no index - * file is found. - */ - public boolean isListingAllowed() { - return this.listingAllowed; - } - - /** - * Indicates if modifications to local resources (most likely files) are - * allowed. Returns false by default. - * - * @return True if modifications to local resources are allowed. - */ - public boolean isModifiable() { - return this.modifiable; - } - - /** - * Indicates if the best content is automatically negotiated. The default value is - * true. - * - * @return True if the best content is automatically negotiated. - */ - public boolean isNegotiatingContent() { - return this.negotiatingContent; - } - - /** - * Sets the reference comparator used to sort index pages. - * - * @param comparator The reference comparator. - */ - public void setComparator(Comparator comparator) { - this.comparator = comparator; - } - - /** - * Indicates if the subdirectories are deeply accessible (true by default). - * - * @param deeplyAccessible True if the subdirectories are deeply accessible. - */ - public void setDeeplyAccessible(boolean deeplyAccessible) { - this.deeplyAccessible = deeplyAccessible; - } - - /** - * Sets the index name, without extensions. - * - * @param indexName The index name. - */ - public void setIndexName(String indexName) { - this.indexName = indexName; - } - - /** - * Indicates if the display of directory listings is allowed when no index file - * is found. - * - * @param listingAllowed True if the display of directory listings is allowed - * when no index file is found. - */ - public void setListingAllowed(boolean listingAllowed) { - this.listingAllowed = listingAllowed; - } - - /** - * Indicates if modifications to local resources are allowed. - * - * @param modifiable True if modifications to local resources are allowed. - */ - public void setModifiable(boolean modifiable) { - this.modifiable = modifiable; - } - - /** - * Indicates if the best content is automatically negotiated. Default value is - * true. - * - * @param negotiatingContent True if the best content is automatically - * negotiated. - */ - public void setNegotiatingContent(boolean negotiatingContent) { - this.negotiatingContent = negotiatingContent; - } - - /** - * Sets the root URI from which the relative resource URIs will be lookep up. - * - * @param rootRef The root URI. - */ - public void setRootRef(Reference rootRef) { - this.rootRef = rootRef; - } - - /** - * Sets the reference comparator based on classic alphabetical order. - * - * @see #setComparator(Comparator) - */ - public void useAlphaComparator() { - setComparator(new AlphabeticalComparator()); - } - - /** - * Sets the reference comparator based on the more friendly "Alphanum Algorithm" - * created by David Koelle. The internal implementation used is based on an - * optimized public domain implementation provided by Rob Heittman from the - * Solertium Corporation. - * - * @see The original Alphanum - * Algorithm from David Koelle - * @see #setComparator(Comparator) - */ - public void useAlphaNumComparator() { - setComparator(new AlphabeticalComparator()); - } - + /** The reference comparator to sort index pages. */ + private volatile Comparator comparator; + + /** Indicates if the subdirectories are deeply accessible (true by default). */ + private volatile boolean deeplyAccessible; + + /** The index name, without extensions (ex: "index" or "home"). */ + private volatile String indexName; + + /** Indicates if the display of directory listings is allowed when no index file is found. */ + private volatile boolean listingAllowed; + + /** Indicates if modifications to local resources are allowed (false by default). */ + private volatile boolean modifiable; + + /** Indicates if the best content is automatically negotiated. */ + private volatile boolean negotiatingContent; + + /** The absolute root reference (file, clap URI). */ + private volatile Reference rootRef; + + /** + * Constructor. + * + * @param context The context. + * @param rootLocalReference The root URI. + */ + public Directory(Context context, Reference rootLocalReference) { + super(context); + + // First, let's normalize the root reference to prevent any issue with + // relative paths inside the reference leading to listing issues. + final String rootIdentifier = rootLocalReference.getTargetRef().getIdentifier(); + + if (rootIdentifier.endsWith("/")) { + this.rootRef = new Reference(rootIdentifier); + } else { + // We don't take the risk of exposing directory "file:///C:/AA" + // if only "file:///C:/A" was intended + this.rootRef = new Reference(rootIdentifier + "/"); + } + + this.comparator = new AlphaNumericComparator(); + this.deeplyAccessible = true; + this.indexName = "index"; + this.listingAllowed = false; + this.modifiable = false; + this.negotiatingContent = true; + setTargetClass(DirectoryServerResource.class); + setName("Directory"); + } + + /** + * Constructor. + * + * @param context The context. + * @param rootUri The absolute root URI.
+ *
+ * If you serve files from the file system, use file:// URIs and make sure that you register + * a FILE connector with your parent Component. On Windows, make sure that you add enough + * slash characters at the beginning, for example: file:///c:/dir/file
+ *
+ * If you serve files from a class loader, use clap:// URIs and make sure that you register + * a CLAP connector with your parent Component.
+ *
+ */ + public Directory(Context context, String rootUri) { + this(context, new Reference(rootUri)); + } + + /** + * Returns the reference comparator used to sort index pages. The default implementation used a + * friendly alphanum sorting. + * + * @return The reference comparator. + * @see AlphaNumericComparator + */ + public Comparator getComparator() { + return this.comparator; + } + + /** + * Returns the index name, without extensions. Returns "index" by default. + * + * @return The index name. + */ + public String getIndexName() { + return this.indexName; + } + + /** + * Returns an actual index representation for a given variant. + * + * @param variant The selected variant. + * @param indexContent The directory index to represent. + * @return The actual index representation. + */ + public Representation getIndexRepresentation(Variant variant, ReferenceList indexContent) { + Representation result = null; + if (variant.getMediaType().equals(MediaType.TEXT_HTML)) { + result = indexContent.getWebRepresentation(); + } else if (variant.getMediaType().equals(MediaType.TEXT_URI_LIST)) { + result = indexContent.getTextRepresentation(); + } + return result; + } + + /** + * Returns the variant representations of a directory index. This method can be subclassed to + * provide alternative representations. + * + *

By default, it returns a simple HTML document and a textual URI list as variants. Note + * that a new instance of the list is created for each call. + * + * @param indexContent The list of references contained in the directory index. + * @return The variant representations of a directory. + */ + public List getIndexVariants(ReferenceList indexContent) { + final List result = new ArrayList<>(); + result.add(new Variant(MediaType.TEXT_HTML)); + result.add(new Variant(MediaType.TEXT_URI_LIST)); + return result; + } + + /** + * Returns the root URI from which the relative resource URIs will be looked up. + * + * @return The root URI. + */ + public Reference getRootRef() { + return this.rootRef; + } + + @Override + public void handle(Request request, Response response) { + request.getAttributes().put("org.restlet.directory", this); + super.handle(request, response); + } + + /** + * Indicates if the subdirectories are deeply accessible (true by default). + * + * @return True if the subdirectories are deeply accessible. + */ + public boolean isDeeplyAccessible() { + return this.deeplyAccessible; + } + + /** + * Indicates if the display of directory listings is allowed when no index file is found. + * + * @return True if the display of directory listings is allowed when no index file is found. + */ + public boolean isListingAllowed() { + return this.listingAllowed; + } + + /** + * Indicates if modifications to local resources (most likely files) are allowed. Returns false + * by default. + * + * @return True if modifications to local resources are allowed. + */ + public boolean isModifiable() { + return this.modifiable; + } + + /** + * Indicates if the best content is automatically negotiated. The default value is true. + * + * @return True if the best content is automatically negotiated. + */ + public boolean isNegotiatingContent() { + return this.negotiatingContent; + } + + /** + * Sets the reference comparator used to sort index pages. + * + * @param comparator The reference comparator. + */ + public void setComparator(Comparator comparator) { + this.comparator = comparator; + } + + /** + * Indicates if the subdirectories are deeply accessible (true by default). + * + * @param deeplyAccessible True if the subdirectories are deeply accessible. + */ + public void setDeeplyAccessible(boolean deeplyAccessible) { + this.deeplyAccessible = deeplyAccessible; + } + + /** + * Sets the index name, without extensions. + * + * @param indexName The index name. + */ + public void setIndexName(String indexName) { + this.indexName = indexName; + } + + /** + * Indicates if the display of directory listings is allowed when no index file is found. + * + * @param listingAllowed True if the display of directory listings is allowed when no index file + * is found. + */ + public void setListingAllowed(boolean listingAllowed) { + this.listingAllowed = listingAllowed; + } + + /** + * Indicates if modifications to local resources are allowed. + * + * @param modifiable True if modifications to local resources are allowed. + */ + public void setModifiable(boolean modifiable) { + this.modifiable = modifiable; + } + + /** + * Indicates if the best content is automatically negotiated. Default value is true. + * + * @param negotiatingContent True if the best content is automatically negotiated. + */ + public void setNegotiatingContent(boolean negotiatingContent) { + this.negotiatingContent = negotiatingContent; + } + + /** + * Sets the root URI from which the relative resource URIs will be looked up. + * + * @param rootRef The root URI. + */ + public void setRootRef(Reference rootRef) { + this.rootRef = rootRef; + } + + /** + * Sets the reference comparator based on classic alphabetical order. + * + * @see #setComparator(Comparator) + */ + public void useAlphaComparator() { + setComparator(new AlphabeticalComparator()); + } + + /** + * Sets the reference comparator based on the more friendly "Alphanum Algorithm" created by + * David Koelle. The internal implementation used is based on an optimized public domain + * implementation provided by Rob Heittman from the Solertium Corporation. + * + * @see The original Alphanum Algorithm from + * David Koelle + * @see #setComparator(Comparator) + */ + public void useAlphaNumComparator() { + setComparator(new AlphabeticalComparator()); + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/Finder.java b/org.restlet/src/main/java/org/restlet/resource/Finder.java index aae04a5b71..66ef2fc78c 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Finder.java +++ b/org.restlet/src/main/java/org/restlet/resource/Finder.java @@ -1,229 +1,229 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.lang.reflect.Constructor; +import java.util.logging.Level; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; import org.restlet.data.Status; -import java.lang.reflect.Constructor; -import java.util.logging.Level; -import java.util.logging.Logger; - /** - * Restlet that can find the target server resource that will effectively handle - * incoming calls. By default, based on a given {@link ServerResource} subclass - * available via the {@link #getTargetClass()} method, it automatically - * instantiates for each incoming call the target resource class using its - * default constructor and invoking the - * {@link ServerResource#init(Context, Request, Response)} method.
+ * Restlet that can find the target server resource that will effectively handle incoming calls. By + * default, based on a given {@link ServerResource} subclass available via the {@link + * #getTargetClass()} method, it automatically instantiates for each incoming call the target + * resource class using its default constructor and invoking the {@link ServerResource#init(Context, + * Request, Response)} method.
*
- * Once the target has been created, the call is automatically dispatched to the - * {@link ServerResource#handle()} method.
+ * Once the target has been created, the call is automatically dispatched to the {@link + * ServerResource#handle()} method.
*
- * Once the call is handled, the {@link ServerResource#release()} method is - * invoked to permit clean-up actions.
+ * Once the call is handled, the {@link ServerResource#release()} method is invoked to permit + * cleanup actions.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class Finder extends Restlet { - /** - * Creates a new finder instance based on the "targetClass" property. - * - * @param targetClass The target Resource class to attach. - * @param finderClass The optional finder class to instantiate. - * @param context The current Context. - * @param logger The logger. - * @return The new finder instance. - */ - public static Finder createFinder(Class targetClass, Class finderClass, - Context context, Logger logger) { - Finder result = null; - - if (finderClass != null) { - try { - Constructor constructor = finderClass.getConstructor(Context.class, Class.class); - - if (constructor != null) { - result = constructor.newInstance(context, targetClass); - } - } catch (Exception e) { - if (logger != null) { - logger.log(Level.WARNING, "Exception while instantiating the finder.", e); - } - } - } else { - result = new Finder(context, targetClass); - } - - return result; - } - - /** Target {@link ServerResource} subclass. */ - private volatile Class targetClass; - - /** - * Constructor. - */ - public Finder() { - this(null); - } - - /** - * Constructor. - * - * @param context The context. - */ - public Finder(Context context) { - super(context); - this.targetClass = null; - } - - /** - * Constructor. - * - * @param context The context. - * @param targetClass The target {@link ServerResource} subclass. - */ - public Finder(Context context, Class targetClass) { - super(context); - this.targetClass = targetClass; - } - - /** - * Creates a new instance of a given {@link ServerResource} subclass. Note that - * {@link Error} and {@link RuntimeException} thrown by {@link ServerResource} - * constructors are re-thrown by this method. Other exception are caught and - * logged. - * - * @param targetClass The target {@link ServerResource} subclass. - * @param request The request to handle. - * @param response The response to update. - * @return The created resource or null. - */ - public ServerResource create(Class targetClass, Request request, Response response) { - ServerResource result = null; - - if (targetClass != null) { - try { - // Invoke the default constructor - result = targetClass.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - getLogger().log(Level.WARNING, "Exception while instantiating the target server resource.", e); - } - } - - return result; - } - - /** - * Creates a new instance of the {@link ServerResource} subclass designated by - * the "targetClass" property. The default behavior is to invoke the - * {@link #create(Class, Request, Response)} with the "targetClass" property as - * a parameter. - * - * @param request The request to handle. - * @param response The response to update. - * @return The created resource or null. - */ - public ServerResource create(Request request, Response response) { - ServerResource result = null; - - if (getTargetClass() != null) { - result = create(getTargetClass(), request, response); - } - - return result; - } - - /** - * Finds the target {@link ServerResource} if available. The default behavior is - * to invoke the {@link #create(Request, Response)} method. - * - * @param request The request to handle. - * @param response The response to update. - * @return The target resource if available or null. - */ - public ServerResource find(Request request, Response response) { - return create(request, response); - } - - /** - * Returns the target resource class which must be either a subclass of - * {@link ServerResource}. - * - * @return the target Handler class. - */ - public Class getTargetClass() { - return this.targetClass; - } - - /** - * Handles a call. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - - if (isStarted()) { - ServerResource targetResource = find(request, response); - - if (targetResource == null) { - // If the current status is a success, but we couldn't - // find the target resource for the request's URI, - // then we set the response status to 404 (Not Found). - if (getLogger().isLoggable(Level.WARNING)) { - getLogger().warning("No target resource was defined for this finder: " + this); - } - - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } else { - targetResource.init(getContext(), request, response); - - if ((response == null) || response.getStatus().isSuccess()) { - targetResource.handle(); - } else { - // Probably during the instantiation of the target - // server resource, or earlier the status was - // changed from the default one. Don't go further. - } - - targetResource.release(); - } - } - } - - /** - * Sets the target resource class which must be a subclass of - * {@link ServerResource}. - * - * @param targetClass The target resource class. It must be a subclass of - * {@link ServerResource}. - */ - public void setTargetClass(Class targetClass) { - this.targetClass = targetClass; - } - - @Override - public String toString() { - return getTargetClass() == null ? "Finder with no target class" - : "Finder for " + getTargetClass().getSimpleName(); - } - + /** + * Creates a new finder instance based on the "targetClass" property. + * + * @param targetClass The target Resource class to attach. + * @param finderClass The optional finder class to instantiate. + * @param context The current Context. + * @param logger The logger. + * @return The new finder instance. + */ + public static Finder createFinder( + Class targetClass, + Class finderClass, + Context context, + Logger logger) { + Finder result = null; + + if (finderClass != null) { + try { + Constructor constructor = + finderClass.getConstructor(Context.class, Class.class); + + if (constructor != null) { + result = constructor.newInstance(context, targetClass); + } + } catch (Exception e) { + if (logger != null) { + logger.log(Level.WARNING, "Exception while instantiating the finder.", e); + } + } + } else { + result = new Finder(context, targetClass); + } + + return result; + } + + /** Target {@link ServerResource} subclass. */ + private volatile Class targetClass; + + /** Constructor. */ + public Finder() { + this(null); + } + + /** + * Constructor. + * + * @param context The context. + */ + public Finder(Context context) { + super(context); + this.targetClass = null; + } + + /** + * Constructor. + * + * @param context The context. + * @param targetClass The target {@link ServerResource} subclass. + */ + public Finder(Context context, Class targetClass) { + super(context); + this.targetClass = targetClass; + } + + /** + * Creates a new instance of a given {@link ServerResource} subclass. Note that {@link Error} + * and {@link RuntimeException} thrown by {@link ServerResource} constructors are re-thrown by + * this method. Other exception is caught and logged. + * + * @param targetClass The target {@link ServerResource} subclass. + * @param request The request to handle. + * @param response The response to update. + * @return The created resource or null. + */ + public ServerResource create( + Class targetClass, Request request, Response response) { + ServerResource result = null; + + if (targetClass != null) { + try { + // Invoke the default constructor + result = targetClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + getLogger() + .log( + Level.WARNING, + "Exception while instantiating the target server resource.", + e); + } + } + + return result; + } + + /** + * Creates a new instance of the {@link ServerResource} subclass designated by the "targetClass" + * property. The default behavior is to invoke the {@link #create(Class, Request, Response)} + * with the "targetClass" property as a parameter. + * + * @param request The request to handle. + * @param response The response to update. + * @return The created resource or null. + */ + public ServerResource create(Request request, Response response) { + ServerResource result = null; + + if (getTargetClass() != null) { + result = create(getTargetClass(), request, response); + } + + return result; + } + + /** + * Finds the target {@link ServerResource} if available. The default behavior is to invoke the + * {@link #create(Request, Response)} method. + * + * @param request The request to handle. + * @param response The response to update. + * @return The target resource if available or null. + */ + public ServerResource find(Request request, Response response) { + return create(request, response); + } + + /** + * Returns the target resource class which must be either a subclass of {@link ServerResource}. + * + * @return the target Handler class. + */ + public Class getTargetClass() { + return this.targetClass; + } + + /** + * Handles a call. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public void handle(Request request, Response response) { + super.handle(request, response); + + if (isStarted()) { + ServerResource targetResource = find(request, response); + + if (targetResource == null) { + // If the current status is a success, but we couldn't + // find the target resource for the request's URI, + // then we set the response status to 404 (Not Found). + if (getLogger().isLoggable(Level.WARNING)) { + getLogger().warning("No target resource was defined for this finder: " + this); + } + + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } else { + targetResource.init(getContext(), request, response); + + if ((response == null) || response.getStatus().isSuccess()) { + targetResource.handle(); + } else { + // Probably during the instantiation of the target + // server resource, or earlier, the status was + // changed from the default one. Don't go further. + } + + targetResource.release(); + } + } + } + + /** + * Sets the target resource class which must be a subclass of {@link ServerResource}. + * + * @param targetClass The target resource class. It must be a subclass of {@link + * ServerResource}. + */ + public void setTargetClass(Class targetClass) { + this.targetClass = targetClass; + } + + @Override + public String toString() { + return getTargetClass() == null + ? "Finder with no target class" + : "Finder for " + getTargetClass().getSimpleName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/Get.java b/org.restlet/src/main/java/org/restlet/resource/Get.java index df77fdceaa..5b3c62d295 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Get.java +++ b/org.restlet/src/main/java/org/restlet/resource/Get.java @@ -1,45 +1,47 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.restlet.engine.connector.Method; import org.restlet.service.MetadataService; -import java.lang.annotation.*; - /** - * Annotation for methods that retrieve a resource representation. Its semantics - * is equivalent to an HTTP GET method.
+ * Annotation for methods that retrieve a resource representation. Its semantics are equivalent to + * an HTTP GET method.
*
* Example: - * + * *

  * @Get
  * public MyBean represent();
- * 
+ *
  * @Get("json")
  * public String toJson();
- * 
+ *
  * @Get("xml|html")
  * public Representation represent();
- * 
+ *
  * @Get("json?param=val")
  * public Representation representWithParam();
- * 
+ *
  * @Get("json?param")
  * public Representation representWithParam();
- * 
+ *
  * @Get("?param")
  * public Representation representWithParam();
  * 
- * + * * @author Jerome Louvel */ @Documented @@ -48,18 +50,15 @@ @Method("GET") public @interface Get { - /** - * Specifies the media type extension of the response entity. If several media - * types are supported, their extension can be specified separated by "|" - * characters. Note that this isn't the full MIME type value, just the extension - * name declared in {@link MetadataService}. For a list of all predefined - * extensions, please check {@link MetadataService#addCommonExtensions()}. New - * extension can be registered using - * {@link MetadataService#addExtension(String, org.restlet.data.Metadata)} - * method. - * - * @return The result media types. - */ - String value() default ""; - + /** + * Specifies the media type extension of the response entity. If several media types are + * supported, their extension can be specified separated by "|" characters. Note that this isn't + * the full MIME type value, just the extension name declared in {@link MetadataService}. For a + * list of all predefined extensions, please check {@link + * MetadataService#addCommonExtensions()}. New extension can be registered using {@link + * MetadataService#addExtension(String, org.restlet.data.Metadata)} method. + * + * @return The result media types. + */ + String value() default ""; } diff --git a/org.restlet/src/main/java/org/restlet/resource/Options.java b/org.restlet/src/main/java/org/restlet/resource/Options.java index 01a7492790..ec3b7fa8f0 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Options.java +++ b/org.restlet/src/main/java/org/restlet/resource/Options.java @@ -1,42 +1,44 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.restlet.engine.connector.Method; import org.restlet.service.MetadataService; -import java.lang.annotation.*; - /** - * Annotation for methods that describe a resource. Its semantics is equivalent - * to an HTTP OPTIONS method.
+ * Annotation for methods that describe a resource. Its semantics are equivalent to an HTTP OPTIONS + * method.
*
* Example: - * + * *

  * @Options
  * public ApplicationInfo describe();
- * 
+ *
  * @Options("wadl|html")
  * public Representation describe();
- * 
+ *
  * @Options("wadl?param=val")
  * public Representation describeWithParam();
- * 
+ *
  * @Options("wadl?param")
  * public Representation describeWithParam();
- * 
+ *
  * @Options("?param")
  * public Representation describeWithParam();
  * 
- * + * * @author Jerome Louvel */ @Documented @@ -45,18 +47,15 @@ @Method("OPTIONS") public @interface Options { - /** - * Specifies the media type extension of the response entity. If several media - * types are supported, their extension can be specified separated by "|" - * characters. Note that this isn't the full MIME type value, just the extension - * name declared in {@link MetadataService}. For a list of all predefined - * extensions, please check {@link MetadataService#addCommonExtensions()}. New - * extension can be registered using - * {@link MetadataService#addExtension(String, org.restlet.data.Metadata)} - * method. - * - * @return The result media types. - */ - String value() default ""; - + /** + * Specifies the media type extension of the response entity. If several media types are + * supported, their extension can be specified separated by "|" characters. Note that this isn't + * the full MIME type value, just the extension name declared in {@link MetadataService}. For a + * list of all predefined extensions, please check {@link + * MetadataService#addCommonExtensions()}. New extension can be registered using {@link + * MetadataService#addExtension(String, org.restlet.data.Metadata)} method. + * + * @return The result media types. + */ + String value() default ""; } diff --git a/org.restlet/src/main/java/org/restlet/resource/Patch.java b/org.restlet/src/main/java/org/restlet/resource/Patch.java index 6da6843e43..56a61cc69b 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Patch.java +++ b/org.restlet/src/main/java/org/restlet/resource/Patch.java @@ -1,47 +1,48 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.restlet.engine.connector.Method; import org.restlet.service.MetadataService; -import java.lang.annotation.*; - /** - * Annotation for methods that apply submitted representations as a patch. Its - * semantics is equivalent to an HTTP PATCH method. Note that your method must - * have one input parameter if you want it to be selected for requests - * containing an entity.
+ * Annotation for methods that apply submitted representations as a patch. Its semantics are + * equivalent to an HTTP PATCH method. Note that your method must have one input parameter if you + * want it to be selected for requests containing an entity.
*
* Example: - * + * *

  * @Patch
  * public Representation update(Representation input);
- * 
+ *
  * @Patch("json-patch")
  * public String updateJson(String value);
- * 
+ *
  * @Patch("json-patch|xml-patch:xml|json")
  * public Representation update(Representation value);
- * 
+ *
  * @Patch("json?param=val")
  * public Representation updateWithParam(String value);
- * 
+ *
  * @Patch("json?param")
  * public Representation updateWithParam(String value);
- * 
+ *
  * @Patch("?param")
  * public Representation updateWithParam(String value);
  * 
- * + * * @author Jerome Louvel */ @Documented @@ -50,24 +51,19 @@ @Method("PATCH") public @interface Patch { - /** - * Specifies the media type of the request and response entities as extensions. - * If only one extension is provided, the extension applies to both request and - * response entities. If two extensions are provided, separated by a colon, then - * the first one is for the request entity and the second one for the response - * entity.
- *
- * If several media types are supported, their extension can be specified - * separated by "|" characters. Note that this isn't the full MIME type value, - * just the extension name declared in {@link MetadataService}. For a list of - * all predefined extensions, please check - * {@link MetadataService#addCommonExtensions()}. New extension can be - * registered using - * {@link MetadataService#addExtension(String, org.restlet.data.Metadata)} - * method. - * - * @return The media types of request and/or response entities. - */ - String value() default ""; - + /** + * Specifies the media type of the request and response entities as extensions. If only one + * extension is provided, the extension applies to both request and response entities. If two + * extensions are provided, separated by a colon, then the first one is for the request entity + * and the second one for the response entity.
+ *
+ * If several media types are supported, their extension can be specified separated by "|" + * characters. Note that this isn't the full MIME type value, just the extension name declared + * in {@link MetadataService}. For a list of all predefined extensions, please check {@link + * MetadataService#addCommonExtensions()}. New extension can be registered using {@link + * MetadataService#addExtension(String, org.restlet.data.Metadata)} method. + * + * @return The media types of request and/or response entities. + */ + String value() default ""; } diff --git a/org.restlet/src/main/java/org/restlet/resource/Post.java b/org.restlet/src/main/java/org/restlet/resource/Post.java index 7bffa7b6e2..c2cab94af5 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Post.java +++ b/org.restlet/src/main/java/org/restlet/resource/Post.java @@ -1,47 +1,48 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.restlet.engine.connector.Method; import org.restlet.service.MetadataService; -import java.lang.annotation.*; - /** - * Annotation for methods that accept submitted representations. Its semantics - * is equivalent to an HTTP POST method. Note that your method must have one - * input parameter if you want it to be selected for requests containing an - * entity.
+ * Annotation for methods that accept submitted representations. Its semantics are equivalent to an + * HTTP POST method. Note that your method must have one input parameter if you want it to be + * selected for requests containing an entity.
*
* Example: - * + * *

  * @Post
  * public MyOutputBean accept(MyInputBean input);
- * 
+ *
  * @Post("json")
  * public String acceptJson(String value);
- * 
+ *
  * @Post("xml|json:xml|json")
  * public Representation accept(Representation xmlValue);
- * 
+ *
  * @Post("json?param=val")
  * public Representation acceptWithParam(String value);
- * 
+ *
  * @Post("json?param")
  * public Representation acceptWithParam(String value);
- * 
+ *
  * @Post("?param")
  * public Representation acceptWithParam(String value);
  * 
- * + * * @author Jerome Louvel */ @Documented @@ -50,24 +51,19 @@ @Method("POST") public @interface Post { - /** - * Specifies the media type of the request and response entities as extensions. - * If only one extension is provided, the extension applies to both request and - * response entities. If two extensions are provided, separated by a colon, then - * the first one is for the request entity and the second one for the response - * entity.
- *
- * If several media types are supported, their extension can be specified - * separated by "|" characters. Note that this isn't the full MIME type value, - * just the extension name declared in {@link MetadataService}. For a list of - * all predefined extensions, please check - * {@link MetadataService#addCommonExtensions()}. New extension can be - * registered using - * {@link MetadataService#addExtension(String, org.restlet.data.Metadata)} - * method. - * - * @return The media types of request and/or response entities. - */ - String value() default ""; - + /** + * Specifies the media type of the request and response entities as extensions. If only one + * extension is provided, the extension applies to both request and response entities. If two + * extensions are provided, separated by a colon, then the first one is for the request entity + * and the second one for the response entity.
+ *
+ * If several media types are supported, their extension can be specified separated by "|" + * characters. Note that this isn't the full MIME type value, just the extension name declared + * in {@link MetadataService}. For a list of all predefined extensions, please check {@link + * MetadataService#addCommonExtensions()}. New extension can be registered using {@link + * MetadataService#addExtension(String, org.restlet.data.Metadata)} method. + * + * @return The media types of request and/or response entities. + */ + String value() default ""; } diff --git a/org.restlet/src/main/java/org/restlet/resource/Put.java b/org.restlet/src/main/java/org/restlet/resource/Put.java index 4ad3984356..d84b5702f6 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Put.java +++ b/org.restlet/src/main/java/org/restlet/resource/Put.java @@ -1,47 +1,48 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.restlet.engine.connector.Method; import org.restlet.service.MetadataService; -import java.lang.annotation.*; - /** - * Annotation for methods that store submitted representations. Its semantics is - * equivalent to an HTTP PUT method. Note that your method must have one input - * parameter if you want it to be selected for requests containing an - * entity.
+ * Annotation for methods that store submitted representations. Its semantics are equivalent to an + * HTTP PUT method. Note that your method must have one input parameter if you want it to be + * selected for requests containing an entity.
*
* Example: - * + * *

  * @Put
  * public MyOutputBean store(MyInputBean input);
- * 
+ *
  * @Put("json")
  * public String storeJson(String value);
- * 
+ *
  * @Put("json|xml:xml|json")
  * public Representation store(Representation value);
- * 
+ *
  * @Put("json?param=val")
  * public Representation storeWithParam(String value);
- * 
+ *
  * @Put("json?param")
  * public Representation storeWithParam(String value);
- * 
+ *
  * @Put("?param")
  * public Representation storeWithParam(String value);
  * 
- * + * * @author Jerome Louvel */ @Documented @@ -50,24 +51,19 @@ @Method("PUT") public @interface Put { - /** - * Specifies the media type of the request and response entities as extensions. - * If only one extension is provided, the extension applies to both request and - * response entities. If two extensions are provided, separated by a colon, then - * the first one is for the request entity and the second one for the response - * entity.
- *
- * If several media types are supported, their extension can be specified - * separated by "|" characters. Note that this isn't the full MIME type value, - * just the extension name declared in {@link MetadataService}. For a list of - * all predefined extensions, please check - * {@link MetadataService#addCommonExtensions()}. New extension can be - * registered using - * {@link MetadataService#addExtension(String, org.restlet.data.Metadata)} - * method. - * - * @return The media types of request and/or response entities. - */ - String value() default ""; - + /** + * Specifies the media type of the request and response entities as extensions. If only one + * extension is provided, the extension applies to both request and response entities. If two + * extensions are provided, separated by a colon, then the first one is for the request entity + * and the second one for the response entity.
+ *
+ * If several media types are supported, their extension can be specified separated by "|" + * characters. Note that this isn't the full MIME type value, just the extension name declared + * in {@link MetadataService}. For a list of all predefined extensions, please check {@link + * MetadataService#addCommonExtensions()}. New extension can be registered using {@link + * MetadataService#addExtension(String, org.restlet.data.Metadata)} method. + * + * @return The media types of request and/or response entities. + */ + String value() default ""; } diff --git a/org.restlet/src/main/java/org/restlet/resource/Resource.java b/org.restlet/src/main/java/org/restlet/resource/Resource.java index 275daaea36..8a9a82d8bb 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Resource.java +++ b/org.restlet/src/main/java/org/restlet/resource/Resource.java @@ -1,881 +1,869 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; +import org.restlet.data.CacheDirective; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ClientInfo; +import org.restlet.data.Conditions; +import org.restlet.data.Cookie; +import org.restlet.data.CookieSetting; +import org.restlet.data.Dimension; +import org.restlet.data.Form; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Protocol; +import org.restlet.data.Range; +import org.restlet.data.Reference; +import org.restlet.data.ServerInfo; import org.restlet.data.Status; -import org.restlet.data.*; import org.restlet.representation.Representation; import org.restlet.representation.Variant; import org.restlet.service.MetadataService; import org.restlet.util.Series; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - /** - * Base resource class exposing the uniform REST interface. Intended conceptual - * target of a hypertext reference. An uniform resource encapsulates a - * {@link Context}, a {@link Request} and a {@link Response}, corresponding to a - * specific target resource.
+ * Base resource class exposing the uniform REST interface. Intended conceptual target of a + * hypertext reference. A uniform resource encapsulates a {@link Context}, a {@link Request} and a + * {@link Response}, corresponding to a specific target resource.
*
- * It also defines a precise life cycle. First, the instance is created and the - * {@link #init(Context, Request, Response)} method is invoked. If you need to - * do some additional initialization, you should just override the - * {@link #doInit()} method.
+ * It also defines a precise life cycle. First, the instance is created and the {@link + * #init(Context, Request, Response)} method is invoked. If you need to do some additional + * initialization, you should override the {@link #doInit()} method.
*
- * Then, the abstract {@link #handle()} method can be invoked. For concrete - * behavior, see the {@link ClientResource} and {@link ServerResource} - * subclasses. Note that the state of the resource can be changed several times - * and the {@link #handle()} method called more than once, but always by the + * Then, the abstract {@link #handle()} method can be invoked. For concrete behavior, see the {@link + * ClientResource} and {@link ServerResource} subclasses. Note that the state of the resource can be + * changed several times and the {@link #handle()} method called more than once, but always by the * same thread.
*
- * Finally, the final {@link #release()} method can be called to clean-up the - * resource, with a chance for the developer to do some additional clean-up by - * overriding the {@link #doRelease()} method.
- *
- * Note also that throwable raised such as {@link Error} and {@link Exception} - * can be caught in a single point by overriding the {@link #doCatch(Throwable)} + * Finally, the final {@link #release()} method can be called to clean up the resource, with a + * chance for the developer to do some additional cleanup by overriding the {@link #doRelease()} * method.
*
- * "The central feature that distinguishes the REST architectural style from - * other network-based styles is its emphasis on a uniform interface between - * components. By applying the software engineering principle of generality to - * the component interface, the overall system architecture is simplified and - * the visibility of interactions is improved. Implementations are decoupled - * from the services they provide, which encourages independent evolvability." - * Roy T. Fielding
+ * Note also that throwable raised such as {@link Error} and {@link Exception} can be caught in a + * single point by overriding the {@link #doCatch(Throwable)} method.
+ *
+ * "The central feature that distinguishes the REST architectural style from other network-based + * styles is its emphasis on a uniform interface between components. By applying the software + * engineering principle of generality to the component interface, the overall system architecture + * is simplified and the visibility of interactions is improved. Implementations are decoupled from + * the services they provide, which encourages independent evolvability." Roy T. Fielding
*
- * Concurrency note: contrary to the {@link org.restlet.Uniform} class and its - * main {@link Restlet} subclass where a single instance can handle several - * calls concurrently, one instance of {@link Resource} is created for each call - * handled and accessed by only one thread at a time. - * - * @see Source - * dissertation + * Concurrency note: contrary to the {@link org.restlet.Uniform} class and its main {@link Restlet} + * subclass where a single instance can handle several calls concurrently, one instance of {@link + * Resource} is created for each call handled and accessed by only one thread at a time. + * + * @see Source + * dissertation * @author Jerome Louvel */ public abstract class Resource { - /** - * Converts the given {@link String} value into a {@link Boolean} or null. - * - * @param value The value to convert or null. - * @return The converted {@link Boolean} value or null. - */ - public static Boolean toBoolean(String value) { - return (value != null) ? Boolean.valueOf(value) : null; - } - - /** - * Converts the given {@link String} value into a {@link Byte} or null. - * - * @param value The value to convert or null. - * @return The converted {@link Byte} value or null. - */ - public static Byte toByte(String value) { - return (value != null) ? Byte.valueOf(value) : null; - } - - /** - * Converts the given {@link String} value into an {@link Double} or null. - * - * @param value The value to convert or null. - * @return The converted {@link Double} value or null. - */ - public static Double toDouble(String value) { - return (value != null) ? Double.valueOf(value) : null; - } - - /** - * Converts the given {@link String} value into a {@link Float} or null. - * - * @param value The value to convert or null. - * @return The converted {@link Float} value or null. - */ - public static Float toFloat(String value) { - return (value != null) ? Float.valueOf(value) : null; - } - - /** - * Converts the given {@link String} value into an {@link Integer} or null. - * - * @param value The value to convert or null. - * @return The converted {@link Integer} value or null. - */ - public static Integer toInteger(String value) { - return (value != null) ? Integer.valueOf(value) : null; - } - - /** - * Converts the given {@link String} value into an {@link Long} or null. - * - * @param value The value to convert or null. - * @return The converted {@link Long} value or null. - */ - public static Long toLong(String value) { - return (value != null) ? Long.valueOf(value) : null; - } - - /** - * Converts the given {@link String} value into a {@link Short} or null. - * - * @param value The value to convert or null. - * @return The converted {@link Short} value or null. - */ - public static Short toShort(String value) { - return (value != null) ? Short.valueOf(value) : null; - } - - /** The parent application. */ - private volatile org.restlet.Application application; - - /** The parent context. */ - private volatile Context context; - - /** The handled request. */ - private volatile Request request; - - /** The handled response. */ - private volatile Response response; - - /** - * Invoked when a {@link Throwable} is caught during initialization, handling or - * releasing. - * - * @param throwable The caught error or exception. - */ - protected void doCatch(Throwable throwable) { - getLogger().log(Level.INFO, "Exception or error caught in resource", throwable); - } - - /** - * Invoked when an error response status is received. - * - * @param errorStatus The error status received. - */ - protected void doError(Status errorStatus) { - } - - /** - * Invoked when an error response status is received. - * - * @param errorStatus The error status received. - * @param errorMessage The custom error message. - */ - protected final void doError(Status errorStatus, String errorMessage) { - doError(new Status(errorStatus, errorMessage)); - } - - /** - * Set-up method that can be overridden in order to initialize the state of the - * resource. By default it does nothing. - * - * @see #init(Context, Request, Response) - */ - protected void doInit() throws ResourceException { - } - - /** - * Clean-up method that can be overridden in order to release the state of the - * resource. By default it does nothing. - * - * @see #release() - */ - protected void doRelease() throws ResourceException { - } - - /** - * Returns the set of methods allowed for the current client by the resource. - * The result can vary based on the client's user agent, authentication and - * authorization data provided by the client. - * - * @return The set of allowed methods. - */ - public Set getAllowedMethods() { - return getResponse() == null ? null : getResponse().getAllowedMethods(); - } - - /** - * Returns the parent application. If it wasn't set, it attempts to retrieve the - * current one via {@link org.restlet.Application#getCurrent()} if it exists, or - * instantiates a new one as a last resort. - * - * @return The parent application if it exists, or a new one. - */ - public org.restlet.Application getApplication() { - org.restlet.Application result = this.application; - - if (result == null) { - result = org.restlet.Application.getCurrent(); - - if (result == null) { - result = new org.restlet.Application(getContext()); - } - - this.application = result; - } - - return result; - } - - /** - * Returns the attribute value by looking up the given name in the request or - * response attributes maps. This is typically used for variables that are - * declared in the URI template used to route the call to this resource. - * - * @param name The attribute name. - * @return The matching request or response attribute value. - */ - public abstract String getAttribute(String name); - - /** - * Returns the list of authentication requests sent by an origin server to a - * client. If none is available, an empty list is returned. - * - * @return The list of authentication requests. - * @see Response#getChallengeRequests() - */ - public List getChallengeRequests() { - return getResponse() == null ? null : getResponse().getChallengeRequests(); - } - - /** - * Returns the authentication response sent by a client to an origin server. - * - * @return The authentication response sent by a client to an origin server. - * @see Request#getChallengeResponse() - */ - public ChallengeResponse getChallengeResponse() { - return getRequest() == null ? null : getRequest().getChallengeResponse(); - } - - /** - * Returns the client-specific information. Creates a new instance if no one has - * been set. - * - * @return The client-specific information. - * @see Request#getClientInfo() - */ - public ClientInfo getClientInfo() { - return getRequest() == null ? null : getRequest().getClientInfo(); - } - - /** - * Returns the modifiable conditions applying to this request. Creates a new - * instance if no one has been set. - * - * @return The conditions applying to this call. - * @see Request#getConditions() - */ - public Conditions getConditions() { - return getRequest() == null ? null : getRequest().getConditions(); - } - - /** - * Returns the application's content negotiation service or create a new one. - * - * @return The content negotiation service. - */ - public org.restlet.service.ConnegService getConnegService() { - org.restlet.service.ConnegService result = null; - - result = getApplication().getConnegService(); - - if (result == null) { - result = new org.restlet.service.ConnegService(); - } - - return result; - } - - /** - * Returns the current context. - * - * @return The current context. - */ - public Context getContext() { - return context; - } - - /** - * Returns the application's converter service or create a new one. - * - * @return The converter service. - */ - public org.restlet.service.ConverterService getConverterService() { - org.restlet.service.ConverterService result = null; - - result = getApplication().getConverterService(); - - if (result == null) { - result = new org.restlet.service.ConverterService(); - } - - return result; - } - - /** - * Returns the modifiable series of cookies provided by the client. Creates a - * new instance if no one has been set. - * - * @return The cookies provided by the client. - * @see Request#getCookies() - */ - public Series getCookies() { - return getRequest() == null ? null : getRequest().getCookies(); - } - - /** - * Returns the modifiable series of cookie settings provided by the server. - * Creates a new instance if no one has been set. - * - * @return The cookie settings provided by the server. - * @see Response#getCookieSettings() - */ - public Series getCookieSettings() { - return getResponse() == null ? null : getResponse().getCookieSettings(); - } - - /** - * Returns the modifiable set of selecting dimensions on which the response - * entity may vary. If some server-side content negotiation is done, this set - * should be properly updated, other it can be left empty. Creates a new - * instance if no one has been set. - * - * @return The set of dimensions on which the response entity may vary. - * @see Response#getDimensions() - */ - public Set getDimensions() { - return getResponse() == null ? null : getResponse().getDimensions(); - } - - /** - * Returns the host reference. This may be different from the resourceRef's - * host, for example for URNs and other URIs that don't contain host - * information. - * - * @return The host reference. - * @see Request#getHostRef() - */ - public Reference getHostRef() { - return getRequest() == null ? null : getRequest().getHostRef(); - } - - /** - * Returns the reference that the client should follow for redirections or - * resource creations. - * - * @return The redirection reference. - * @see Response#getLocationRef() - */ - public Reference getLocationRef() { - return getResponse() == null ? null : getResponse().getLocationRef(); - } - - /** - * Returns the logger. - * - * @return The logger. - */ - public Logger getLogger() { - return getContext() != null ? getContext().getLogger() : Context.getCurrentLogger(); - } - - /** - * Returns the resource reference's optional matrix. - * - * @return The resource reference's optional matrix. - * @see Reference#getMatrixAsForm() - */ - public Form getMatrix() { - return getReference() == null ? null : getReference().getMatrixAsForm(); - } - - /** - * Returns the first value of the matrix parameter given its name if existing, - * or null. - * - * @param name The matrix parameter name. - * @return The first value of the matrix parameter. - */ - public String getMatrixValue(String name) { - String result = null; - Form matrix = getMatrix(); - - if (matrix != null) { - result = matrix.getFirstValue(name); - } - - return result; - } - - /** - * Returns the maximum number of intermediaries. - * - * @return The maximum number of intermediaries. - */ - public int getMaxForwards() { - return getRequest() == null ? null : getRequest().getMaxForwards(); - } - - /** - * Returns the application's metadata service or create a new one. - * - * @return The metadata service. - */ - public MetadataService getMetadataService() { - MetadataService result = null; - - result = getApplication().getMetadataService(); - - if (result == null) { - result = new MetadataService(); - } - - return result; - } - - /** - * Returns the method. - * - * @return The method. - * @see Request#getMethod() - */ - public Method getMethod() { - return getRequest() == null ? null : getRequest().getMethod(); - } - - /** - * Returns the original reference as requested by the client. Note that this - * property is not used during request routing. - * - * @return The original reference. - * @see Request#getOriginalRef() - */ - public Reference getOriginalRef() { - return getRequest() == null ? null : getRequest().getOriginalRef(); - } - - /** - * Returns the protocol by first returning the resourceRef.schemeProtocol - * property if it is set, or the baseRef.schemeProtocol property otherwise. - * - * @return The protocol or null if not available. - * @see Request#getProtocol() - */ - public Protocol getProtocol() { - return getRequest() == null ? null : getRequest().getProtocol(); - } - - /** - * Returns the list of proxy authentication requests sent by an origin server to - * a client. If none is available, an empty list is returned. - * - * @return The list of proxy authentication requests. - * @see Response#getProxyChallengeRequests() - */ - public List getProxyChallengeRequests() { - return getResponse() == null ? null : getResponse().getProxyChallengeRequests(); - } - - /** - * Returns the proxy authentication response sent by a client to an origin - * server. - * - * @return The proxy authentication response sent by a client to an origin - * server. - * @see Request#getProxyChallengeResponse() - */ - public ChallengeResponse getProxyChallengeResponse() { - return getRequest() == null ? null : getRequest().getProxyChallengeResponse(); - } - - /** - * Returns the resource reference's optional query. Note that modifications to - * the returned {@link Form} object aren't reported to the underlying reference. - * - * @return The resource reference's optional query. - * @see Reference#getQueryAsForm() - */ - public Form getQuery() { - return getReference() == null ? null : getReference().getQueryAsForm(); - } - - /** - * Returns the first value of the query parameter given its name if existing, or - * null. - * - * @param name The query parameter name. - * @return The first value of the query parameter. - */ - public String getQueryValue(String name) { - String result = null; - Form query = getQuery(); - - if (query != null) { - result = query.getFirstValue(name); - } - - return result; - } - - /** - * Returns the ranges to return from the target resource's representation. - * - * @return The ranges to return. - * @see Request#getRanges() - */ - public List getRanges() { - return getRequest() == null ? null : getRequest().getRanges(); - } - - /** - * Returns the URI reference. - * - * @return The URI reference. - */ - public Reference getReference() { - return getRequest() == null ? null : getRequest().getResourceRef(); - } - - /** - * Returns the referrer reference if available. - * - * @return The referrer reference. - */ - public Reference getReferrerRef() { - return getRequest() == null ? null : getRequest().getReferrerRef(); - } - - /** - * Returns the handled request. - * - * @return The handled request. - */ - public Request getRequest() { - return request; - } - - /** - * Returns the request attributes. - * - * @return The request attributes. - * @see Request#getAttributes() - */ - public Map getRequestAttributes() { - return getRequest() == null ? null : getRequest().getAttributes(); - } - - /** - * Returns the request cache directives. Note that when used with HTTP - * connectors, this property maps to the "Cache-Control" header. - * - * @return The cache directives. - */ - public List getRequestCacheDirectives() { - return getRequest() == null ? null : getRequest().getCacheDirectives(); - } - - /** - * Returns the request entity representation. - * - * @return The request entity representation. - */ - public Representation getRequestEntity() { - return getRequest() == null ? null : getRequest().getEntity(); - } - - /** - * Returns the handled response. - * - * @return The handled response. - */ - public Response getResponse() { - return response; - } - - /** - * Returns the response attributes. - * - * @return The response attributes. - * @see Response#getAttributes() - */ - public Map getResponseAttributes() { - return getResponse() == null ? null : getResponse().getAttributes(); - } - - /** - * Returns the response cache directives. Note that when used with HTTP - * connectors, this property maps to the "Cache-Control" header. - * - * @return The cache directives. - */ - public List getResponseCacheDirectives() { - return getResponse() == null ? null : getResponse().getCacheDirectives(); - } - - /** - * Returns the response entity representation. - * - * @return The response entity representation. - */ - public Representation getResponseEntity() { - return getResponse() == null ? null : getResponse().getEntity(); - } - - /** - * Returns the application root reference. - * - * @return The application root reference. - * @see Request#getRootRef() - */ - public Reference getRootRef() { - return getRequest() == null ? null : getRequest().getRootRef(); - } - - /** - * Returns the server-specific information. Creates a new instance if no one has - * been set. - * - * @return The server-specific information. - * @see Response#getServerInfo() - */ - public ServerInfo getServerInfo() { - return getResponse() == null ? null : getResponse().getServerInfo(); - } - - /** - * Returns the status. - * - * @return The status. - * @see Response#getStatus() - */ - public Status getStatus() { - return getResponse() == null ? null : getResponse().getStatus(); - } - - /** - * Returns the application's status service or create a new one. - * - * @return The status service. - */ - public org.restlet.service.StatusService getStatusService() { - org.restlet.service.StatusService result = null; - - result = getApplication().getStatusService(); - - if (result == null) { - result = new org.restlet.service.StatusService(); - } - - return result; - } - - /** - * Handles the call composed of the current context, request and response. - * - * @return The optional response entity. - */ - public abstract Representation handle(); - - /** - * Initialization method setting the environment of the current resource - * instance. It the calls the {@link #doInit()} method that can be overridden. - * - * @param context The current context. - * @param request The handled request. - * @param response The handled response. - */ - public void init(Context context, Request request, Response response) { - this.context = context; - this.request = request; - this.response = response; - - try { - doInit(); - } catch (Throwable t) { - doCatch(t); - } - } - - /** - * Indicates if the message was or will be exchanged confidentially, for example - * via a SSL-secured connection. - * - * @return True if the message is confidential. - * @see Request#isConfidential() - */ - public boolean isConfidential() { - return getRequest() == null ? null : getRequest().isConfidential(); - } - - /** - * Indicates if the call is loggable - * - * @return True if the call is loggable - */ - public boolean isLoggable() { - return getRequest() == null ? null : getRequest().isLoggable(); - } - - /** - * Releases the resource by calling {@link #doRelease()}. - */ - public final void release() { - try { - doRelease(); - } catch (Throwable t) { - doCatch(t); - } - } - - /** - * Sets the parent application. - * - * @param application The parent application. - */ - public void setApplication(org.restlet.Application application) { - this.application = application; - } - - /** - * Sets the request or response attribute value. - * - * @param name The attribute name. - * @param value The attribute to set. - */ - public abstract void setAttribute(String name, Object value); - - /** - * Sets the query value for the named parameter. If no query is defined, it - * creates one. If the same parameter exists, it replaces it altogether. - * - * @param name The query parameter name. - * @param value The query parameter value. - */ - public void setQueryValue(String name, String value) { - Form query = getQuery(); - - if (query == null) { - query = new Form(); - } - - query.set(name, value); - - try { - getReference().setQuery(query.encode()); - } catch (IOException e) { - getLogger().fine("Unable to set the query value"); - } - } - - /** - * Sets the handled request. - * - * @param request The handled request. - */ - public void setRequest(Request request) { - this.request = request; - } - - /** - * Sets the handled response. - * - * @param response The handled response. - */ - public void setResponse(Response response) { - this.response = response; - } - - /** - * Converts a representation into a Java object. Leverages the - * {@link org.restlet.service.ConverterService}. - * - * @param The expected class of the Java object. - * @param source The source representation to convert. - * @param target The target class of the Java object. - * @return The converted Java object. - * @throws ResourceException - */ - public T toObject(Representation source, Class target) throws ResourceException { - T result = null; - - if (source != null) { - try { - org.restlet.service.ConverterService cs = getConverterService(); - result = cs.toObject(source, target, this); - } catch (ResourceException e) { - throw e; - } catch (Exception e) { - throw new ResourceException(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE, e); - } - } - - return result; - } - - /** - * Converts an object into a representation based on the default converter - * service variant. - * - * @param source The object to convert. - * @return The wrapper representation. - * @throws IOException - */ - public Representation toRepresentation(Object source) throws IOException { - return toRepresentation(source, (Variant) null); - } - - /** - * Converts an object into a representation based on a given media type. - * - * @param source The object to convert. - * @param target The target representation media type. - * @return The wrapper representation. - * @throws IOException - */ - public Representation toRepresentation(Object source, MediaType target) throws IOException { - return toRepresentation(source, new Variant(target)); - } - - /** - * Converts an object into a representation based on client preferences. - * - * @param source The object to convert. - * @param target The target representation variant. - * @return The wrapper representation. - * @throws IOException - */ - public Representation toRepresentation(Object source, Variant target) throws IOException { - Representation result = null; - - if (source != null) { - org.restlet.service.ConverterService cs = getConverterService(); - result = cs.toRepresentation(source, target, this); - } - - return result; - } - - @Override - public String toString() { - return (getRequest() == null ? "" : getRequest().toString()) - + (getResponse() == null ? "" : " => " + getResponse().toString()); - } + /** + * Converts the given {@link String} value into a {@link Boolean} or null. + * + * @param value The value to convert or null. + * @return The converted {@link Boolean} value or null. + */ + public static Boolean toBoolean(String value) { + return (value != null) ? Boolean.valueOf(value) : null; + } + + /** + * Converts the given {@link String} value into a {@link Byte} or null. + * + * @param value The value to convert or null. + * @return The converted {@link Byte} value or null. + */ + public static Byte toByte(String value) { + return (value != null) ? Byte.valueOf(value) : null; + } + + /** + * Converts the given {@link String} value into an {@link Double} or null. + * + * @param value The value to convert or null. + * @return The converted {@link Double} value or null. + */ + public static Double toDouble(String value) { + return (value != null) ? Double.valueOf(value) : null; + } + + /** + * Converts the given {@link String} value into a {@link Float} or null. + * + * @param value The value to convert or null. + * @return The converted {@link Float} value or null. + */ + public static Float toFloat(String value) { + return (value != null) ? Float.valueOf(value) : null; + } + + /** + * Converts the given {@link String} value into an {@link Integer} or null. + * + * @param value The value to convert or null. + * @return The converted {@link Integer} value or null. + */ + public static Integer toInteger(String value) { + return (value != null) ? Integer.valueOf(value) : null; + } + + /** + * Converts the given {@link String} value into an {@link Long} or null. + * + * @param value The value to convert or null. + * @return The converted {@link Long} value or null. + */ + public static Long toLong(String value) { + return (value != null) ? Long.valueOf(value) : null; + } + + /** + * Converts the given {@link String} value into a {@link Short} or null. + * + * @param value The value to convert or null. + * @return The converted {@link Short} value or null. + */ + public static Short toShort(String value) { + return (value != null) ? Short.valueOf(value) : null; + } + + /** The parent application. */ + private volatile org.restlet.Application application; + + /** The parent context. */ + private volatile Context context; + + /** The handled request. */ + private volatile Request request; + + /** The handled response. */ + private volatile Response response; + + /** + * Invoked when a {@link Throwable} is caught during initialization, handling, or releasing. + * + * @param throwable The caught error or exception. + */ + protected void doCatch(Throwable throwable) { + getLogger().log(Level.INFO, "Exception or error caught in resource", throwable); + } + + /** + * Invoked when an error response status is received. + * + * @param errorStatus The error status received. + */ + protected void doError(Status errorStatus) {} + + /** + * Invoked when an error response status is received. + * + * @param errorStatus The error status received. + * @param errorMessage The custom error message. + */ + protected final void doError(Status errorStatus, String errorMessage) { + doError(new Status(errorStatus, errorMessage)); + } + + /** + * Setup method that can be overridden to initialize the state of the resource. By default, it + * does nothing. + * + * @see #init(Context, Request, Response) + */ + protected void doInit() throws ResourceException {} + + /** + * Cleanup method that can be overridden to release the state of the resource. By default, it + * does nothing. + * + * @see #release() + */ + protected void doRelease() throws ResourceException {} + + /** + * Returns the set of methods allowed for the current client by the resource. The result can + * vary based on the client's user agent, authentication and authorization data provided by the + * client. + * + * @return The set of allowed methods. + */ + public Set getAllowedMethods() { + return getResponse() == null ? null : getResponse().getAllowedMethods(); + } + + /** + * Returns the parent application. If it wasn't set, it attempts to retrieve the current one via + * {@link org.restlet.Application#getCurrent()} if it exists, or instantiates a new one as a + * last resort. + * + * @return The parent application if it exists, or a new one. + */ + public org.restlet.Application getApplication() { + org.restlet.Application result = this.application; + + if (result == null) { + result = org.restlet.Application.getCurrent(); + + if (result == null) { + result = new org.restlet.Application(getContext()); + } + + this.application = result; + } + + return result; + } + + /** + * Returns the attribute value by looking up the given name in the request or response + * attributes maps. This is typically used for variables that are declared in the URI template + * used to route the call to this resource. + * + * @param name The attribute name. + * @return The matching request or response attribute value. + */ + public abstract String getAttribute(String name); + + /** + * Returns the list of authentication requests sent by an origin server to a client. If none is + * available, an empty list is returned. + * + * @return The list of authentication requests. + * @see Response#getChallengeRequests() + */ + public List getChallengeRequests() { + return getResponse() == null ? null : getResponse().getChallengeRequests(); + } + + /** + * Returns the authentication response sent by a client to an origin server. + * + * @return The authentication response sent by a client to an origin server. + * @see Request#getChallengeResponse() + */ + public ChallengeResponse getChallengeResponse() { + return getRequest() == null ? null : getRequest().getChallengeResponse(); + } + + /** + * Returns the client-specific information. Creates a new instance if no one has been set. + * + * @return The client-specific information. + * @see Request#getClientInfo() + */ + public ClientInfo getClientInfo() { + return getRequest() == null ? null : getRequest().getClientInfo(); + } + + /** + * Returns the modifiable conditions applying to this request. Creates a new instance if no one + * has been set. + * + * @return The conditions applying to this call. + * @see Request#getConditions() + */ + public Conditions getConditions() { + return getRequest() == null ? null : getRequest().getConditions(); + } + + /** + * Returns the application's content negotiation service or create a new one. + * + * @return The content negotiation service. + */ + public org.restlet.service.ConnegService getConnegService() { + org.restlet.service.ConnegService result = null; + + result = getApplication().getConnegService(); + + if (result == null) { + result = new org.restlet.service.ConnegService(); + } + + return result; + } + + /** + * Returns the current context. + * + * @return The current context. + */ + public Context getContext() { + return context; + } + + /** + * Returns the application's converter service or create a new one. + * + * @return The converter service. + */ + public org.restlet.service.ConverterService getConverterService() { + org.restlet.service.ConverterService result = null; + + result = getApplication().getConverterService(); + + if (result == null) { + result = new org.restlet.service.ConverterService(); + } + + return result; + } + + /** + * Returns the modifiable series of cookies provided by the client. Creates a new instance if no + * one has been set. + * + * @return The cookies provided by the client. + * @see Request#getCookies() + */ + public Series getCookies() { + return getRequest() == null ? null : getRequest().getCookies(); + } + + /** + * Returns the modifiable series of cookie settings provided by the server. Creates a new + * instance if no one has been set. + * + * @return The cookie settings provided by the server. + * @see Response#getCookieSettings() + */ + public Series getCookieSettings() { + return getResponse() == null ? null : getResponse().getCookieSettings(); + } + + /** + * Returns the modifiable set of selecting dimensions on which the response entity may vary. If + * some server-side content negotiation is done, this set should be properly updated, other it + * can be left empty. Creates a new instance if no one has been set. + * + * @return The set of dimensions on which the response entity may vary. + * @see Response#getDimensions() + */ + public Set getDimensions() { + return getResponse() == null ? null : getResponse().getDimensions(); + } + + /** + * Returns the host reference. This may be different from the resourceRef's host, for example + * for URNs and other URIs that don't contain host information. + * + * @return The host reference. + * @see Request#getHostRef() + */ + public Reference getHostRef() { + return getRequest() == null ? null : getRequest().getHostRef(); + } + + /** + * Returns the reference that the client should follow for redirections or resource creations. + * + * @return The redirection reference. + * @see Response#getLocationRef() + */ + public Reference getLocationRef() { + return getResponse() == null ? null : getResponse().getLocationRef(); + } + + /** + * Returns the logger. + * + * @return The logger. + */ + public Logger getLogger() { + return getContext() != null ? getContext().getLogger() : Context.getCurrentLogger(); + } + + /** + * Returns the resource reference's optional matrix. + * + * @return The resource reference's optional matrix. + * @see Reference#getMatrixAsForm() + */ + public Form getMatrix() { + return getReference() == null ? null : getReference().getMatrixAsForm(); + } + + /** + * Returns the first value of the matrix parameter given its name if existing, or null. + * + * @param name The matrix parameter name. + * @return The first value of the matrix parameter. + */ + public String getMatrixValue(String name) { + String result = null; + Form matrix = getMatrix(); + + if (matrix != null) { + result = matrix.getFirstValue(name); + } + + return result; + } + + /** + * Returns the maximum number of intermediaries. + * + * @return The maximum number of intermediaries. + */ + public int getMaxForwards() { + return getRequest() == null ? 0 : getRequest().getMaxForwards(); + } + + /** + * Returns the application's metadata service or create a new one. + * + * @return The metadata service. + */ + public MetadataService getMetadataService() { + MetadataService result = null; + + result = getApplication().getMetadataService(); + + if (result == null) { + result = new MetadataService(); + } + + return result; + } + + /** + * Returns the method. + * + * @return The method. + * @see Request#getMethod() + */ + public Method getMethod() { + return getRequest() == null ? null : getRequest().getMethod(); + } + + /** + * Returns the original reference as requested by the client. Note that this property is not + * used during request routing. + * + * @return The original reference. + * @see Request#getOriginalRef() + */ + public Reference getOriginalRef() { + return getRequest() == null ? null : getRequest().getOriginalRef(); + } + + /** + * Returns the protocol by first returning the resourceRef.schemeProtocol property if it is set, + * or the baseRef.schemeProtocol property otherwise. + * + * @return The protocol or null if not available. + * @see Request#getProtocol() + */ + public Protocol getProtocol() { + return getRequest() == null ? null : getRequest().getProtocol(); + } + + /** + * Returns the list of proxy authentication requests sent by an origin server to a client. If + * none is available, an empty list is returned. + * + * @return The list of proxy authentication requests. + * @see Response#getProxyChallengeRequests() + */ + public List getProxyChallengeRequests() { + return getResponse() == null ? null : getResponse().getProxyChallengeRequests(); + } + + /** + * Returns the proxy authentication response sent by a client to an origin server. + * + * @return The proxy authentication response sent by a client to an origin server. + * @see Request#getProxyChallengeResponse() + */ + public ChallengeResponse getProxyChallengeResponse() { + return getRequest() == null ? null : getRequest().getProxyChallengeResponse(); + } + + /** + * Returns the resource reference's optional query. Note that modifications to the returned + * {@link Form} object aren't reported to the underlying reference. + * + * @return The resource reference's optional query. + * @see Reference#getQueryAsForm() + */ + public Form getQuery() { + return getReference() == null ? null : getReference().getQueryAsForm(); + } + + /** + * Returns the first value of the query parameter given its name if existing, or null. + * + * @param name The query parameter name. + * @return The first value of the query parameter. + */ + public String getQueryValue(String name) { + String result = null; + Form query = getQuery(); + + if (query != null) { + result = query.getFirstValue(name); + } + + return result; + } + + /** + * Returns the ranges to return from the target resource's representation. + * + * @return The ranges to return. + * @see Request#getRanges() + */ + public List getRanges() { + return getRequest() == null ? null : getRequest().getRanges(); + } + + /** + * Returns the URI reference. + * + * @return The URI reference. + */ + public Reference getReference() { + return getRequest() == null ? null : getRequest().getResourceRef(); + } + + /** + * Returns the referrer reference if available. + * + * @return The referrer reference. + */ + public Reference getReferrerRef() { + return getRequest() == null ? null : getRequest().getReferrerRef(); + } + + /** + * Returns the handled request. + * + * @return The handled request. + */ + public Request getRequest() { + return request; + } + + /** + * Returns the request attributes. + * + * @return The request attributes. + * @see Request#getAttributes() + */ + public Map getRequestAttributes() { + return getRequest() == null ? null : getRequest().getAttributes(); + } + + /** + * Returns the request cache directives. Note that when used with HTTP connectors, this property + * maps to the "Cache-Control" header. + * + * @return The cache directives. + */ + public List getRequestCacheDirectives() { + return getRequest() == null ? null : getRequest().getCacheDirectives(); + } + + /** + * Returns the request entity representation. + * + * @return The request entity representation. + */ + public Representation getRequestEntity() { + return getRequest() == null ? null : getRequest().getEntity(); + } + + /** + * Returns the handled response. + * + * @return The handled response. + */ + public Response getResponse() { + return response; + } + + /** + * Returns the response attributes. + * + * @return The response attributes. + * @see Response#getAttributes() + */ + public Map getResponseAttributes() { + return getResponse() == null ? null : getResponse().getAttributes(); + } + + /** + * Returns the response cache directives. Note that when used with HTTP connectors, this + * property maps to the "Cache-Control" header. + * + * @return The cache directives. + */ + public List getResponseCacheDirectives() { + return getResponse() == null ? null : getResponse().getCacheDirectives(); + } + + /** + * Returns the response entity representation. + * + * @return The response entity representation. + */ + public Representation getResponseEntity() { + return getResponse() == null ? null : getResponse().getEntity(); + } + + /** + * Returns the application root reference. + * + * @return The application root reference. + * @see Request#getRootRef() + */ + public Reference getRootRef() { + return getRequest() == null ? null : getRequest().getRootRef(); + } + + /** + * Returns the server-specific information. Creates a new instance if no one has been set. + * + * @return The server-specific information. + * @see Response#getServerInfo() + */ + public ServerInfo getServerInfo() { + return getResponse() == null ? null : getResponse().getServerInfo(); + } + + /** + * Returns the status. + * + * @return The status. + * @see Response#getStatus() + */ + public Status getStatus() { + return getResponse() == null ? null : getResponse().getStatus(); + } + + /** + * Returns the application's status service or create a new one. + * + * @return The status service. + */ + public org.restlet.service.StatusService getStatusService() { + org.restlet.service.StatusService result = null; + + result = getApplication().getStatusService(); + + if (result == null) { + result = new org.restlet.service.StatusService(); + } + + return result; + } + + /** + * Handles the call composed of the current context, request, and response. + * + * @return The optional response entity. + */ + public abstract Representation handle(); + + /** + * Initialization method setting the environment of the current resource instance. It the calls + * the {@link #doInit()} method that can be overridden. + * + * @param context The current context. + * @param request The handled request. + * @param response The handled response. + */ + public void init(Context context, Request request, Response response) { + this.context = context; + this.request = request; + this.response = response; + + try { + doInit(); + } catch (Throwable t) { + doCatch(t); + } + } + + /** + * Indicates if the message was or will be exchanged confidentially, for example, via an + * SSL-secured connection. + * + * @return True if the message is confidential. + * @see Request#isConfidential() + */ + public boolean isConfidential() { + return getRequest() == null ? null : getRequest().isConfidential(); + } + + /** + * Indicates if the call is loggable + * + * @return True if the call is loggable + */ + public boolean isLoggable() { + return getRequest() == null ? null : getRequest().isLoggable(); + } + + /** Releases the resource by calling {@link #doRelease()}. */ + public final void release() { + try { + doRelease(); + } catch (Throwable t) { + doCatch(t); + } + } + + /** + * Sets the parent application. + * + * @param application The parent application. + */ + public void setApplication(org.restlet.Application application) { + this.application = application; + } + + /** + * Sets the request or response attribute value. + * + * @param name The attribute name. + * @param value The attribute to set. + */ + public abstract void setAttribute(String name, Object value); + + /** + * Sets the query value for the named parameter. If no query is defined, it creates one. If the + * same parameter exists, it replaces it altogether. + * + * @param name The query parameter name. + * @param value The query parameter value. + */ + public void setQueryValue(String name, String value) { + Form query = getQuery(); + + if (query == null) { + query = new Form(); + } + + query.set(name, value); + + try { + getReference().setQuery(query.encode()); + } catch (IOException e) { + getLogger().fine("Unable to set the query value"); + } + } + + /** + * Sets the handled request. + * + * @param request The handled request. + */ + public void setRequest(Request request) { + this.request = request; + } + + /** + * Sets the handled response. + * + * @param response The handled response. + */ + public void setResponse(Response response) { + this.response = response; + } + + /** + * Converts a representation into a Java object. Leverages the {@link + * org.restlet.service.ConverterService}. + * + * @param The expected class of the Java object. + * @param source The source representation to convert. + * @param target The target class of the Java object. + * @return The converted Java object. + * @throws ResourceException + */ + public T toObject(Representation source, Class target) throws ResourceException { + T result = null; + + if (source != null) { + try { + org.restlet.service.ConverterService cs = getConverterService(); + result = cs.toObject(source, target, this); + } catch (ResourceException e) { + throw e; + } catch (Exception e) { + throw new ResourceException(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE, e); + } + } + + return result; + } + + /** + * Converts an object into a representation based on the default converter service variant. + * + * @param source The object to convert. + * @return The wrapper representation. + * @throws IOException + */ + public Representation toRepresentation(Object source) throws IOException { + return toRepresentation(source, (Variant) null); + } + + /** + * Converts an object into a representation based on a given media type. + * + * @param source The object to convert. + * @param target The target representation media type. + * @return The wrapper representation. + * @throws IOException + */ + public Representation toRepresentation(Object source, MediaType target) throws IOException { + return toRepresentation(source, new Variant(target)); + } + + /** + * Converts an object into a representation based on client preferences. + * + * @param source The object to convert. + * @param target The target representation variant. + * @return The wrapper representation. + * @throws IOException + */ + public Representation toRepresentation(Object source, Variant target) throws IOException { + Representation result = null; + + if (source != null) { + org.restlet.service.ConverterService cs = getConverterService(); + result = cs.toRepresentation(source, target, this); + } + + return result; + } + + @Override + public String toString() { + return (getRequest() == null ? "" : getRequest().toString()) + + (getResponse() == null ? "" : " => " + getResponse().toString()); + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/ResourceException.java b/org.restlet/src/main/java/org/restlet/resource/ResourceException.java index f8f5fc54cd..3ee8f8ada3 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ResourceException.java +++ b/org.restlet/src/main/java/org/restlet/resource/ResourceException.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import org.restlet.Request; @@ -15,226 +14,228 @@ /** * Encapsulates a response status and the optional cause as a checked exception. - * + * * @author Jerome Louvel */ public class ResourceException extends RuntimeException { - private static final long serialVersionUID = 1L; - - /** The status associated to this exception. */ - private transient Status status; - - /** The request associated to this exception. Could be null. */ - private transient Request request; - - /** The response associated to this exception. Could be null. */ - private transient Response response; - - /** - * Constructor. - * - * @param code The specification code of the encapsulated status. - */ - public ResourceException(int code) { - this(new Status(code)); - } - - /** - * Constructor. - * - * @param code The specification code of the encapsulated status. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - */ - public ResourceException(int code, String reasonPhrase) { - this(new Status(code, reasonPhrase)); - } - - /** - * Constructor. - * - * @param code The specification code of the encapsulated status. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The description of the encapsulated status. - */ - public ResourceException(int code, String reasonPhrase, String description) { - this(new Status(code, reasonPhrase, description)); - } - - /** - * Constructor. - * - * @param code The specification code of the encapsulated status. - * @param name The name of the encapsulated status. - * @param description The description of the encapsulated status. - * @param uri The URI of the specification describing the method. - */ - public ResourceException(int code, String name, String description, String uri) { - this(new Status(code, name, description, uri)); - } - - /** - * Constructor. - * - * @param code The specification code of the encapsulated status. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The description of the encapsulated status. - * @param uri The URI of the specification describing the method. - * @param cause The wrapped cause error or exception. - */ - public ResourceException(int code, String reasonPhrase, String description, String uri, Throwable cause) { - this(new Status(code, cause, reasonPhrase, description, uri), cause); - } - - /** - * Constructor. - * - * @param code The specification code of the encapsulated status. - * @param cause The wrapped cause error or exception. - */ - public ResourceException(int code, Throwable cause) { - this(new Status(code, cause), cause); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - */ - public ResourceException(int code, Throwable throwable, String reasonPhrase) { - this(new Status(code, throwable, reasonPhrase, null, null)); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The longer description. - */ - public ResourceException(int code, Throwable throwable, String reasonPhrase, String description) { - this(new Status(code, throwable, reasonPhrase, description, null)); - } - - /** - * Constructor. - * - * @param code The specification code. - * @param throwable The related error or exception. - * @param reasonPhrase The short reason phrase displayed next to the status code - * in a HTTP response. - * @param description The longer description. - * @param uri The URI of the specification describing the method. - */ - public ResourceException(int code, Throwable throwable, String reasonPhrase, String description, String uri) { - this(new Status(code, throwable, reasonPhrase, description, uri)); - } - - /** - * Constructor. - * - * @param status The status to associate. - */ - public ResourceException(Status status) { - this(status, (status == null) ? null : status.getThrowable()); - } - - /** - * Constructor. - * - * @param status The status to associate. - */ - public ResourceException(Status status, Request request, Response response) { - this(status, (status == null) ? null : status.getThrowable(), request, response); - } - - /** - * Constructor. - * - * @param status The status to copy. - * @param description The description of the encapsulated status. - */ - public ResourceException(Status status, String description) { - this(new Status(status, description)); - } - - /** - * Constructor. - * - * @param status The status to copy. - * @param description The description of the encapsulated status. - * @param cause The wrapped cause error or exception. - */ - public ResourceException(Status status, String description, Throwable cause) { - this(new Status(status, cause, null, description), cause); - } - - /** - * Constructor. - * - * @param status The status to associate. - * @param cause The wrapped cause error or exception. - */ - public ResourceException(Status status, Throwable cause) { - this(status, cause, null, null); - } - - /** - * Constructor. - * - * @param status The status to associate. - * @param cause The wrapped cause error or exception. - */ - public ResourceException(Status status, Throwable cause, Request request, Response response) { - super((status == null) ? null : status.toString(), cause); - this.status = status; - this.request = request; - this.response = response; - } - - /** - * Constructor that set the status to - * {@link org.restlet.data.Status#SERVER_ERROR_INTERNAL} including the related - * error or exception. - * - * @param cause The wrapped cause error or exception. - */ - public ResourceException(Throwable cause) { - this(new Status(Status.SERVER_ERROR_INTERNAL, cause), cause); - } - - /** - * Returns the request associated to this exception. - * - * @return The request associated to this exception. - */ - public Request getRequest() { - return this.request; - } - - /** - * Returns the response associated to this exception. - * - * @return The response associated to this exception. - */ - public Response getResponse() { - return this.response; - } - - /** - * Returns the status associated to this exception. - * - * @return The status associated to this exception. - */ - public Status getStatus() { - return this.status; - } + private static final long serialVersionUID = 1L; + + /** The status associated with this exception. */ + private transient Status status; + + /** The request associated with this exception. Could be null. */ + private transient Request request; + + /** The response associated with this exception. Could be null. */ + private transient Response response; + + /** + * Constructor. + * + * @param code The specification code of the encapsulated status. + */ + public ResourceException(int code) { + this(new Status(code)); + } + + /** + * Constructor. + * + * @param code The specification code of the encapsulated status. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + */ + public ResourceException(int code, String reasonPhrase) { + this(new Status(code, reasonPhrase)); + } + + /** + * Constructor. + * + * @param code The specification code of the encapsulated status. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The description of the encapsulated status. + */ + public ResourceException(int code, String reasonPhrase, String description) { + this(new Status(code, reasonPhrase, description)); + } + + /** + * Constructor. + * + * @param code The specification code of the encapsulated status. + * @param name The name of the encapsulated status. + * @param description The description of the encapsulated status. + * @param uri The URI of the specification describing the method. + */ + public ResourceException(int code, String name, String description, String uri) { + this(new Status(code, name, description, uri)); + } + + /** + * Constructor. + * + * @param code The specification code of the encapsulated status. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The description of the encapsulated status. + * @param uri The URI of the specification describing the method. + * @param cause The wrapped cause error or exception. + */ + public ResourceException( + int code, String reasonPhrase, String description, String uri, Throwable cause) { + this(new Status(code, cause, reasonPhrase, description, uri), cause); + } + + /** + * Constructor. + * + * @param code The specification code of the encapsulated status. + * @param cause The wrapped cause error or exception. + */ + public ResourceException(int code, Throwable cause) { + this(new Status(code, cause), cause); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + */ + public ResourceException(int code, Throwable throwable, String reasonPhrase) { + this(new Status(code, throwable, reasonPhrase, null, null)); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The longer description. + */ + public ResourceException( + int code, Throwable throwable, String reasonPhrase, String description) { + this(new Status(code, throwable, reasonPhrase, description, null)); + } + + /** + * Constructor. + * + * @param code The specification code. + * @param throwable The related error or exception. + * @param reasonPhrase The short reason phrase displayed next to the status code in an HTTP + * response. + * @param description The longer description. + * @param uri The URI of the specification describing the method. + */ + public ResourceException( + int code, Throwable throwable, String reasonPhrase, String description, String uri) { + this(new Status(code, throwable, reasonPhrase, description, uri)); + } + + /** + * Constructor. + * + * @param status The status to associate. + */ + public ResourceException(Status status) { + this(status, (status == null) ? null : status.getThrowable()); + } + + /** + * Constructor. + * + * @param status The status to associate. + */ + public ResourceException(Status status, Request request, Response response) { + this(status, (status == null) ? null : status.getThrowable(), request, response); + } + + /** + * Constructor. + * + * @param status The status to copy. + * @param description The description of the encapsulated status. + */ + public ResourceException(Status status, String description) { + this(new Status(status, description)); + } + + /** + * Constructor. + * + * @param status The status to copy. + * @param description The description of the encapsulated status. + * @param cause The wrapped cause error or exception. + */ + public ResourceException(Status status, String description, Throwable cause) { + this(new Status(status, cause, null, description), cause); + } + + /** + * Constructor. + * + * @param status The status to associate. + * @param cause The wrapped cause error or exception. + */ + public ResourceException(Status status, Throwable cause) { + this(status, cause, null, null); + } + + /** + * Constructor. + * + * @param status The status to associate. + * @param cause The wrapped cause error or exception. + */ + public ResourceException(Status status, Throwable cause, Request request, Response response) { + super((status == null) ? null : status.toString(), cause); + this.status = status; + this.request = request; + this.response = response; + } + + /** + * Constructor that set the status to {@link org.restlet.data.Status#SERVER_ERROR_INTERNAL} + * including the related error or exception. + * + * @param cause The wrapped cause error or exception. + */ + public ResourceException(Throwable cause) { + this(new Status(Status.SERVER_ERROR_INTERNAL, cause), cause); + } + + /** + * Returns the request associated with this exception. + * + * @return The request associated with this exception. + */ + public Request getRequest() { + return this.request; + } + + /** + * Returns the response associated with this exception. + * + * @return The response associated with this exception. + */ + public Response getResponse() { + return this.response; + } + + /** + * Returns the status associated with this exception. + * + * @return The status associated with this exception. + */ + public Status getStatus() { + return this.status; + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/Result.java b/org.restlet/src/main/java/org/restlet/resource/Result.java index 43ab57d88e..8c92755707 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Result.java +++ b/org.restlet/src/main/java/org/restlet/resource/Result.java @@ -1,34 +1,32 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Callback interface for asynchronous tasks. - * + * * @param The class of the result object returned in case of success. * @author Jerome Louvel */ public interface Result { - /** - * Method called back by the associated object when a failure is detected. - * - * @param caught The exception or error caught. - */ - void onFailure(Throwable caught); - - /** - * Method called back by the associated object in case of success. - * - * @param result The result object. - */ - void onSuccess(T result); + /** + * Method called back by the associated object when a failure is detected. + * + * @param caught The exception or error caught. + */ + void onFailure(Throwable caught); + /** + * Method called back by the associated object in case of success. + * + * @param result The result object. + */ + void onSuccess(T result); } diff --git a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java index 6b822a8c8a..3d1b48e363 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java @@ -1,17 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; -import org.restlet.*; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.Uniform; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.CookieSetting; +import org.restlet.data.Dimension; +import org.restlet.data.Form; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Reference; +import org.restlet.data.ServerInfo; import org.restlet.data.Status; -import org.restlet.data.*; import org.restlet.engine.resource.AnnotationInfo; import org.restlet.engine.resource.AnnotationUtils; import org.restlet.engine.resource.MethodAnnotationInfo; @@ -25,1685 +43,1648 @@ import org.restlet.service.ConverterService; import org.restlet.util.Series; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.logging.Level; - /** - * Base class for server-side resources. It acts as a wrapper to a given call, - * including the incoming {@link Request} and the outgoing {@link Response}. + * Base class for server-side resources. It acts as a wrapper to a given call, including the + * incoming {@link Request} and the outgoing {@link Response}.
*
+ * It's life cycle is managed by a {@link Finder} created either explicitly or more likely + * implicitly when your {@link ServerResource} subclass is attached to a {@link Filter} or a {@link + * Router} via the {@link Filter#setNext(Class)} or {@link Router#attach(String, Class)} methods for + * example. After instantiation using the default constructor, the final {@link #init(Context, + * Request, Response)} method is invoked, setting the context, request, and response. You can + * intercept this by overriding the {@link #doInit()} method. Then, if the response status is still + * a success, the {@link #handle()} method is invoked to actually handle the call. Finally, the + * final {@link #release()} method is invoked to do the necessary cleanup, which you can intercept + * by overriding the {@link #doRelease()} method. During this life cycle, if any exception is + * caught, then the {@link #doCatch(Throwable)} method is invoked.
*
- * It's life cycle is managed by a {@link Finder} created either explicitly or - * more likely implicitly when your {@link ServerResource} subclass is attached - * to a {@link Filter} or a {@link Router} via the {@link Filter#setNext(Class)} - * or {@link Router#attach(String, Class)} methods for example. After - * instantiation using the default constructor, the final - * {@link #init(Context, Request, Response)} method is invoked, setting the - * context, request and response. You can intercept this by overriding the - * {@link #doInit()} method. Then, if the response status is still a success, - * the {@link #handle()} method is invoked to actually handle the call. Finally, - * the final {@link #release()} method is invoked to do the necessary clean-up, - * which you can intercept by overriding the {@link #doRelease()} method. During - * this life cycle, if any exception is caught, then the - * {@link #doCatch(Throwable)} method is invoked.
+ * Note that when an annotated method manually sets the response entity, if this entity is + * available, then it will be preserved and the result of the annotated method ignored.
*
- * Note that when an annotated method manually sets the response entity, if this - * entity is available then it will be preserved and the result of the annotated - * method ignored.
+ * In addition, there are two ways to declare representation variants, one is based on the {@link + * #getVariants()} method and another one on the annotated methods. Both approaches can't, however, + * be used at the same time for now.
*
- * In addition, there are two ways to declare representation variants, one is - * based on the {@link #getVariants()} method and another one on the annotated - * methods. Both approaches can't however be used at the same time for now.
- *
- * Concurrency note: contrary to the {@link org.restlet.Uniform} class and its - * main {@link Restlet} subclass where a single instance can handle several - * calls concurrently, one instance of {@link ServerResource} is created for - * each call handled and accessed by only one thread at a time. - * + * Concurrency note: contrary to the {@link org.restlet.Uniform} class and its main {@link Restlet} + * subclass where a single instance can handle several calls concurrently, one instance of {@link + * ServerResource} is created for each call handled and accessed by only one thread at a time. + * * @author Jerome Louvel */ public abstract class ServerResource extends Resource { - /** Indicates if annotations are supported. */ - private volatile boolean annotated; - - /** Indicates if conditional handling is enabled. */ - private volatile boolean conditional; - - /** The description. */ - private volatile String description; - - /** Indicates if the identified resource exists. */ - private volatile boolean existing; - - /** The display name. */ - private volatile String name; - - /** Indicates if content negotiation of response entities is enabled. */ - private volatile boolean negotiated; - - /** Modifiable list of variants. */ - private volatile List variants; - - /** - * Initializer block to ensure that the basic properties are initialized - * consistently across constructors. - */ - { - this.annotated = true; - this.conditional = true; - this.existing = true; - this.negotiated = true; - this.variants = null; - } - - /** - * Default constructor. Note that the - * {@link #init(Context, Request, Response)}() method will be invoked right - * after the creation of the resource. - */ - public ServerResource() { - } - - /** - * Ask the connector to abort the related network connection, for example - * immediately closing the socket. - */ - public void abort() { - getResponse().abort(); - } - - /** - * Asks the response to immediately commit making it ready to be sent back to - * the client. Note that all server connectors don't necessarily support this - * feature. - */ - public void commit() { - getResponse().commit(); - } - - /** - * Deletes the resource and all its representations. This method is only invoked - * if content negotiation has been disabled as indicated by the - * {@link #isNegotiated()}, otherwise the {@link #delete(Variant)} method is - * invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. - * - * @return The optional response entity. - * @throws ResourceException - * @see HTTP DELETE method - */ - protected Representation delete() throws ResourceException { - Representation result = null; - MethodAnnotationInfo annotationInfo; - - try { - annotationInfo = getAnnotation(Method.DELETE); - - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); - } - - return result; - } - - /** - * Deletes the resource and all its representations. A variant parameter is - * passed to indicate which representation should be returned if any.
- *
- * This method is only invoked if content negotiation has been enabled as - * indicated by the {@link #isNegotiated()}, otherwise the {@link #delete()} - * method is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. - * - * @param variant The variant of the response entity. - * @return The optional response entity. - * @throws ResourceException - * @see #get(Variant) - * @see HTTP DELETE method - */ - protected Representation delete(Variant variant) throws ResourceException { - Representation result = null; - - if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - - return result; - } - - /** - * Describes the available variants to help client-side content negotiation. - * Return null by default. - * - * @return The description of available variants. - */ - protected Representation describeVariants() { - Representation result = null; - - // The list of all variants is transmitted to the client - // final ReferenceList refs = new ReferenceList(variants.size()); - // for (final Variant variant : variants) { - // if (variant.getIdentifier() != null) { - // refs.add(variant.getIdentifier()); - // } - // } - // - // result = refs.getTextRepresentation(); - return result; - } - - /** - * Invoked when an error or an exception is caught during initialization, - * handling or releasing. By default, updates the responses's status with the - * result of - * {@link org.restlet.service.StatusService#toStatus(Throwable, Resource)}. - * - * @param throwable The caught error or exception. - */ - protected void doCatch(Throwable throwable) { - Level level = Level.INFO; - Status status = getStatusService().toStatus(throwable, this); - - if (status.isServerError()) { - level = Level.SEVERE; - } else if (status.isConnectorError()) { - level = Level.INFO; - } else if (status.isClientError()) { - level = Level.FINE; - } - - getLogger().log(level, "Exception or error caught in server resource", throwable); - - if (getResponse() != null) { - getResponse().setStatus(status); - Representation errorEntity = getStatusService().toRepresentation(status, this); - getResponse().setEntity(errorEntity); - } - } - - /** - * Handles a call by first verifying the optional request conditions and - * continue the processing if possible. Note that in order to evaluate those - * conditions, {@link #getInfo()} or {@link #getInfo(Variant)} methods might be - * invoked. - * - * @return The response entity. - * @throws ResourceException - */ - protected Representation doConditionalHandle() throws ResourceException { - Representation result = null; - - if (getConditions().hasSome()) { - RepresentationInfo resultInfo = null; - - if (existing) { - if (isNegotiated()) { - Variant preferredVariant = getPreferredVariant(getVariants(Method.GET)); - - if (preferredVariant == null && getConnegService().isStrict()) { - doError(Status.CLIENT_ERROR_NOT_ACCEPTABLE); - } else { - resultInfo = doGetInfo(preferredVariant); - } - } else { - resultInfo = doGetInfo(); - } - - if (resultInfo == null) { - if ((getStatus() == null) - || (getStatus().isSuccess() && !Status.SUCCESS_NO_CONTENT.equals(getStatus()))) { - doError(Status.CLIENT_ERROR_NOT_FOUND); - } else { - // Keep the current status as the developer might - // prefer a special status like 'method not authorized'. - } - } else { - Status status = getConditions().getStatus(getMethod(), resultInfo); - - if (status != null) { - if (status.isError()) { - doError(status); - } else { - setStatus(status); - } - } - } - } else { - Status status = getConditions().getStatus(getMethod(), resultInfo); - - if (status != null) { - if (status.isError()) { - doError(status); - } else { - setStatus(status); - } - } - } - - if ((Method.GET.equals(getMethod()) || Method.HEAD.equals(getMethod())) - && resultInfo instanceof Representation) { - result = (Representation) resultInfo; - } else if ((getStatus() != null) && getStatus().isSuccess()) { - // Conditions were passed successfully, continue the normal - // processing. - if (isNegotiated()) { - // Reset the list of variants, as the method differs. - getVariants().clear(); - result = doNegotiatedHandle(); - } else { - result = doHandle(); - } - } - } else { - if (isNegotiated()) { - result = doNegotiatedHandle(); - } else { - result = doHandle(); - } - } - - return result; - } - - /** - * By default, it sets the status on the response. - */ - @Override - protected void doError(Status errorStatus) { - setStatus(errorStatus); - } - - /** - * Returns a descriptor of the response entity returned by a {@link Method#GET} - * call. - * - * @return The response entity. - * @throws ResourceException - */ - private RepresentationInfo doGetInfo() throws ResourceException { - RepresentationInfo result = null; - MethodAnnotationInfo annotationInfo; - - try { - annotationInfo = getAnnotation(Method.GET); - - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - result = getInfo(); - } - } catch (IOException e) { - throw new ResourceException(e); - } - - return result; - } - - /** - * Returns a descriptor of the response entity returned by a negotiated - * {@link Method#GET} call. - * - * @param variant The selected variant descriptor. - * @return The response entity descriptor. - * @throws ResourceException - */ - private RepresentationInfo doGetInfo(Variant variant) throws ResourceException { - RepresentationInfo result = null; - - if (variant != null) { - if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else if (variant instanceof RepresentationInfo) { - result = (RepresentationInfo) variant; - } else { - result = getInfo(variant); - } - } else { - result = doGetInfo(); - } - - return result; - } - - /** - * Effectively handles a call without content negotiation of the response - * entity. The default behavior is to dispatch the call to one of the - * {@link #get()}, {@link #post(Representation)}, {@link #put(Representation)}, - * {@link #delete()}, {@link #head()} or {@link #options()} methods. - * - * @return The response entity. - * @throws ResourceException - */ - protected Representation doHandle() throws ResourceException { - Representation result = null; - Method method = getMethod(); - - if (method == null) { - setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "No method specified"); - } else { - if (method.equals(Method.PUT)) { - result = put(getRequestEntity()); - } else if (method.equals(Method.PATCH)) { - result = patch(getRequestEntity()); - } else if (isExisting()) { - if (method.equals(Method.GET)) { - result = get(); - } else if (method.equals(Method.POST)) { - result = post(getRequestEntity()); - } else if (method.equals(Method.DELETE)) { - result = delete(); - } else if (method.equals(Method.HEAD)) { - result = head(); - } else if (method.equals(Method.OPTIONS)) { - result = options(); - } else { - result = doHandle(method, getQuery(), getRequestEntity()); - } - } else { - doError(Status.CLIENT_ERROR_NOT_FOUND); - } - } - - return result; - } - - /** - * Effectively handles a call with content negotiation of the response entity - * using an annotated method. - * - * @param annotationInfo The annotation descriptor. - * @param variant The response variant expected (can be null). - * @return The response entity. - * @throws ResourceException - */ - protected Representation doHandle(MethodAnnotationInfo annotationInfo, Variant variant) throws ResourceException { - Representation result = null; - Class[] parameterTypes = annotationInfo.getJavaInputTypes(); - - // Invoke the annotated method and get the resulting object. - Object resultObject = null; - - try { - if (parameterTypes.length > 0) { - List parameters = new ArrayList(); - Object parameter = null; - - for (Class parameterType : parameterTypes) { - if (Variant.class.equals(parameterType)) { - parameters.add(variant); - } else { - if (getRequestEntity() != null && getRequestEntity().isAvailable() - && getRequestEntity().getSize() != 0) { - // Assume there is content to be read. - // NB: it does not handle the case where the size is - // unknown, but there is no content. - parameter = toObject(getRequestEntity(), parameterType); - - if (parameter == null) { - throw new ResourceException(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE); - } - } else { - parameter = null; - } - - parameters.add(parameter); - } - } - - resultObject = annotationInfo.getJavaMethod().invoke(this, parameters.toArray()); - } else { - resultObject = annotationInfo.getJavaMethod().invoke(this); - } - - if (resultObject != null) { - result = toRepresentation(resultObject, variant); - } - - } catch (IllegalArgumentException | IllegalAccessException | IOException e) { - throw new ResourceException(e); - } catch (InvocationTargetException e) { - if (e.getTargetException() instanceof ResourceException) { - throw (ResourceException) e.getTargetException(); - } - - throw new ResourceException(e.getTargetException()); - } + /** Indicates if annotations are supported. */ + private volatile boolean annotated; + + /** Indicates if conditional handling is enabled. */ + private volatile boolean conditional; + + /** The description. */ + private volatile String description; + + /** Indicates if the identified resource exists. */ + private volatile boolean existing; + + /** The display name. */ + private volatile String name; + + /** Indicates if content negotiation of response entities is enabled. */ + private volatile boolean negotiated; + + /** Modifiable list of variants. */ + private volatile List variants; + + /** + * Initializer block to ensure that the basic properties are initialized consistently across + * constructors. + */ + { + this.annotated = true; + this.conditional = true; + this.existing = true; + this.negotiated = true; + this.variants = null; + } + + /** + * Default constructor. Note that the {@link #init(Context, Request, Response)}() method will be + * invoked right after the creation of the resource. + */ + public ServerResource() {} + + /** + * Ask the connector to abort the related network connection, for example immediately closing + * the socket. + */ + public void abort() { + getResponse().abort(); + } + + /** + * Asks the response to immediately commit making it ready to be sent back to the client. Note + * that all server connectors don't necessarily support this feature. + */ + public void commit() { + getResponse().commit(); + } + + /** + * Deletes the resource and all its representations. This method is only invoked if content + * negotiation has been disabled as indicated by the {@link #isNegotiated()}, otherwise the + * {@link #delete(Variant)} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. + * + * @return The optional response entity. + * @throws ResourceException + * @see HTTP DELETE + * method + */ + protected Representation delete() throws ResourceException { + Representation result = null; + MethodAnnotationInfo annotationInfo; + + try { + annotationInfo = getAnnotation(Method.DELETE); + + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + } catch (IOException e) { + throw new ResourceException(e); + } + + return result; + } + + /** + * Deletes the resource and all its representations. A variant parameter is passed to indicate + * which representation should be returned, if any.
+ *
+ * This method is only invoked if content negotiation has been enabled as indicated by the + * {@link #isNegotiated()}, otherwise the {@link #delete()} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. + * + * @param variant The variant of the response entity. + * @return The optional response entity. + * @throws ResourceException + * @see #get(Variant) + * @see HTTP DELETE + * method + */ + protected Representation delete(Variant variant) throws ResourceException { + Representation result = null; + + if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + + return result; + } + + /** + * Describes the available variants to help client-side content negotiation. Return null by + * default. + * + * @return The description of available variants. + */ + protected Representation describeVariants() { + Representation result = null; + + // The list of all variants is transmitted to the client + // final ReferenceList refs = new ReferenceList(variants.size()); + // for (final Variant variant : variants) { + // if (variant.getIdentifier() != null) { + // refs.add(variant.getIdentifier()); + // } + // } + // + // result = refs.getTextRepresentation(); + return result; + } + + /** + * Invoked when an error or an exception is caught during initialization, handling, or + * releasing. By default, updates the response's status with the result of {@link + * org.restlet.service.StatusService#toStatus(Throwable, Resource)}. + * + * @param throwable The caught error or exception. + */ + protected void doCatch(Throwable throwable) { + Level level = Level.INFO; + Status status = getStatusService().toStatus(throwable, this); + + if (status.isServerError()) { + level = Level.SEVERE; + } else if (status.isConnectorError()) { + level = Level.INFO; + } else if (status.isClientError()) { + level = Level.FINE; + } + + getLogger().log(level, "Exception or error caught in server resource", throwable); + + if (getResponse() != null) { + getResponse().setStatus(status); + Representation errorEntity = getStatusService().toRepresentation(status, this); + getResponse().setEntity(errorEntity); + } + } + + /** + * Handles a call by first verifying the optional request conditions and continue the processing + * if possible. Note that to evaluate those conditions, {@link #getInfo()} or {@link + * #getInfo(Variant)} methods might be invoked. + * + * @return The response entity. + * @throws ResourceException + */ + protected Representation doConditionalHandle() throws ResourceException { + Representation result = null; + + if (getConditions().hasSome()) { + RepresentationInfo resultInfo = null; + + if (existing) { + if (isNegotiated()) { + Variant preferredVariant = getPreferredVariant(getVariants(Method.GET)); + + if (preferredVariant == null && getConnegService().isStrict()) { + doError(Status.CLIENT_ERROR_NOT_ACCEPTABLE); + } else { + resultInfo = doGetInfo(preferredVariant); + } + } else { + resultInfo = doGetInfo(); + } + + if (resultInfo == null) { + if ((getStatus() == null) + || (getStatus().isSuccess() + && !Status.SUCCESS_NO_CONTENT.equals(getStatus()))) { + doError(Status.CLIENT_ERROR_NOT_FOUND); + } else { + // Keep the current status as the developer might + // prefer a special status like 'method not authorized'. + } + } else { + Status status = getConditions().getStatus(getMethod(), resultInfo); + + if (status != null) { + if (status.isError()) { + doError(status); + } else { + setStatus(status); + } + } + } + } else { + Status status = getConditions().getStatus(getMethod(), resultInfo); + + if (status != null) { + if (status.isError()) { + doError(status); + } else { + setStatus(status); + } + } + } + + if ((Method.GET.equals(getMethod()) || Method.HEAD.equals(getMethod())) + && resultInfo instanceof Representation) { + result = (Representation) resultInfo; + } else if ((getStatus() != null) && getStatus().isSuccess()) { + // Conditions were passed successfully, continue the normal + // processing. + if (isNegotiated()) { + // Reset the list of variants, as the method differs. + getVariants().clear(); + result = doNegotiatedHandle(); + } else { + result = doHandle(); + } + } + } else { + if (isNegotiated()) { + result = doNegotiatedHandle(); + } else { + result = doHandle(); + } + } + + return result; + } + + /** By default, it sets the status on the response. */ + @Override + protected void doError(Status errorStatus) { + setStatus(errorStatus); + } + + /** + * Returns a descriptor of the response entity returned by a {@link Method#GET} call. + * + * @return The response entity. + * @throws ResourceException + */ + private RepresentationInfo doGetInfo() throws ResourceException { + RepresentationInfo result = null; + MethodAnnotationInfo annotationInfo; + + try { + annotationInfo = getAnnotation(Method.GET); + + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + result = getInfo(); + } + } catch (IOException e) { + throw new ResourceException(e); + } + + return result; + } + + /** + * Returns a descriptor of the response entity returned by a negotiated {@link Method#GET} call. + * + * @param variant The selected variant descriptor. + * @return The response entity descriptor. + * @throws ResourceException + */ + private RepresentationInfo doGetInfo(Variant variant) throws ResourceException { + final RepresentationInfo result; + + if (variant != null) { + if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else if (variant instanceof RepresentationInfo) { + result = (RepresentationInfo) variant; + } else { + result = getInfo(variant); + } + } else { + result = doGetInfo(); + } + + return result; + } + + /** + * Effectively handles a call without content negotiation of the response entity. The default + * behavior is to dispatch the call to one of the {@link #get()}, {@link #post(Representation)}, + * {@link #put(Representation)}, {@link #delete()}, {@link #head()} or {@link #options()} + * methods. + * + * @return The response entity. + * @throws ResourceException + */ + protected Representation doHandle() throws ResourceException { + Representation result = null; + Method method = getMethod(); + + if (method == null) { + setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "No method specified"); + } else { + if (method.equals(Method.PUT)) { + result = put(getRequestEntity()); + } else if (method.equals(Method.PATCH)) { + result = patch(getRequestEntity()); + } else if (isExisting()) { + if (method.equals(Method.GET)) { + result = get(); + } else if (method.equals(Method.POST)) { + result = post(getRequestEntity()); + } else if (method.equals(Method.DELETE)) { + result = delete(); + } else if (method.equals(Method.HEAD)) { + result = head(); + } else if (method.equals(Method.OPTIONS)) { + result = options(); + } else { + result = doHandle(method, getQuery(), getRequestEntity()); + } + } else { + doError(Status.CLIENT_ERROR_NOT_FOUND); + } + } + + return result; + } + + /** + * Effectively handles a call with content negotiation of the response entity using an annotated + * method. + * + * @param annotationInfo The annotation descriptor. + * @param variant The response variant expected (can be null). + * @return The response entity. + * @throws ResourceException + */ + protected Representation doHandle(MethodAnnotationInfo annotationInfo, Variant variant) + throws ResourceException { + Representation result = null; + Class[] parameterTypes = annotationInfo.getJavaInputTypes(); + + // Invoke the annotated method and get the resulting object. + Object resultObject = null; + + try { + if (parameterTypes.length > 0) { + List parameters = new ArrayList(); + Object parameter = null; + + for (Class parameterType : parameterTypes) { + if (Variant.class.equals(parameterType)) { + parameters.add(variant); + } else { + if (getRequestEntity() != null + && getRequestEntity().isAvailable() + && getRequestEntity().getSize() != 0) { + // Assume there is content to be read. + // NB: it does not handle the case where the size is + // unknown, but there is no content. + parameter = toObject(getRequestEntity(), parameterType); + + if (parameter == null) { + throw new ResourceException( + Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE); + } + } else { + parameter = null; + } + + parameters.add(parameter); + } + } + + resultObject = annotationInfo.getJavaMethod().invoke(this, parameters.toArray()); + } else { + resultObject = annotationInfo.getJavaMethod().invoke(this); + } + + if (resultObject != null) { + result = toRepresentation(resultObject, variant); + } + + } catch (IllegalArgumentException | IllegalAccessException | IOException e) { + throw new ResourceException(e); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof ResourceException) { + throw (ResourceException) e.getTargetException(); + } + + throw new ResourceException(e.getTargetException()); + } + + return result; + } + + /** + * Handles a call and checks the request's method and entity. If the method is not supported, + * the response status is set to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. If the request's entity is no + * supported, the response status is set to {@link Status#CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE}. + * + * @param method The request method. + * @param query The query parameters. + * @param entity The request entity (can be null, or unavailable). + * @return The response entity. + * @throws IOException + */ + private Representation doHandle(Method method, Form query, Representation entity) + throws ResourceException { + Representation result = null; + + try { + if (getAnnotation(method) != null) { + // We know the method is supported, let's check the entity. + MethodAnnotationInfo annotationInfo = getAnnotation(method, query, entity); + + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + // The request entity is not supported. + doError(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE); + } + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + } catch (IOException e) { + throw new ResourceException(e); + } + + return result; + } + + /** + * Effectively handles a call with content negotiation of the response entity. The default + * behavior is to dispatch the call to one of the {@link #get(Variant)}, {@link + * #post(Representation,Variant)}, {@link #put(Representation,Variant)}, {@link + * #delete(Variant)}, {@link #head(Variant)} or {@link #options(Variant)} methods. + * + * @param variant The response variant expected. + * @return The response entity. + * @throws ResourceException + */ + protected Representation doHandle(Variant variant) throws ResourceException { + Representation result = null; + Method method = getMethod(); + + if (method == null) { + setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "No method specified"); + } else { + if (method.equals(Method.PUT)) { + result = put(getRequestEntity(), variant); + } else if (method.equals(Method.PATCH)) { + result = patch(getRequestEntity(), variant); + } else if (isExisting()) { + if (method.equals(Method.GET)) { + if (variant instanceof Representation) { + result = (Representation) variant; + } else { + result = get(variant); + } + } else if (method.equals(Method.POST)) { + result = post(getRequestEntity(), variant); + } else if (method.equals(Method.DELETE)) { + result = delete(variant); + } else if (method.equals(Method.HEAD)) { + if (variant instanceof Representation) { + result = (Representation) variant; + } else { + result = head(variant); + } + } else if (method.equals(Method.OPTIONS)) { + if (variant instanceof Representation) { + result = (Representation) variant; + } else { + result = options(variant); + } + } else if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + } else { + doError(Status.CLIENT_ERROR_NOT_FOUND); + } + } + + return result; + } + + /** + * Effectively handles a call with content negotiation of the response entity. The default + * behavior is to dispatch the call to call a matching annotated method or one of the {@link + * #get(Variant)}, {@link #post(Representation,Variant)}, {@link #put(Representation,Variant)}, + * {@link #delete(Variant)}, {@link #head(Variant)} or {@link #options(Variant)} methods.
+ *
+ * If no acceptable variant is found, the {@link + * org.restlet.data.Status#CLIENT_ERROR_NOT_ACCEPTABLE} status is set. + * + * @return The response entity. + * @throws ResourceException + */ + protected Representation doNegotiatedHandle() throws ResourceException { + Representation result = null; + + if ((getVariants() != null) && (!getVariants().isEmpty())) { + Variant preferredVariant = getPreferredVariant(getVariants()); + + if (preferredVariant == null) { + // No variant was found matching the client preferences + doError(Status.CLIENT_ERROR_NOT_ACCEPTABLE); + result = describeVariants(); + } else { + // Update the variant dimensions used for content + // negotiation + updateDimensions(); + result = doHandle(preferredVariant); + } + } else { + // No variant declared for this method. + result = doHandle(); + } + + return result; + } + + /** + * Returns a full representation. This method is only invoked if content negotiation has been + * disabled as indicated by the {@link #isNegotiated()} , otherwise the {@link #get(Variant)} + * method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. + * + * @return The resource's representation. + * @throws ResourceException + * @see HTTP GET + * method + */ + protected Representation get() throws ResourceException { + Representation result = null; + MethodAnnotationInfo annotationInfo; + + try { + annotationInfo = getAnnotation(Method.GET); + + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + } catch (IOException e) { + throw new ResourceException(e); + } + + return result; + } + + /** + * Returns a full representation for a given variant. A variant parameter is passed to indicate + * which representation should be returned, if any.
+ *
+ * This method is only invoked if content negotiation has been enabled as indicated by the + * {@link #isNegotiated()}, otherwise the {@link #get()} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
+ * + * @param variant The variant whose full representation must be returned. + * @return The resource's representation. + * @see #get(Variant) + * @throws ResourceException + */ + protected Representation get(Variant variant) throws ResourceException { + Representation result = null; + + if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + + return result; + } + + /** + * Returns the first annotation descriptor matching the given method. + * + * @param method The method to match. + * @return The annotation descriptor. + * @throws IOException + */ + protected MethodAnnotationInfo getAnnotation(Method method) throws IOException { + return getAnnotation(method, getQuery(), null); + } + + /** + * Returns the first annotation descriptor matching the given method. + * + * @param method The method to match. + * @param query The query parameters. + * @param entity The request entity or null. + * @return The annotation descriptor. + * @throws IOException + */ + protected MethodAnnotationInfo getAnnotation(Method method, Form query, Representation entity) + throws IOException { + if (isAnnotated()) { + return AnnotationUtils.getInstance() + .getMethodAnnotation( + getAnnotations(), + method, + query, + entity, + getMetadataService(), + getConverterService()); + } + + return null; + } + + /** + * Returns the annotation descriptors. + * + * @return The annotation descriptors. + */ + protected List getAnnotations() { + return isAnnotated() ? AnnotationUtils.getInstance().getAnnotations(getClass()) : null; + } + + /** + * Returns the attribute value by looking up the given name in the request attributes maps. The + * toString() method is then invoked on the attribute value. This is typically used for + * variables that are declared in the URI template used to route the call to this resource. + * + * @param name The attribute name. + * @return The request attribute value. + */ + public String getAttribute(String name) { + Object value = getRequestAttributes().get(name); + return (value == null) ? null : value.toString(); + } + + /** + * Returns the description. + * + * @return The description + */ + public String getDescription() { + return this.description; + } + + /** + * Returns information about the resource's representation. This metadata is important for + * conditional method processing. The advantage over the complete {@link Representation} class + * is that it is much lighter to create. This method is only invoked if content negotiation has + * been disabled as indicated by the {@link #isNegotiated()}, otherwise the {@link + * #getInfo(Variant)} method is invoked.
+ *
+ * The default behavior is to invoke the {@link #get()} method. + * + * @return Information about the resource's representation. + * @throws ResourceException + */ + protected RepresentationInfo getInfo() throws ResourceException { + return get(); + } + + /** + * Returns information about the resource's representation. This metadata is important for + * conditional method processing. The advantage over the complete {@link Representation} class + * is that it is much lighter to create. A variant parameter is passed to indicate which + * representation should be returned, if any.
+ *
+ * This method is only invoked if content negotiation has been enabled as indicated by the + * {@link #isNegotiated()}, otherwise the {@link #getInfo(Variant)} method is invoked.
+ *
+ * The default behavior is to invoke the {@link #get(Variant)} method. + * + * @param variant The variant whose representation information must be returned. + * @return Information about the resource's representation. + * @throws ResourceException + */ + protected RepresentationInfo getInfo(Variant variant) throws ResourceException { + return get(variant); + } + + /** + * Returns the display name. + * + * @return The display name. + */ + public String getName() { + return this.name; + } + + /** + * Returns the callback invoked after sending the response. + * + * @return The callback invoked after sending the response. + */ + public Uniform getOnSent() { + return getResponse().getOnSent(); + } + + /** + * Returns the preferred variant among a list of available variants. The selection is based on + * the client preferences using the {@link + * org.restlet.service.ConnegService#getPreferredVariant(List, Request, + * org.restlet.service.MetadataService)} method. + * + * @param variants The available variants. + * @return The preferred variant. + */ + protected Variant getPreferredVariant(List variants) { + Variant result = null; + + // If variants were found, select the best matching one + if ((variants != null) && (!variants.isEmpty())) { + result = + getConnegService() + .getPreferredVariant(variants, getRequest(), getMetadataService()); + } + + return result; + } + + /** + * Retrieves an existing role or creates a new one if needed based on its name. Note that a null + * description will be set if the role has to be created. + * + * @param name The role name to find or create. + * @return The role found or created. + */ + public Role getRole(String name) { + return Role.get(getApplication(), name); + } + + /** + * Returns a modifiable list of exposed variants for the current request method. You can declare + * variants manually by updating the result list, by overriding this method. By default, the + * variants will be provided based on annotated methods. + * + * @return The modifiable list of variants. + * @throws IOException + */ + public List getVariants() { + return getVariants(getMethod()); + } + + /** + * Returns a modifiable list of exposed variants for the given method. You can declare variants + * manually by updating the result list, by overriding this method. By default, the variants + * will be provided based on annotated methods. + * + * @param method The method. + * @return The modifiable list of variants. + */ + protected List getVariants(final Method method) { + + if (this.variants == null && isAnnotated() && hasAnnotations()) { + this.variants = + getVariantsFromAnnotations((Method.HEAD.equals(method)) ? Method.GET : method); + } else if (this.variants == null) { + this.variants = new ArrayList<>(); + } + + return this.variants; + } + + private List getVariantsFromAnnotations(Method method) { + final List result = new ArrayList<>(); + + for (AnnotationInfo annotationInfo : getAnnotations()) { + if (!(annotationInfo instanceof final MethodAnnotationInfo methodAnnotationInfo)) { + continue; + } + + try { + final boolean methodAnnotationIsCompatible = + methodAnnotationInfo.isCompatible( + method, + getQuery(), + getRequestEntity(), + getMetadataService(), + getConverterService()); + + if (!methodAnnotationIsCompatible) { + continue; + } + + final List responseVariants = + methodAnnotationInfo.getResponseVariants( + getMetadataService(), getConverterService()); + + if (responseVariants == null) { + continue; + } + + // Compute an affinity score between this annotation and the input entity. + float score = 0.5f; + if ((getRequest().getEntity() != null) && getRequest().getEntity().isAvailable()) { + final MediaType requestEntityMediaType = + getRequest().getEntity().getMediaType(); + final List amts = + getMetadataService().getAllMediaTypes(methodAnnotationInfo.getInput()); + if (amts != null) { + for (MediaType amt : amts) { + if (amt.equals(requestEntityMediaType)) { + score = 1.0f; + } else if (amt.includes(requestEntityMediaType)) { + score = Math.max(0.8f, score); + } else if (amt.isCompatible(requestEntityMediaType)) { + score = Math.max(0.6f, score); + } + } + } + } else if (methodAnnotationInfo.getInput() == null + || methodAnnotationInfo + .getInput() + .isEmpty()) { // the annotation does not declare input media type + score = 1.0f; + } else if (methodAnnotationInfo.getJavaInputTypes() == null + || methodAnnotationInfo.getJavaInputTypes().length + == 0) { // the annotated method does not require an entity + score = 0.9f; + } + + for (Variant variant : responseVariants) { + final VariantInfo variantInfo = new VariantInfo(variant, methodAnnotationInfo); + variantInfo.setInputScore(score); + result.add(variantInfo); + } + + } catch (IOException e) { + getLogger().log(Level.FINE, "Unable to get variants from annotation", e); + } + } + + return result; + } + + /** + * Handles any call to this resource. The default implementation checks the {@link + * #isConditional()} and {@link #isNegotiated()} method to determine which one of the {@link + * #doConditionalHandle()}, {@link #doNegotiatedHandle()} and {@link #doHandle()} methods should + * be invoked. It also catches any {@link ResourceException} thrown and updates the response + * status using the {@link #setStatus(Status, Throwable, String)} method.
+ *
+ * After handling, if the status is set to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}, then {@link + * #updateAllowedMethods()} is invoked to give the resource a chance to inform the client about + * the allowed methods. + * + * @return The response entity, but this method is still responsible for setting the response + * entity. + */ + @Override + public Representation handle() { + Representation result = null; + + // If the resource is not available after initialization and if this a + // retrieval method, then return a "not found" response. + if (!isExisting() && getMethod().isSafe()) { + doError(Status.CLIENT_ERROR_NOT_FOUND); + } else { + try { + if (isConditional()) { + result = doConditionalHandle(); + } else if (isNegotiated()) { + result = doNegotiatedHandle(); + } else { + result = doHandle(); + } + + if (result != null) { + // If the user manually set the entity, keep it + getResponse().setEntity(result); + } + + } catch (Throwable t) { + doCatch(t); + } finally { + if (Status.CLIENT_ERROR_METHOD_NOT_ALLOWED.equals(getStatus())) { + updateAllowedMethods(); + } else if (Status.SUCCESS_OK.equals(getStatus()) + && (getResponseEntity() == null || !getResponseEntity().isAvailable())) { + getLogger() + .fine( + "A response with a 200 (Ok) status should have an entity. " + + "Changing the status to 204 (No content)."); + setStatus(Status.SUCCESS_NO_CONTENT); + } + } + } + + return result; + } + + /** + * Indicates if annotations were defined on this resource. + * + * @return True if annotations were defined on this resource. + */ + protected boolean hasAnnotations() { + return (getAnnotations() != null) && (!getAnnotations().isEmpty()); + } + + /** + * Returns a representation whose metadata will be returned to the client. This method is only + * invoked if content negotiation has been disabled as indicated by the {@link #isNegotiated()}, + * otherwise the {@link #head(Variant)} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. + * + * @return The resource's representation. + * @throws ResourceException + * @see HTTP GET + * method + */ + protected Representation head() throws ResourceException { + return get(); + } + + /** + * Returns a representation whose metadata will be returned to the client. A variant parameter + * is passed to indicate which representation should be returned, if any.
+ *
+ * This method is only invoked if content negotiation has been enabled as indicated by the + * {@link #isNegotiated()}, otherwise the {@link #head()} method is invoked.
+ *
+ * The default implementation directly returns the variant if it is already an instance of + * {@link Representation}. In other cases, you need to override this method to provide your own + * implementation. * + * + * @param variant The variant whose full representation must be returned. + * @return The resource's representation. + * @see #get(Variant) + * @throws ResourceException + */ + protected Representation head(Variant variant) throws ResourceException { + return get(variant); + } + + /** + * Indicates if annotations are supported. The default value is true. + * + * @return True if annotations are supported. + */ + public boolean isAnnotated() { + return annotated; + } + + /** + * Indicates if the response should be automatically committed. When processing a request on the + * server-side, setting this property to 'false' let you ask the server connector to wait before + * sending the response back to the client when the initial calling thread returns. This will + * let you do further updates to the response and manually calling {@link #commit()} later on, + * using another thread. + * + * @return True if the response should be automatically committed. + */ + public boolean isAutoCommitting() { + return getResponse().isAutoCommitting(); + } + + /** + * Indicates if the response has already been committed. + * + * @return True if the response has already been committed. + */ + public boolean isCommitted() { + return getResponse().isCommitted(); + } + + /** + * Indicates if conditional handling is enabled. The default value is true. + * + * @return True if conditional handling is enabled. + */ + public boolean isConditional() { + return conditional; + } + + /** + * Indicates if the identified resource exists. The default value is true. + * + * @return True if the identified resource exists. + */ + public boolean isExisting() { + return existing; + } + + /** + * Indicates if the authenticated client user associated with the current request is in the + * given role name. + * + * @param roleName The role name to test. + * @return True if the authenticated subject is in the given role. + */ + public boolean isInRole(String roleName) { + return getClientInfo().getRoles().contains(getRole(roleName)); + } + + /** + * Indicates if content negotiation of response entities is enabled. The default value is true. + * + * @return True if content negotiation of response entities is enabled. + */ + public boolean isNegotiated() { + return this.negotiated; + } + + /** + * Indicates the communication options available for this resource. This method is only invoked + * if content negotiation has been disabled as indicated by the {@link #isNegotiated()}, + * otherwise the {@link #options(Variant)} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. + * + * @return The optional response entity. + */ + protected Representation options() throws ResourceException { + Representation result = null; + MethodAnnotationInfo annotationInfo; + + try { + annotationInfo = getAnnotation(Method.OPTIONS); + + // Updates the list of allowed methods + updateAllowedMethods(); + + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + } catch (IOException e) { + throw new ResourceException(e); + } + + return result; + } + + /** + * Indicates the communication options available for this resource. A variant parameter is + * passed to indicate which representation should be returned, if any.
+ *
+ * This method is only invoked if content negotiation has been enabled as indicated by the + * {@link #isNegotiated()}, otherwise the {@link #options()} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
+ * + * @param variant The variant of the response entity. + * @return The optional response entity. + * @see #get(Variant) + */ + protected Representation options(Variant variant) throws ResourceException { + Representation result = null; + + // Updates the list of allowed methods + updateAllowedMethods(); + + if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } return result; - } - - /** - * Handles a call and checks the request's method and entity. If the method is - * not supported, the response status is set to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. If the - * request's entity is no supported, the response status is set to - * {@link Status#CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE}. - * - * @param method The request method. - * @param query The query parameters. - * @param entity The request entity (can be null, or unavailable). - * @return The response entity. - * @throws IOException - */ - private Representation doHandle(Method method, Form query, Representation entity) throws ResourceException { - Representation result = null; - - try { - if (getAnnotation(method) != null) { - // We know the method is supported, let's check the entity. - MethodAnnotationInfo annotationInfo = getAnnotation(method, query, entity); - - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - // The request entity is not supported. - doError(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE); - } - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); - } - - return result; - } - - /** - * Effectively handles a call with content negotiation of the response entity. - * The default behavior is to dispatch the call to one of the - * {@link #get(Variant)}, {@link #post(Representation,Variant)}, - * {@link #put(Representation,Variant)}, {@link #delete(Variant)}, - * {@link #head(Variant)} or {@link #options(Variant)} methods. - * - * @param variant The response variant expected. - * @return The response entity. - * @throws ResourceException - */ - protected Representation doHandle(Variant variant) throws ResourceException { - Representation result = null; - Method method = getMethod(); - - if (method == null) { - setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "No method specified"); - } else { - if (method.equals(Method.PUT)) { - result = put(getRequestEntity(), variant); - } else if (method.equals(Method.PATCH)) { - result = patch(getRequestEntity(), variant); - } else if (isExisting()) { - if (method.equals(Method.GET)) { - if (variant instanceof Representation) { - result = (Representation) variant; - } else { - result = get(variant); - } - } else if (method.equals(Method.POST)) { - result = post(getRequestEntity(), variant); - } else if (method.equals(Method.DELETE)) { - result = delete(variant); - } else if (method.equals(Method.HEAD)) { - if (variant instanceof Representation) { - result = (Representation) variant; - } else { - result = head(variant); - } - } else if (method.equals(Method.OPTIONS)) { - if (variant instanceof Representation) { - result = (Representation) variant; - } else { - result = options(variant); - } - } else if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } else { - doError(Status.CLIENT_ERROR_NOT_FOUND); - } - } - - return result; - } - - /** - * Effectively handles a call with content negotiation of the response entity. - * The default behavior is to dispatch the call to call a matching annotated - * method or one of the {@link #get(Variant)}, - * {@link #post(Representation,Variant)}, {@link #put(Representation,Variant)}, - * {@link #delete(Variant)}, {@link #head(Variant)} or {@link #options(Variant)} - * methods.
- *
- * If no acceptable variant is found, the - * {@link org.restlet.data.Status#CLIENT_ERROR_NOT_ACCEPTABLE} status is set. - * - * @return The response entity. - * @throws ResourceException - */ - protected Representation doNegotiatedHandle() throws ResourceException { - Representation result = null; - - if ((getVariants() != null) && (!getVariants().isEmpty())) { - Variant preferredVariant = getPreferredVariant(getVariants()); - - if (preferredVariant == null) { - // No variant was found matching the client preferences - doError(Status.CLIENT_ERROR_NOT_ACCEPTABLE); - result = describeVariants(); - } else { - // Update the variant dimensions used for content - // negotiation - updateDimensions(); - result = doHandle(preferredVariant); - } - } else { - // No variant declared for this method. - result = doHandle(); - } - - return result; - } - - /** - * Returns a full representation. This method is only invoked if content - * negotiation has been disabled as indicated by the {@link #isNegotiated()} , - * otherwise the {@link #get(Variant)} method is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. - * - * @return The resource's representation. - * @throws ResourceException - * @see HTTP GET - * method - */ - protected Representation get() throws ResourceException { - Representation result = null; - MethodAnnotationInfo annotationInfo; - - try { - annotationInfo = getAnnotation(Method.GET); - - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); - } - - return result; - } - - /** - * Returns a full representation for a given variant. A variant parameter is - * passed to indicate which representation should be returned if any.
- *
- * This method is only invoked if content negotiation has been enabled as - * indicated by the {@link #isNegotiated()}, otherwise the {@link #get()} method - * is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
- * - * @param variant The variant whose full representation must be returned. - * @return The resource's representation. - * @see #get(Variant) - * @throws ResourceException - */ - protected Representation get(Variant variant) throws ResourceException { - Representation result = null; - - if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - - return result; - } - - /** - * Returns the first annotation descriptor matching the given method. - * - * @param method The method to match. - * @return The annotation descriptor. - * @throws IOException - */ - protected MethodAnnotationInfo getAnnotation(Method method) throws IOException { - return getAnnotation(method, getQuery(), null); - } - - /** - * Returns the first annotation descriptor matching the given method. - * - * @param method The method to match. - * @param query The query parameters. - * @param entity The request entity or null. - * @return The annotation descriptor. - * @throws IOException - */ - protected MethodAnnotationInfo getAnnotation(Method method, Form query, Representation entity) throws IOException { - if (isAnnotated()) { - return AnnotationUtils.getInstance().getMethodAnnotation(getAnnotations(), method, query, entity, - getMetadataService(), getConverterService()); - } - - return null; - } - - /** - * Returns the annotation descriptors. - * - * @return The annotation descriptors. - */ - protected List getAnnotations() { - return isAnnotated() ? AnnotationUtils.getInstance().getAnnotations(getClass()) : null; - } - - /** - * Returns the attribute value by looking up the given name in the request - * attributes maps. The toString() method is then invoked on the attribute - * value. This is typically used for variables that are declared in the URI - * template used to route the call to this resource. - * - * @param name The attribute name. - * @return The request attribute value. - */ - public String getAttribute(String name) { - Object value = getRequestAttributes().get(name); - return (value == null) ? null : value.toString(); - } - - /** - * Returns the description. - * - * @return The description - */ - public String getDescription() { - return this.description; - } - - /** - * Returns information about the resource's representation. Those metadata are - * important for conditional method processing. The advantage over the complete - * {@link Representation} class is that it is much lighter to create. This - * method is only invoked if content negotiation has been disabled as indicated - * by the {@link #isNegotiated()}, otherwise the {@link #getInfo(Variant)} - * method is invoked.
- *
- * The default behavior is to invoke the {@link #get()} method. - * - * @return Information about the resource's representation. - * @throws ResourceException - */ - protected RepresentationInfo getInfo() throws ResourceException { - return get(); - } - - /** - * Returns information about the resource's representation. Those metadata are - * important for conditional method processing. The advantage over the complete - * {@link Representation} class is that it is much lighter to create. A variant - * parameter is passed to indicate which representation should be returned if - * any.
- *
- * This method is only invoked if content negotiation has been enabled as - * indicated by the {@link #isNegotiated()}, otherwise the - * {@link #getInfo(Variant)} method is invoked.
- *
- * The default behavior is to invoke the {@link #get(Variant)} method. - * - * @param variant The variant whose representation information must be returned. - * @return Information about the resource's representation. - * @throws ResourceException - */ - protected RepresentationInfo getInfo(Variant variant) throws ResourceException { - return get(variant); - } - - /** - * Returns the display name. - * - * @return The display name. - */ - public String getName() { - return this.name; - } - - /** - * Returns the callback invoked after sending the response. - * - * @return The callback invoked after sending the response. - */ - public Uniform getOnSent() { - return getResponse().getOnSent(); - } - - /** - * Returns the preferred variant among a list of available variants. The - * selection is based on the client preferences using the - * {@link org.restlet.service.ConnegService#getPreferredVariant(List, Request, org.restlet.service.MetadataService)} - * method. - * - * @param variants The available variants. - * @return The preferred variant. - */ - protected Variant getPreferredVariant(List variants) { - Variant result = null; - - // If variants were found, select the best matching one - if ((variants != null) && (!variants.isEmpty())) { - result = getConnegService().getPreferredVariant(variants, getRequest(), getMetadataService()); - } - - return result; - } - - /** - * Retrieves an existing role or creates a new one if needed based on its name. - * Note that a null description will be set if the role has to be created. - * - * @param name The role name to find or create. - * @return The role found or created. - */ - public Role getRole(String name) { - return Role.get(getApplication(), name); - } - - /** - * Returns a modifiable list of exposed variants for the current request method. - * You can declare variants manually by updating the result list , by overriding - * this method. By default, the variants will be provided based on annotated - * methods. - * - * @return The modifiable list of variants. - * @throws IOException - */ - public List getVariants() { - return getVariants(getMethod()); - } - - /** - * Returns a modifiable list of exposed variants for the given method. You can - * declare variants manually by updating the result list, by overriding this - * method. By default, the variants will be provided based on annotated methods. - * - * @param method The method. - * @return The modifiable list of variants. - */ - protected List getVariants(final Method method) { - - if (this.variants == null && isAnnotated() && hasAnnotations()) { - this.variants = getVariantsFromAnnotations((Method.HEAD.equals(method)) ? Method.GET : method); - } else if (this.variants == null) { - this.variants = new ArrayList<>(); - } - - return this.variants; - } - - private List getVariantsFromAnnotations(Method method) { - final List result = new ArrayList<>(); - - for (AnnotationInfo annotationInfo : getAnnotations()) { - if (!(annotationInfo instanceof MethodAnnotationInfo)) { - continue; - } - - final MethodAnnotationInfo methodAnnotationInfo = (MethodAnnotationInfo) annotationInfo; - try { - final boolean methodAnnotationIsCompatible = methodAnnotationInfo.isCompatible(method, getQuery(), getRequestEntity(), - getMetadataService(), getConverterService()); - - if (!methodAnnotationIsCompatible) { - continue; - } - - final List responseVariants = methodAnnotationInfo.getResponseVariants(getMetadataService(), getConverterService()); - - if (responseVariants == null) { - continue; - } - - // Compute an affinity score between this annotation and the input entity. - float score = 0.5f; - if ((getRequest().getEntity() != null) && getRequest().getEntity().isAvailable()) { - final MediaType requestEntityMediaType = getRequest().getEntity().getMediaType(); - final List amts = getMetadataService().getAllMediaTypes(methodAnnotationInfo.getInput()); - if (amts != null) { - for (MediaType amt : amts) { - if (amt.equals(requestEntityMediaType)) { - score = 1.0f; - } else if (amt.includes(requestEntityMediaType)) { - score = Math.max(0.8f, score); - } else if (amt.isCompatible(requestEntityMediaType)) { - score = Math.max(0.6f, score); - } - } - } - } else if (methodAnnotationInfo.getInput() == null || methodAnnotationInfo.getInput().isEmpty()) { // the annotation does not declare input media type - score = 1.0f; - } else if (methodAnnotationInfo.getJavaInputTypes() == null || methodAnnotationInfo.getJavaInputTypes().length == 0) { // the annotated method does not require an entity - score = 0.9f; - } - - for (Variant variant : responseVariants) { - final VariantInfo variantInfo = new VariantInfo(variant, methodAnnotationInfo); - variantInfo.setInputScore(score); - result.add(variantInfo); - } - - } catch (IOException e) { - getLogger().log(Level.FINE, "Unable to get variants from annotation", e); - } - } - - return result; - } - - /** - * Handles any call to this resource. The default implementation check the - * {@link #isConditional()} and {@link #isNegotiated()} method to determine - * which one of the {@link #doConditionalHandle()}, - * {@link #doNegotiatedHandle()} and {@link #doHandle()} methods should be - * invoked. It also catches any {@link ResourceException} thrown and updates the - * response status using the {@link #setStatus(Status, Throwable, String)} - * method.
- *
- * After handling, if the status is set to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}, then - * {@link #updateAllowedMethods()} is invoked to give the resource a chance to - * inform the client about the allowed methods. - * - * @return The response entity, but this method is still responsible for setting - * the response entity. - */ - @Override - public Representation handle() { - Representation result = null; - - // If the resource is not available after initialization and if this a - // retrieval method, then return a "not found" response. - if (!isExisting() && getMethod().isSafe()) { - doError(Status.CLIENT_ERROR_NOT_FOUND); - } else { - try { - if (isConditional()) { - result = doConditionalHandle(); - } else if (isNegotiated()) { - result = doNegotiatedHandle(); - } else { - result = doHandle(); - } - - if (result != null) { - // If the user manually set the entity, keep it - getResponse().setEntity(result); - } - - } catch (Throwable t) { - doCatch(t); - } finally { - if (Status.CLIENT_ERROR_METHOD_NOT_ALLOWED.equals(getStatus())) { - updateAllowedMethods(); - } else if (Status.SUCCESS_OK.equals(getStatus()) - && (getResponseEntity() == null || !getResponseEntity().isAvailable())) { - getLogger().fine("A response with a 200 (Ok) status should have an entity. " - + "Changing the status to 204 (No content)."); - setStatus(Status.SUCCESS_NO_CONTENT); - } - } - } - - return result; - } - - /** - * Indicates if annotations were defined on this resource. - * - * @return True if annotations were defined on this resource. - */ - protected boolean hasAnnotations() { - return (getAnnotations() != null) && (!getAnnotations().isEmpty()); - } - - /** - * Returns a representation whose metadata will be returned to the client. This - * method is only invoked if content negotiation has been disabled as indicated - * by the {@link #isNegotiated()}, otherwise the {@link #head(Variant)} method - * is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. - * - * @return The resource's representation. - * @throws ResourceException - * @see HTTP GET - * method - */ - protected Representation head() throws ResourceException { - return get(); - } - - /** - * Returns a representation whose metadata will be returned to the client. A - * variant parameter is passed to indicate which representation should be - * returned if any.
- *
- * This method is only invoked if content negotiation has been enabled as - * indicated by the {@link #isNegotiated()}, otherwise the {@link #head()} - * method is invoked.
- *
- * The default implementation directly returns the variant if it is already an - * instance of {@link Representation}. In other cases, you need to override this - * method in order to provide your own implementation. * - * - * @param variant The variant whose full representation must be returned. - * @return The resource's representation. - * @see #get(Variant) - * @throws ResourceException - */ - protected Representation head(Variant variant) throws ResourceException { - return get(variant); - } - - /** - * Indicates if annotations are supported. The default value is true. - * - * @return True if annotations are supported. - */ - public boolean isAnnotated() { - return annotated; - } - - /** - * Indicates if the response should be automatically committed. When processing - * a request on the server-side, setting this property to 'false' let you ask to - * the server connector to wait before sending the response back to the client - * when the initial calling thread returns. This will let you do further updates - * to the response and manually calling {@link #commit()} later on, using - * another thread. - * - * @return True if the response should be automatically committed. - */ - public boolean isAutoCommitting() { - return getResponse().isAutoCommitting(); - } - - /** - * Indicates if the response has already been committed. - * - * @return True if the response has already been committed. - */ - public boolean isCommitted() { - return getResponse().isCommitted(); - } - - /** - * Indicates if conditional handling is enabled. The default value is true. - * - * @return True if conditional handling is enabled. - */ - public boolean isConditional() { - return conditional; - } - - /** - * Indicates if the identified resource exists. The default value is true. - * - * @return True if the identified resource exists. - */ - public boolean isExisting() { - return existing; - } - - /** - * Indicates if the authenticated client user associated to the current request - * is in the given role name. - * - * @param roleName The role name to test. - * @return True if the authenticated subject is in the given role. - */ - public boolean isInRole(String roleName) { - return getClientInfo().getRoles().contains(getRole(roleName)); - } - - /** - * Indicates if content negotiation of response entities is enabled. The default - * value is true. - * - * @return True if content negotiation of response entities is enabled. - */ - public boolean isNegotiated() { - return this.negotiated; - } - - /** - * Indicates the communication options available for this resource. This method - * is only invoked if content negotiation has been disabled as indicated by the - * {@link #isNegotiated()}, otherwise the {@link #options(Variant)} method is - * invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. - * - * @return The optional response entity. - */ - protected Representation options() throws ResourceException { - Representation result = null; - MethodAnnotationInfo annotationInfo; - - try { - annotationInfo = getAnnotation(Method.OPTIONS); - - // Updates the list of allowed methods - updateAllowedMethods(); - - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); - } - - return result; - } - - /** - * Indicates the communication options available for this resource. A variant - * parameter is passed to indicate which representation should be returned if - * any.
- *
- * This method is only invoked if content negotiation has been enabled as - * indicated by the {@link #isNegotiated()}, otherwise the {@link #options()} - * method is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
- * - * @param variant The variant of the response entity. - * @return The optional response entity. - * @see #get(Variant) - */ - protected Representation options(Variant variant) throws ResourceException { - Representation result = null; - - // Updates the list of allowed methods - updateAllowedMethods(); - - if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - - return result; - } - - /** - * Apply a patch entity to the current representation of the resource retrieved - * by calling {@link #get()}. By default, the - * {@link ConverterService#applyPatch(Representation, Representation)} method is - * used and then the {@link #put(Representation)} method called. - * - * @param entity The patch entity to apply. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP PATCH method - */ - protected Representation patch(Representation entity) throws ResourceException { - AnnotationInfo annotationInfo; - try { - annotationInfo = getAnnotation(Method.PATCH); - if (annotationInfo != null) { - return doHandle(Method.PATCH, getQuery(), entity); - } else { - // Default implementation - return put(getConverterService().applyPatch(get(), entity)); - // doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); - } - } - - /** - * Apply a patch entity to the current representation of the resource retrieved - * by calling {@link #get()}. By default, the - * {@link ConverterService#applyPatch(Representation, Representation)} method is - * used and then the {@link #put(Representation, Variant)} method called. - * - * @param entity The patch entity to apply. - * @param variant The variant of the response entity. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP PATCH method - */ - protected Representation patch(Representation entity, Variant variant) throws ResourceException { - Representation result = null; - - try { - if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else { - // Default implementation - result = put(getConverterService().applyPatch(get(), entity), variant); - // doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); - } - return result; - } - - /** - * Posts a representation to the resource at the target URI reference. This - * method is only invoked if content negotiation has been disabled as indicated - * by the {@link #isNegotiated()}, otherwise the - * {@link #post(Representation, Variant)} method is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. - * - * @param entity The posted entity. - * @return The optional response entity. - * @throws ResourceException - * @see #get(Variant) - * @see HTTP POST - * method - */ - protected Representation post(Representation entity) throws ResourceException { - return doHandle(Method.POST, getQuery(), entity); - } - - /** - * Posts a representation to the resource at the target URI reference. A variant - * parameter is passed to indicate which representation should be returned if - * any.
- *
- * This method is only invoked if content negotiation has been enabled as - * indicated by the {@link #isNegotiated()}, otherwise the - * {@link #post(Representation)} method is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
- * - * @param entity The posted entity. - * @param variant The variant of the response entity. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP POST method - */ - protected Representation post(Representation entity, Variant variant) throws ResourceException { - Representation result = null; - - if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - - return result; - } - - /** - * Creates or updates a resource with the given representation as new state to - * be stored. This method is only invoked if content negotiation has been - * disabled as indicated by the {@link #isNegotiated()}, otherwise the - * {@link #put(Representation, Variant)} method is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. - * - * @param entity The representation to store. - * @return The optional result entity. - * @throws ResourceException - * @see HTTP PUT - * method - */ - protected Representation put(Representation entity) throws ResourceException { - return doHandle(Method.PUT, getQuery(), entity); - } - - /** - * Creates or updates a resource with the given representation as new state to - * be stored. A variant parameter is passed to indicate which representation - * should be returned if any.
- *
- * This method is only invoked if content negotiation has been enabled as - * indicated by the {@link #isNegotiated()}, otherwise the - * {@link #put(Representation)} method is invoked.
- *
- * The default behavior is to set the response status to - * {@link org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
- * - * @param representation The representation to store. - * @param variant The variant of the response entity. - * @return The optional result entity. - * @throws ResourceException - * @see #get(Variant) - * @see HTTP PUT method - */ - protected Representation put(Representation representation, Variant variant) throws ResourceException { - Representation result = null; - - if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - - return result; - } - - /** - * Permanently redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetRef The target URI reference. - */ - public void redirectPermanent(Reference targetRef) { - if (getResponse() != null) { - getResponse().redirectPermanent(targetRef); - } - } - - /** - * Permanently redirects the client to a target URI. The client is expected to - * reuse the same method for the new request.
- *
- * If you pass a relative target URI, it will be resolved with the current base - * reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}. - * - * @param targetUri The target URI. - */ - public void redirectPermanent(String targetUri) { - if (getResponse() != null) { - getResponse().redirectPermanent(targetUri); - } - } - - /** - * Redirects the client to a different URI that SHOULD be retrieved using a GET - * method on that resource. This method exists primarily to allow the output of - * a POST-activated script to redirect the user agent to a selected resource. - * The new URI is not a substitute reference for the originally requested - * resource. - * - * @param targetRef The target reference. - */ - public void redirectSeeOther(Reference targetRef) { - if (getResponse() != null) { - getResponse().redirectSeeOther(targetRef); - } - } - - /** - * Redirects the client to a different URI that SHOULD be retrieved using a GET - * method on that resource. This method exists primarily to allow the output of - * a POST-activated script to redirect the user agent to a selected resource. - * The new URI is not a substitute reference for the originally requested - * resource.
- *
- * If you pass a relative target URI, it will be resolved with the current base - * reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}. - * - * @param targetUri The target URI. - */ - public void redirectSeeOther(String targetUri) { - if (getResponse() != null) { - getResponse().redirectSeeOther(targetUri); - } - } - - /** - * Temporarily redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetRef The target reference. - */ - public void redirectTemporary(Reference targetRef) { - if (getResponse() != null) { - getResponse().redirectTemporary(targetRef); - } - } - - /** - * Temporarily redirects the client to a target URI. The client is expected to - * reuse the same method for the new request.
- *
- * If you pass a relative target URI, it will be resolved with the current base - * reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}. - * - * @param targetUri The target URI. - */ - public void redirectTemporary(String targetUri) { - if (getResponse() != null) { - getResponse().redirectTemporary(targetUri); - } - } - - /** - * Sets the set of methods allowed on the requested resource. The set instance - * set must be thread-safe (use {@link CopyOnWriteArraySet} for example. - * - * @param allowedMethods The set of methods allowed on the requested resource. - * @see Response#setAllowedMethods(Set) - */ - public void setAllowedMethods(Set allowedMethods) { - if (getResponse() != null) { - getResponse().setAllowedMethods(allowedMethods); - } - } - - /** - * Indicates if annotations are supported. The default value is true. - * - * @param annotated Indicates if annotations are supported. - */ - public void setAnnotated(boolean annotated) { - this.annotated = annotated; - } - - /** - * Sets the response attribute value. - * - * @param name The attribute name. - * @param value The attribute to set. - */ - public void setAttribute(String name, Object value) { - getResponseAttributes().put(name, value); - } - - /** - * Indicates if the response should be automatically committed. - * - * @param autoCommitting True if the response should be automatically committed - */ - public void setAutoCommitting(boolean autoCommitting) { - getResponse().setAutoCommitting(autoCommitting); - } - - /** - * Sets the list of authentication requests sent by an origin server to a - * client. The list instance set must be thread-safe (use - * {@link CopyOnWriteArrayList} for example. - * - * @param requests The list of authentication requests sent by an origin server - * to a client. - * @see Response#setChallengeRequests(List) - */ - public void setChallengeRequests(List requests) { - if (getResponse() != null) { - getResponse().setChallengeRequests(requests); - } - } - - /** - * Indicates if the response has already been committed. - * - * @param committed True if the response has already been committed. - */ - public void setCommitted(boolean committed) { - getResponse().setCommitted(committed); - } - - /** - * Indicates if conditional handling is enabled. The default value is true. - * - * @param conditional True if conditional handling is enabled. - */ - public void setConditional(boolean conditional) { - this.conditional = conditional; - } - - /** - * Sets the cookie settings provided by the server. - * - * @param cookieSettings The cookie settings provided by the server. - * @see Response#setCookieSettings(Series) - */ - public void setCookieSettings(Series cookieSettings) { - if (getResponse() != null) { - getResponse().setCookieSettings(cookieSettings); - } - } - - /** - * Sets the description. - * - * @param description The description. - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Sets the set of dimensions on which the response entity may vary. The set - * instance set must be thread-safe (use {@link CopyOnWriteArraySet} for - * example. - * - * @param dimensions The set of dimensions on which the response entity may - * vary. - * @see Response#setDimensions(Set) - */ - public void setDimensions(Set dimensions) { - if (getResponse() != null) { - getResponse().setDimensions(dimensions); - } - } - - /** - * Indicates if the identified resource exists. The default value is true. - * - * @param exists Indicates if the identified resource exists. - */ - public void setExisting(boolean exists) { - this.existing = exists; - } - - /** - * Sets the reference that the client should follow for redirections or resource - * creations. - * - * @param locationRef The reference to set. - * @see Response#setLocationRef(Reference) - */ - public void setLocationRef(Reference locationRef) { - if (getResponse() != null) { - getResponse().setLocationRef(locationRef); - } - } - - /** - * Sets the reference that the client should follow for redirections or resource - * creations. If you pass a relative location URI, it will be resolved with the - * current base reference of the request's resource reference (see - * {@link Request#getResourceRef()} and {@link Reference#getBaseRef()}. - * - * @param locationUri The URI to set. - * @see Response#setLocationRef(String) - */ - public void setLocationRef(String locationUri) { - if (getResponse() != null) { - getResponse().setLocationRef(locationUri); - } - } - - /** - * Sets the display name. - * - * @param name The display name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Indicates if content negotiation of response entities is enabled. The default - * value is true. - * - * @param negotiateContent True if content negotiation of response entities is - * enabled. - */ - public void setNegotiated(boolean negotiateContent) { - this.negotiated = negotiateContent; - } - - /** - * Sets the callback invoked after sending the response. - * - * @param onSentCallback The callback invoked after sending the response. - */ - public void setOnSent(Uniform onSentCallback) { - getResponse().setOnSent(onSentCallback); - } - - /** - * Sets the list of proxy authentication requests sent by an origin server to a - * client. The list instance set must be thread-safe (use - * {@link CopyOnWriteArrayList} for example. - * - * @param requests The list of proxy authentication requests sent by an origin - * server to a client. - * @see Response#setProxyChallengeRequests(List) - */ - public void setProxyChallengeRequests(List requests) { - if (getResponse() != null) { - getResponse().setProxyChallengeRequests(requests); - } - } - - /** - * Sets the server-specific information. - * - * @param serverInfo The server-specific information. - * @see Response#setServerInfo(ServerInfo) - */ - public void setServerInfo(ServerInfo serverInfo) { - if (getResponse() != null) { - getResponse().setServerInfo(serverInfo); - } - } - - /** - * Sets the status. - * - * @param status The status to set. - * @see Response#setStatus(Status) - */ - public void setStatus(Status status) { - if (getResponse() != null) { - getResponse().setStatus(status); - } - } - - /** - * Sets the status. - * - * @param status The status to set. - * @param message The status message. - * @see Response#setStatus(Status, String) - */ - public void setStatus(Status status, String message) { - if (getResponse() != null) { - getResponse().setStatus(status, message); - } - } - - /** - * Sets the status. - * - * @param status The status to set. - * @param throwable The related error or exception. - * @see Response#setStatus(Status, Throwable) - */ - public void setStatus(Status status, Throwable throwable) { - if (getResponse() != null) { - getResponse().setStatus(status, throwable); - } - } - - /** - * Sets the status. - * - * @param status The status to set. - * @param throwable The related error or exception. - * @param message The status message. - * @see Response#setStatus(Status, Throwable, String) - */ - public void setStatus(Status status, Throwable throwable, String message) { - if (getResponse() != null) { - getResponse().setStatus(status, throwable, message); - } - } - - /** - * Invoked when the list of allowed methods needs to be updated. The - * {@link #getAllowedMethods()} or the {@link #setAllowedMethods(Set)} methods - * should be used. The default implementation lists the annotated methods. - */ - public void updateAllowedMethods() { - getAllowedMethods().clear(); - List annotations = getAnnotations(); - - if (annotations != null) { - for (AnnotationInfo annotationInfo : annotations) { - if (annotationInfo instanceof MethodAnnotationInfo) { - MethodAnnotationInfo methodAnnotationInfo = (MethodAnnotationInfo) annotationInfo; - - if (!getAllowedMethods().contains(methodAnnotationInfo.getRestletMethod())) { - getAllowedMethods().add(methodAnnotationInfo.getRestletMethod()); - } - } - } - } - } - - /** - * Update the dimensions that were used for content negotiation. By default, it - * adds the {@link Dimension#CHARACTER_SET}, {@link Dimension#ENCODING}, - * {@link Dimension#LANGUAGE}and {@link Dimension#MEDIA_TYPE} constants. - */ - protected void updateDimensions() { - getDimensions().add(Dimension.CHARACTER_SET); - getDimensions().add(Dimension.ENCODING); - getDimensions().add(Dimension.LANGUAGE); - getDimensions().add(Dimension.MEDIA_TYPE); - } + } + + /** + * Apply a patch entity to the current representation of the resource retrieved by calling + * {@link #get()}. By default, the {@link ConverterService#applyPatch(Representation, + * Representation)} method is used and then the {@link #put(Representation)} method called. + * + * @param entity The patch entity to apply. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP PATCH method + */ + protected Representation patch(Representation entity) throws ResourceException { + AnnotationInfo annotationInfo; + try { + annotationInfo = getAnnotation(Method.PATCH); + if (annotationInfo != null) { + return doHandle(Method.PATCH, getQuery(), entity); + } else { + // Default implementation + return put(getConverterService().applyPatch(get(), entity)); + // doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + } catch (IOException e) { + throw new ResourceException(e); + } + } + + /** + * Apply a patch entity to the current representation of the resource retrieved by calling + * {@link #get()}. By default, the {@link ConverterService#applyPatch(Representation, + * Representation)} method is used and then the {@link #put(Representation, Variant)} method + * called. + * + * @param entity The patch entity to apply. + * @param variant The variant of the response entity. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP PATCH method + */ + protected Representation patch(Representation entity, Variant variant) + throws ResourceException { + Representation result = null; + + try { + if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else { + // Default implementation + result = put(getConverterService().applyPatch(get(), entity), variant); + // doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + } catch (IOException e) { + throw new ResourceException(e); + } + return result; + } + + /** + * Posts a representation to the resource at the target URI reference. This method is only + * invoked if content negotiation has been disabled as indicated by the {@link #isNegotiated()}, + * otherwise the {@link #post(Representation, Variant)} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. + * + * @param entity The posted entity. + * @return The optional response entity. + * @throws ResourceException + * @see #get(Variant) + * @see HTTP POST + * method + */ + protected Representation post(Representation entity) throws ResourceException { + return doHandle(Method.POST, getQuery(), entity); + } + + /** + * Posts a representation to the resource at the target URI reference. A variant parameter is + * passed to indicate which representation should be returned, if any.
+ *
+ * This method is only invoked if content negotiation has been enabled as indicated by the + * {@link #isNegotiated()}, otherwise the {@link #post(Representation)} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
+ * + * @param entity The posted entity. + * @param variant The variant of the response entity. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP POST + * method + */ + protected Representation post(Representation entity, Variant variant) throws ResourceException { + Representation result = null; + + if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + return result; + } + + /** + * Creates or updates a resource with the given representation as a new state to be stored. This + * method is only invoked if content negotiation has been disabled as indicated by the {@link + * #isNegotiated()}, otherwise the {@link #put(Representation, Variant)} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}. + * + * @param entity The representation to store. + * @return The optional result entity. + * @throws ResourceException + * @see HTTP PUT + * method + */ + protected Representation put(Representation entity) throws ResourceException { + return doHandle(Method.PUT, getQuery(), entity); + } + + /** + * Creates or updates a resource with the given representation as a new state to be stored. A + * variant parameter is passed to indicate which representation should be returned, if any.
+ *
+ * This method is only invoked if content negotiation has been enabled as indicated by the + * {@link #isNegotiated()}, otherwise the {@link #put(Representation)} method is invoked.
+ *
+ * The default behavior is to set the response status to {@link + * org.restlet.data.Status#CLIENT_ERROR_METHOD_NOT_ALLOWED}.
+ * + * @param representation The representation to store. + * @param variant The variant of the response entity. + * @return The optional result entity. + * @throws ResourceException + * @see #get(Variant) + * @see HTTP PUT + * method + */ + protected Representation put(Representation representation, Variant variant) + throws ResourceException { + Representation result = null; + + if (variant instanceof VariantInfo) { + result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + } + + return result; + } + + /** + * Permanently redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetRef The target URI reference. + */ + public void redirectPermanent(Reference targetRef) { + if (getResponse() != null) { + getResponse().redirectPermanent(targetRef); + } + } + + /** + * Permanently redirects the client to a target URI. The client is expected to reuse the same + * method for the new request.
+ *
+ * If you pass a relative target URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}. + * + * @param targetUri The target URI. + */ + public void redirectPermanent(String targetUri) { + if (getResponse() != null) { + getResponse().redirectPermanent(targetUri); + } + } + + /** + * Redirects the client to a different URI that SHOULD be retrieved using a GET method on that + * resource. This method exists primarily to allow the output of a POST-activated script to + * redirect the user agent to a selected resource. The new URI is not a substitute reference for + * the originally requested resource. + * + * @param targetRef The target reference. + */ + public void redirectSeeOther(Reference targetRef) { + if (getResponse() != null) { + getResponse().redirectSeeOther(targetRef); + } + } + + /** + * Redirects the client to a different URI that SHOULD be retrieved using a GET method on that + * resource. This method exists primarily to allow the output of a POST-activated script to + * redirect the user agent to a selected resource. The new URI is not a substitute reference for + * the originally requested resource.
+ *
+ * If you pass a relative target URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}. + * + * @param targetUri The target URI. + */ + public void redirectSeeOther(String targetUri) { + if (getResponse() != null) { + getResponse().redirectSeeOther(targetUri); + } + } + + /** + * Temporarily redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetRef The target reference. + */ + public void redirectTemporary(Reference targetRef) { + if (getResponse() != null) { + getResponse().redirectTemporary(targetRef); + } + } + + /** + * Temporarily redirects the client to a target URI. The client is expected to reuse the same + * method for the new request.
+ *
+ * If you pass a relative target URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}. + * + * @param targetUri The target URI. + */ + public void redirectTemporary(String targetUri) { + if (getResponse() != null) { + getResponse().redirectTemporary(targetUri); + } + } + + /** + * Sets the set of methods allowed on the requested resource. The set instance set must be + * thread-safe (use {@link CopyOnWriteArraySet} for example. + * + * @param allowedMethods The set of methods allowed on the requested resource. + * @see Response#setAllowedMethods(Set) + */ + public void setAllowedMethods(Set allowedMethods) { + if (getResponse() != null) { + getResponse().setAllowedMethods(allowedMethods); + } + } + + /** + * Indicates if annotations are supported. The default value is true. + * + * @param annotated Indicates if annotations are supported. + */ + public void setAnnotated(boolean annotated) { + this.annotated = annotated; + } + + /** + * Sets the response attribute value. + * + * @param name The attribute name. + * @param value The attribute to set. + */ + public void setAttribute(String name, Object value) { + getResponseAttributes().put(name, value); + } + + /** + * Indicates if the response should be automatically committed. + * + * @param autoCommitting True if the response should be automatically committed + */ + public void setAutoCommitting(boolean autoCommitting) { + getResponse().setAutoCommitting(autoCommitting); + } + + /** + * Sets the list of authentication requests sent by an origin server to a client. The list + * instance set must be thread-safe (use {@link CopyOnWriteArrayList} for example. + * + * @param requests The list of authentication requests sent by an origin server to a client. + * @see Response#setChallengeRequests(List) + */ + public void setChallengeRequests(List requests) { + if (getResponse() != null) { + getResponse().setChallengeRequests(requests); + } + } + + /** + * Indicates if the response has already been committed. + * + * @param committed True if the response has already been committed. + */ + public void setCommitted(boolean committed) { + getResponse().setCommitted(committed); + } + + /** + * Indicates if conditional handling is enabled. The default value is true. + * + * @param conditional True if conditional handling is enabled. + */ + public void setConditional(boolean conditional) { + this.conditional = conditional; + } + + /** + * Sets the cookie settings provided by the server. + * + * @param cookieSettings The cookie settings provided by the server. + * @see Response#setCookieSettings(Series) + */ + public void setCookieSettings(Series cookieSettings) { + if (getResponse() != null) { + getResponse().setCookieSettings(cookieSettings); + } + } + + /** + * Sets the description. + * + * @param description The description. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Sets the set of dimensions on which the response entity may vary. The set instance set must + * be thread-safe (use {@link CopyOnWriteArraySet} for example. + * + * @param dimensions The set of dimensions on which the response entity may vary. + * @see Response#setDimensions(Set) + */ + public void setDimensions(Set dimensions) { + if (getResponse() != null) { + getResponse().setDimensions(dimensions); + } + } + + /** + * Indicates if the identified resource exists. The default value is true. + * + * @param exists Indicates if the identified resource exists. + */ + public void setExisting(boolean exists) { + this.existing = exists; + } + + /** + * Sets the reference that the client should follow for redirections or resource creations. + * + * @param locationRef The reference to set. + * @see Response#setLocationRef(Reference) + */ + public void setLocationRef(Reference locationRef) { + if (getResponse() != null) { + getResponse().setLocationRef(locationRef); + } + } + + /** + * Sets the reference that the client should follow for redirections or resource creations. If + * you pass a relative location URI, it will be resolved with the current base reference of the + * request's resource reference (see {@link Request#getResourceRef()} and {@link + * Reference#getBaseRef()}). + * + * @param locationUri The URI to set. + * @see Response#setLocationRef(String) + */ + public void setLocationRef(String locationUri) { + if (getResponse() != null) { + getResponse().setLocationRef(locationUri); + } + } + + /** + * Sets the display name. + * + * @param name The display name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Indicates if content negotiation of response entities is enabled. The default value is true. + * + * @param negotiateContent True if content negotiation of response entities is enabled. + */ + public void setNegotiated(boolean negotiateContent) { + this.negotiated = negotiateContent; + } + + /** + * Sets the callback invoked after sending the response. + * + * @param onSentCallback The callback invoked after sending the response. + */ + public void setOnSent(Uniform onSentCallback) { + getResponse().setOnSent(onSentCallback); + } + + /** + * Sets the list of proxy authentication requests sent by an origin server to a client. The list + * instance set must be thread-safe (use {@link CopyOnWriteArrayList} for example. + * + * @param requests The list of proxy authentication requests sent by an origin server to a + * client. + * @see Response#setProxyChallengeRequests(List) + */ + public void setProxyChallengeRequests(List requests) { + if (getResponse() != null) { + getResponse().setProxyChallengeRequests(requests); + } + } + + /** + * Sets the server-specific information. + * + * @param serverInfo The server-specific information. + * @see Response#setServerInfo(ServerInfo) + */ + public void setServerInfo(ServerInfo serverInfo) { + if (getResponse() != null) { + getResponse().setServerInfo(serverInfo); + } + } + + /** + * Sets the status. + * + * @param status The status to set. + * @see Response#setStatus(Status) + */ + public void setStatus(Status status) { + if (getResponse() != null) { + getResponse().setStatus(status); + } + } + + /** + * Sets the status. + * + * @param status The status to set. + * @param message The status message. + * @see Response#setStatus(Status, String) + */ + public void setStatus(Status status, String message) { + if (getResponse() != null) { + getResponse().setStatus(status, message); + } + } + + /** + * Sets the status. + * + * @param status The status to set. + * @param throwable The related error or exception. + * @see Response#setStatus(Status, Throwable) + */ + public void setStatus(Status status, Throwable throwable) { + if (getResponse() != null) { + getResponse().setStatus(status, throwable); + } + } + + /** + * Sets the status. + * + * @param status The status to set. + * @param throwable The related error or exception. + * @param message The status message. + * @see Response#setStatus(Status, Throwable, String) + */ + public void setStatus(Status status, Throwable throwable, String message) { + if (getResponse() != null) { + getResponse().setStatus(status, throwable, message); + } + } + + /** + * Invoked when the list of allowed methods needs to be updated. The {@link + * #getAllowedMethods()} or the {@link #setAllowedMethods(Set)} methods should be used. The + * default implementation lists the annotated methods. + */ + public void updateAllowedMethods() { + getAllowedMethods().clear(); + List annotations = getAnnotations(); + + if (annotations != null) { + for (AnnotationInfo annotationInfo : annotations) { + if (annotationInfo instanceof final MethodAnnotationInfo methodAnnotationInfo) { + + if (!getAllowedMethods().contains(methodAnnotationInfo.getRestletMethod())) { + getAllowedMethods().add(methodAnnotationInfo.getRestletMethod()); + } + } + } + } + } + + /** + * Update the dimensions that were used for content negotiation. By default, it adds the {@link + * Dimension#CHARACTER_SET}, {@link Dimension#ENCODING}, {@link Dimension#LANGUAGE}and {@link + * Dimension#MEDIA_TYPE} constants. + */ + protected void updateDimensions() { + getDimensions().add(Dimension.CHARACTER_SET); + getDimensions().add(Dimension.ENCODING); + getDimensions().add(Dimension.LANGUAGE); + getDimensions().add(Dimension.MEDIA_TYPE); + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/Status.java b/org.restlet/src/main/java/org/restlet/resource/Status.java index 4fcd3ef539..c9968cd772 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Status.java +++ b/org.restlet/src/main/java/org/restlet/resource/Status.java @@ -1,37 +1,39 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** - * Annotation for {@link Throwable} that map to HTTP error statuses. Its - * semantics is equivalent to an HTTP status line plus a related HTTP entity for - * errors.
+ * Annotation for {@link Throwable} that map to HTTP error statuses. Its semantics are equivalent to + * an HTTP status line plus a related HTTP entity for errors.
*
* Example: - * + * *

  * @Get
  * public MyBean represent() throws MyServerError, MyNotFoundError;
- * 
+ *
  * @Status(500)
  * public class MyServerError implements Throwable{
  *    ...
  * }
- * 
+ *
  * @Status(404, serialize = false)
  * public class MyNotFoundError extends RuntimeException{
  *    ...
  * }
- * 
+ *
  * @Status(value = 400)
  * public class MyBadParameterError extends RuntimeException{
  *    public String getParameterName() {
@@ -40,7 +42,7 @@
  *    ...
  * }
  * 
- * + * * @author Jerome Louvel */ @Documented @@ -48,21 +50,19 @@ @Target(ElementType.TYPE) public @interface Status { - /** - * Specifies the HTTP status code associated to the annotated {@link Throwable}. - * Default is 500. - * - * @return The result HTTP status code. - */ - int value() default 500; - - /** - * Indicates if the annotated {@link Throwable} should be serialized in the HTTP - * response entity. - * - * @return True if {@link Throwable} should be serialized in the HTTP response - * entity. - */ - boolean serialize() default true; + /** + * Specifies the HTTP status code associated with the annotated {@link Throwable}. Default is + * 500. + * + * @return The result HTTP status code. + */ + int value() default 500; + /** + * Indicates if the annotated {@link Throwable} should be serialized in the HTTP response + * entity. + * + * @return True if {@link Throwable} should be serialized in the HTTP response entity. + */ + boolean serialize() default true; } diff --git a/org.restlet/src/main/java/org/restlet/routing/Extractor.java b/org.restlet/src/main/java/org/restlet/routing/Extractor.java index 0c23b7007d..6f4e316b97 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Extractor.java +++ b/org.restlet/src/main/java/org/restlet/routing/Extractor.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -18,246 +19,239 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - /** - * Filter extracting attributes from a call. Multiple extractions can be - * defined, based on the query string of the resource reference, on the request - * form (ex: posted from a browser) or on cookies.
+ * Filter extracting attributes from a call. Multiple extractions can be defined, based on the query + * string of the resource reference, on the request form (ex: posted from a browser) or on cookies. *
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + *
+ * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class Extractor extends Filter { - /** Internal class holding extraction information. */ - private static final class ExtractInfo { - /** Target attribute name. */ - protected String attribute; - - /** Indicates how to handle repeating values. */ - protected boolean first; + /** Internal class holding extraction information. */ + private static final class ExtractInfo { + /** Target attribute name. */ + protected String attribute; - /** Name of the parameter to look for. */ - protected String parameter; + /** Indicates how to handle repeating values. */ + protected boolean first; - /** - * Constructor. - * - * @param attribute Target attribute name. - * @param parameter Name of the parameter to look for. - * @param first Indicates how to handle repeating values. - */ - public ExtractInfo(String attribute, String parameter, boolean first) { - this.attribute = attribute; - this.parameter = parameter; - this.first = first; - } - } + /** Name of the parameter to look for. */ + protected String parameter; - /** The list of cookies to extract. */ - private volatile List cookieExtracts; + /** + * Constructor. + * + * @param attribute Target attribute name. + * @param parameter Name of the parameter to look for. + * @param first Indicates how to handle repeating values. + */ + public ExtractInfo(String attribute, String parameter, boolean first) { + this.attribute = attribute; + this.parameter = parameter; + this.first = first; + } + } - /** The list of request entity parameters to extract. */ - private volatile List entityExtracts; + /** The list of cookies to extract. */ + private volatile List cookieExtracts; - /** The list of query parameters to extract. */ - private volatile List queryExtracts; + /** The list of request entity parameters to extract. */ + private volatile List entityExtracts; - /** - * Constructor. - */ - public Extractor() { - this(null); - } + /** The list of query parameters to extract. */ + private volatile List queryExtracts; - /** - * Constructor. - * - * @param context The context. - */ - public Extractor(Context context) { - this(context, null); - } + /** Constructor. */ + public Extractor() { + this(null); + } - /** - * Constructor. - * - * @param context The context. - * @param next The next Restlet. - */ - public Extractor(Context context, Restlet next) { - super(context, next); - } + /** + * Constructor. + * + * @param context The context. + */ + public Extractor(Context context) { + this(context, null); + } - /** - * Allows filtering before its handling by the target Restlet. By default it - * extracts the attributes from form parameters (query, cookies, entity) and - * finally puts them in the request's attributes ( - * {@link Request#getAttributes()}). - * - * @param request The request to filter. - * @param response The response to filter. - * @return The continuation status. - */ - @Override - protected int beforeHandle(Request request, Response response) { - // Extract the query parameters - if (!getQueryExtracts().isEmpty()) { - Form form = request.getResourceRef().getQueryAsForm(); + /** + * Constructor. + * + * @param context The context. + * @param next The next Restlet. + */ + public Extractor(Context context, Restlet next) { + super(context, next); + } - if (form != null) { - for (ExtractInfo ei : getQueryExtracts()) { - if (ei.first) { - String value = form.getFirstValue(ei.parameter); + /** + * Allows filtering before its handling by the target Restlet. By default, it extracts the + * attributes from form parameters (query, cookies, entity) and finally puts them in the + * request's attributes ( {@link Request#getAttributes()}). + * + * @param request The request to filter. + * @param response The response to filter. + * @return The continuation status. + */ + @Override + protected int beforeHandle(Request request, Response response) { + // Extract the query parameters + if (!getQueryExtracts().isEmpty()) { + Form form = request.getResourceRef().getQueryAsForm(); - if (value != null) { - request.getAttributes().put(ei.attribute, value); - } - } else { - request.getAttributes().put(ei.attribute, form.subList(ei.parameter)); - } - } - } - } + if (form != null) { + for (ExtractInfo ei : getQueryExtracts()) { + if (ei.first) { + String value = form.getFirstValue(ei.parameter); - // Extract the request entity parameters - if (!getEntityExtracts().isEmpty()) { - Representation entity = request.getEntity(); - if (entity != null) { - Form form = new Form(entity); + if (value != null) { + request.getAttributes().put(ei.attribute, value); + } + } else { + request.getAttributes().put(ei.attribute, form.subList(ei.parameter)); + } + } + } + } - for (ExtractInfo ei : getEntityExtracts()) { - if (ei.first) { - String value = form.getFirstValue(ei.parameter); + // Extract the request entity parameters + if (!getEntityExtracts().isEmpty()) { + Representation entity = request.getEntity(); + if (entity != null) { + Form form = new Form(entity); - if (value != null) { - request.getAttributes().put(ei.attribute, value); - } - } else { - request.getAttributes().put(ei.attribute, form.subList(ei.parameter)); - } - } - } - } + for (ExtractInfo ei : getEntityExtracts()) { + if (ei.first) { + String value = form.getFirstValue(ei.parameter); - // Extract the cookie parameters - if (!getCookieExtracts().isEmpty()) { - Series cookies = request.getCookies(); + if (value != null) { + request.getAttributes().put(ei.attribute, value); + } + } else { + request.getAttributes().put(ei.attribute, form.subList(ei.parameter)); + } + } + } + } - if (cookies != null) { - for (ExtractInfo ei : getCookieExtracts()) { - if (ei.first) { - String value = cookies.getFirstValue(ei.parameter); + // Extract the cookie parameters + if (!getCookieExtracts().isEmpty()) { + Series cookies = request.getCookies(); - if (value != null) { - request.getAttributes().put(ei.attribute, value); - } - } else { - request.getAttributes().put(ei.attribute, cookies.subList(ei.parameter)); - } - } - } - } + if (cookies != null) { + for (ExtractInfo ei : getCookieExtracts()) { + if (ei.first) { + String value = cookies.getFirstValue(ei.parameter); - return CONTINUE; - } + if (value != null) { + request.getAttributes().put(ei.attribute, value); + } + } else { + request.getAttributes().put(ei.attribute, cookies.subList(ei.parameter)); + } + } + } + } - /** - * Extracts an attribute from the request cookies. - * - * @param attribute The name of the request attribute to set. - * @param cookieName The name of the cookies to extract. - * @param first Indicates if only the first cookie should be set. Otherwise - * as a List instance might be set in the attribute value. - */ - public void extractFromCookie(String attribute, String cookieName, boolean first) { - getCookieExtracts().add(new ExtractInfo(attribute, cookieName, first)); - } + return CONTINUE; + } - /** - * Extracts an attribute from the request entity form. - * - * @param attribute The name of the request attribute to set. - * @param parameter The name of the entity form parameter to extract. - * @param first Indicates if only the first cookie should be set. Otherwise - * as a List instance might be set in the attribute value. - */ - public void extractFromEntity(String attribute, String parameter, boolean first) { - getEntityExtracts().add(new ExtractInfo(attribute, parameter, first)); - } + /** + * Extracts an attribute from the request cookies. + * + * @param attribute The name of the request attribute to set. + * @param cookieName The name of the cookies to extract. + * @param first Indicates if only the first cookie should be set. Otherwise, as a List instance + * might be set in the attribute value. + */ + public void extractFromCookie(String attribute, String cookieName, boolean first) { + getCookieExtracts().add(new ExtractInfo(attribute, cookieName, first)); + } - /** - * Extracts an attribute from the query string of the resource reference. - * - * @param attribute The name of the request attribute to set. - * @param parameter The name of the query string parameter to extract. - * @param first Indicates if only the first cookie should be set. Otherwise - * as a List instance might be set in the attribute value. - */ - public void extractFromQuery(String attribute, String parameter, boolean first) { - getQueryExtracts().add(new ExtractInfo(attribute, parameter, first)); - } + /** + * Extracts an attribute from the request entity form. + * + * @param attribute The name of the request attribute to set. + * @param parameter The name of the entity form parameter to extract. + * @param first Indicates if only the first cookie should be set. Otherwise, as a List instance + * might be set in the attribute value. + */ + public void extractFromEntity(String attribute, String parameter, boolean first) { + getEntityExtracts().add(new ExtractInfo(attribute, parameter, first)); + } - /** - * Returns the list of query extracts. - * - * @return The list of query extracts. - */ - private List getCookieExtracts() { - // Lazy initialization with double-check. - List ce = this.cookieExtracts; - if (ce == null) { - synchronized (this) { - ce = this.cookieExtracts; - if (ce == null) { - this.cookieExtracts = ce = new CopyOnWriteArrayList(); - } - } - } - return ce; - } + /** + * Extracts an attribute from the query string of the resource reference. + * + * @param attribute The name of the request attribute to set. + * @param parameter The name of the query string parameter to extract. + * @param first Indicates if only the first cookie should be set. Otherwise, as a List instance + * might be set in the attribute value. + */ + public void extractFromQuery(String attribute, String parameter, boolean first) { + getQueryExtracts().add(new ExtractInfo(attribute, parameter, first)); + } - /** - * Returns the list of query extracts. - * - * @return The list of query extracts. - */ - private List getEntityExtracts() { - // Lazy initialization with double-check. - List ee = this.entityExtracts; - if (ee == null) { - synchronized (this) { - ee = this.entityExtracts; - if (ee == null) { - this.entityExtracts = ee = new CopyOnWriteArrayList(); - } - } - } - return ee; - } + /** + * Returns the list of query extracts. + * + * @return The list of query extracts. + */ + private List getCookieExtracts() { + // Lazy initialization with double-check. + List ce = this.cookieExtracts; + if (ce == null) { + synchronized (this) { + ce = this.cookieExtracts; + if (ce == null) { + this.cookieExtracts = ce = new CopyOnWriteArrayList<>(); + } + } + } + return ce; + } - /** - * Returns the list of query extracts. - * - * @return The list of query extracts. - */ - private List getQueryExtracts() { - // Lazy initialization with double-check. - List qe = this.queryExtracts; - if (qe == null) { - synchronized (this) { - qe = this.queryExtracts; - if (qe == null) { - this.queryExtracts = qe = new CopyOnWriteArrayList(); - } - } - } - return qe; - } + /** + * Returns the list of query extracts. + * + * @return The list of query extracts. + */ + private List getEntityExtracts() { + // Lazy initialization with double-check. + List ee = this.entityExtracts; + if (ee == null) { + synchronized (this) { + ee = this.entityExtracts; + if (ee == null) { + this.entityExtracts = ee = new CopyOnWriteArrayList<>(); + } + } + } + return ee; + } + /** + * Returns the list of query extracts. + * + * @return The list of query extracts. + */ + private List getQueryExtracts() { + // Lazy initialization with double-check. + List qe = this.queryExtracts; + if (qe == null) { + synchronized (this) { + qe = this.queryExtracts; + if (qe == null) { + this.queryExtracts = qe = new CopyOnWriteArrayList<>(); + } + } + } + return qe; + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/Filter.java b/org.restlet/src/main/java/org/restlet/routing/Filter.java index 500bec7cb3..1eecdf4242 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Filter.java +++ b/org.restlet/src/main/java/org/restlet/routing/Filter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; import org.restlet.Context; @@ -18,232 +17,222 @@ import org.restlet.resource.ServerResource; /** - * Restlet filtering calls before passing them to an attached Restlet. The - * purpose is to do some pre-processing or post-processing on the calls going - * through it before or after they are actually handled by an attached Restlet. - * Also note that you can attach and detach targets while handling incoming - * calls as the filter is ensured to be thread-safe.
+ * Restlet filtering calls before passing them to an attached Restlet. The purpose is to do some + * pre-processing or post-processing on the calls going through it before or after they are actually + * handled by an attached Restlet. Also note that you can attach and detach targets while handling + * incoming calls as the filter is ensured to be thread-safe.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public abstract class Filter extends Restlet { - /** - * Indicates that the request processing should continue normally. If returned - * from the {@link #beforeHandle(Request, Response)} method, the filter then - * invokes the {@link #doHandle(Request, Response)} method. If returned from the - * {@link #doHandle(Request, Response)} method, the filter then invokes the - * {@link #afterHandle(Request, Response)} method. - */ - public static final int CONTINUE = 0; - - /** - * Indicates that after the {@link #beforeHandle(Request, Response)} method, the - * request processing should skip the {@link #doHandle(Request, Response)} - * method to continue with the {@link #afterHandle(Request, Response)} method. - */ - public static final int SKIP = 1; - - /** - * Indicates that the request processing should stop and return the current - * response from the filter. - */ - public static final int STOP = 2; - - /** The next Restlet. */ - private volatile Restlet next; - - /** - * Constructor. - */ - public Filter() { - this(null); - } - - /** - * Constructor. - * - * @param context The context. - */ - public Filter(Context context) { - this(context, null); - } - - /** - * Constructor. - * - * @param context The context. - * @param next The next Restlet. - */ - public Filter(Context context, Restlet next) { - super(context); - this.next = next; - } - - /** - * Allows filtering after processing by the next Restlet. Does nothing by - * default. - * - * @param request The request to handle. - * @param response The response to update. - */ - protected void afterHandle(Request request, Response response) { - // To be overriden - } - - /** - * Allows filtering before processing by the next Restlet. Returns - * {@link #CONTINUE} by default. - * - * @param request The request to handle. - * @param response The response to update. - * @return The continuation status. Either {@link #CONTINUE} or {@link #SKIP} or - * {@link #STOP}. - */ - protected int beforeHandle(Request request, Response response) { - return CONTINUE; - } - - /** - * Handles the call by distributing it to the next Restlet. If no Restlet is - * attached, then a {@link Status#SERVER_ERROR_INTERNAL} status is returned. - * Returns {@link #CONTINUE} by default. - * - * @param request The request to handle. - * @param response The response to update. - * @return The continuation status. Either {@link #CONTINUE} or {@link #STOP}. - */ - protected int doHandle(Request request, Response response) { - final int result = CONTINUE; - - if (getNext() != null) { - getNext().handle(request, response); - - // Re-associate the response to the current thread - Response.setCurrent(response); - - // Associate the context to the current thread - if (getContext() != null) { - Context.setCurrent(getContext()); - } - } else { - response.setStatus(Status.SERVER_ERROR_INTERNAL); - getLogger().warning("The filter " + getName() + " was executed without a next Restlet attached to it."); - } - - return result; - } - - /** - * Returns the next Restlet. - * - * @return The next Restlet or null. - */ - public Restlet getNext() { - return this.next; - } - - /** - * Handles a call by first invoking the beforeHandle() method for pre-filtering, - * then distributing the call to the next Restlet via the doHandle() method. - * When the handling is completed, it finally invokes the afterHandle() method - * for post-filtering. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public final void handle(Request request, Response response) { - super.handle(request, response); - - switch (beforeHandle(request, response)) { - case CONTINUE: - // Stop the processing - if (doHandle(request, response) == CONTINUE) { + /** + * Indicates that the request processing should continue normally. If returned from the {@link + * #beforeHandle(Request, Response)} method, the filter then invokes the {@link + * #doHandle(Request, Response)} method. If returned from the {@link #doHandle(Request, + * Response)} method, the filter then invokes the {@link #afterHandle(Request, Response)} + * method. + */ + public static final int CONTINUE = 0; + + /** + * Indicates that after the {@link #beforeHandle(Request, Response)} method, the request + * processing should skip the {@link #doHandle(Request, Response)} method to continue with the + * {@link #afterHandle(Request, Response)} method. + */ + public static final int SKIP = 1; + + /** + * Indicates that the request processing should stop and return the current response from the + * filter. + */ + public static final int STOP = 2; + + /** The next Restlet. */ + private volatile Restlet next; + + /** Constructor. */ + public Filter() { + this(null); + } + + /** + * Constructor. + * + * @param context The context. + */ + public Filter(Context context) { + this(context, null); + } + + /** + * Constructor. + * + * @param context The context. + * @param next The next Restlet. + */ + public Filter(Context context, Restlet next) { + super(context); + this.next = next; + } + + /** + * Allows filtering after processing by the next Restlet. Does nothing by default. + * + * @param request The request to handle. + * @param response The response to update. + */ + protected void afterHandle(Request request, Response response) { + // To be overriden + } + + /** + * Allows filtering before processing by the next Restlet. Returns {@link #CONTINUE} by default. + * + * @param request The request to handle. + * @param response The response to update. + * @return The continuation status. Either {@link #CONTINUE} or {@link #SKIP} or {@link #STOP}. + */ + protected int beforeHandle(Request request, Response response) { + return CONTINUE; + } + + /** + * Handles the call by distributing it to the next Restlet. If no Restlet is attached, then a + * {@link Status#SERVER_ERROR_INTERNAL} status is returned. Returns {@link #CONTINUE} by + * default. + * + * @param request The request to handle. + * @param response The response to update. + * @return The continuation status. Either {@link #CONTINUE} or {@link #STOP}. + */ + protected int doHandle(Request request, Response response) { + final int result = CONTINUE; + + if (getNext() != null) { + getNext().handle(request, response); + + // Re-associate the response to the current thread + Response.setCurrent(response); + + // Associate the context to the current thread + if (getContext() != null) { + Context.setCurrent(getContext()); + } + } else { + response.setStatus(Status.SERVER_ERROR_INTERNAL); + getLogger() + .warning( + "The filter " + + getName() + + " was executed without a next Restlet attached to it."); + } + + return result; + } + + /** + * Returns the next Restlet. + * + * @return The next Restlet or null. + */ + public Restlet getNext() { + return this.next; + } + + /** + * Handles a call by first invoking the beforeHandle() method for pre-filtering, then + * distributing the call to the next Restlet via the doHandle() method. When the handling is + * completed, it finally invokes the afterHandle() method for post-filtering. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public final void handle(Request request, Response response) { + super.handle(request, response); + + switch (beforeHandle(request, response)) { + case CONTINUE: + // Stop the processing + if (doHandle(request, response) == CONTINUE) { + afterHandle(request, response); + } + break; + + case SKIP: afterHandle(request, response); + break; + + default: + // Stop the processing + break; + } + } + + /** + * Indicates if there is a next Restlet. + * + * @return True if there is a next Restlet. + */ + public boolean hasNext() { + return getNext() != null; + } + + /** + * Sets the next {@link Restlet} as a {@link Finder} for a given {@link ServerResource} class. + * When the call is delegated to the {@link Finder} instance, a new instance of the resource + * class will be created and will actually handle the request. + * + * @param targetClass The target resource class to attach. + */ + public void setNext(Class targetClass) { + setNext(createFinder(targetClass)); + } + + /** + * Sets the next Restlet. + * + *

In addition, this method will set the context of the next Restlet if it is null by passing + * a reference to its own context. + * + * @param next The next Restlet. + */ + public void setNext(Restlet next) { + if ((next != null) && (next.getContext() == null)) { + next.setContext(getContext()); + } + + this.next = next; + } + + /** Starts the filter and the next Restlet if attached. */ + @Override + public synchronized void start() throws Exception { + if (isStopped()) { + if (getNext() != null) { + getNext().start(); } - break; - - case SKIP: - afterHandle(request, response); - break; - - default: - // Stop the processing - break; - } - - } - - /** - * Indicates if there is a next Restlet. - * - * @return True if there is a next Restlet. - */ - public boolean hasNext() { - return getNext() != null; - } - - /** - * Sets the next {@link Restlet} as a {@link Finder} for a given - * {@link ServerResource} class. When the call is delegated to the - * {@link Finder} instance, a new instance of the resource class will be created - * and will actually handle the request. - * - * @param targetClass The target resource class to attach. - */ - public void setNext(Class targetClass) { - setNext(createFinder(targetClass)); - } - - /** - * Sets the next Restlet. - * - * In addition, this method will set the context of the next Restlet if it is - * null by passing a reference to its own context. - * - * @param next The next Restlet. - */ - public void setNext(Restlet next) { - if ((next != null) && (next.getContext() == null)) { - next.setContext(getContext()); - } - - this.next = next; - } - - /** - * Starts the filter and the next Restlet if attached. - */ - @Override - public synchronized void start() throws Exception { - if (isStopped()) { - if (getNext() != null) { - getNext().start(); - } - - // Must be invoked as a last step - super.start(); - } - } - - /** - * Stops the filter and the next Restlet if attached. - */ - @Override - public synchronized void stop() throws Exception { - if (isStarted()) { - // Must be invoked as a first step - super.stop(); - - if (getNext() != null) { - getNext().stop(); - } - } - } + // Must be invoked as a last step + super.start(); + } + } + + /** Stops the filter and the next Restlet if attached. */ + @Override + public synchronized void stop() throws Exception { + if (isStarted()) { + // Must be invoked as a first step + super.stop(); + + if (getNext() != null) { + getNext().stop(); + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/Redirector.java b/org.restlet/src/main/java/org/restlet/routing/Redirector.java index 1fcf7257bf..6f41791349 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Redirector.java +++ b/org.restlet/src/main/java/org/restlet/routing/Redirector.java @@ -1,15 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; -import org.restlet.*; +import java.util.logging.Level; +import org.restlet.Application; +import org.restlet.Component; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.Reference; import org.restlet.data.Status; import org.restlet.engine.header.HeaderConstants; @@ -17,469 +22,461 @@ import org.restlet.representation.Representation; import org.restlet.util.Resolver; -import java.util.logging.Level; - /** - * Rewrites URIs then redirects the call or the client to a new destination. - * There are various redirection modes that you can choose from: client-side - * redirections ({@link #MODE_CLIENT_FOUND}, {@link #MODE_CLIENT_PERMANENT}, - * {@link #MODE_CLIENT_SEE_OTHER}, {@link #MODE_CLIENT_TEMPORARY}) or - * server-side redirections, similar to a reverse proxy ( - * {@link #MODE_SERVER_OUTBOUND} and {@link #MODE_SERVER_INBOUND}).
+ * Rewrites URIs then redirects the call or the client to a new destination. There are various + * redirection modes that you can choose from: client-side redirections ({@link #MODE_CLIENT_FOUND}, + * {@link #MODE_CLIENT_PERMANENT}, {@link #MODE_CLIENT_SEE_OTHER}, {@link #MODE_CLIENT_TEMPORARY}) + * or server-side redirections, similar to a reverse proxy ( {@link #MODE_SERVER_OUTBOUND} and + * {@link #MODE_SERVER_INBOUND}).
*
- * When setting the redirection URIs, you can also used special URI variables to - * reuse most properties from the original request as well as URI template - * variables. For a complete list of properties, please see the {@link Resolver} - * class. For example "/target?referer={fi}" would redirect to the relative URI, - * inserting the referrer URI as a query parameter.
+ * When setting the redirection URIs, you can also use special URI variables to reuse most + * properties from the original request as well as URI template variables. For a complete list of + * properties, please see the {@link Resolver} class. For example, "/target?referer={fi}" would + * redirect to the relative URI, inserting the referrer URI as a query parameter.
*
- * To create a reverse proxy, a typically configuration will use the - * {@link #MODE_SERVER_OUTBOUND} constant and a target URI like - * "http://targetHost/targetRootPath/{rr}" to ensure that all child URIs are - * properly redirected as well, "rr" appending the remaining part of the current - * request URI that hasn't been routed yet.
+ * To create a reverse proxy, a typical configuration will use the {@link #MODE_SERVER_OUTBOUND} + * constant and a target URI like "http://targetHost/targetRootPath/{rr}" to ensure that all child + * URIs are properly redirected as well, "rr" appending the remaining part of the current request + * URI that hasn't been routed yet.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @see org.restlet.routing.Template * @author Jerome Louvel */ public class Redirector extends Restlet { - /** - * In this mode, the client is simply redirected to the URI generated from the - * target URI pattern using the {@link Status#REDIRECTION_FOUND} status. Note: - * this is a client-side redirection.
- * - * @see Status#REDIRECTION_FOUND - */ - public static final int MODE_CLIENT_FOUND = 2; - - /** - * In this mode, the client is permanently redirected to the URI generated from - * the target URI pattern, using the {@link Status#REDIRECTION_PERMANENT} - * status. Note: this is a client-side redirection.
- * - * @see Status#REDIRECTION_PERMANENT - */ - public static final int MODE_CLIENT_PERMANENT = 1; - - /** - * In this mode, the client is simply redirected to the URI generated from the - * target URI pattern using the {@link Status#REDIRECTION_SEE_OTHER} status. - * Note: this is a client-side redirection.
- * - * @see Status#REDIRECTION_SEE_OTHER - */ - public static final int MODE_CLIENT_SEE_OTHER = 3; - - /** - * In this mode, the client is temporarily redirected to the URI generated from - * the target URI pattern using the {@link Status#REDIRECTION_TEMPORARY} status. - * Note: this is a client-side redirection.
- * - * @see Status#REDIRECTION_TEMPORARY - */ - public static final int MODE_CLIENT_TEMPORARY = 4; - - /** - * In this mode, the call is sent to {@link Context#getServerDispatcher()}. Once - * the selected client connector has completed the request handling, the - * response is normally returned to the client. In this case, you can view the - * Redirector as acting as a transparent proxy Restlet. Note: this is a - * server-side redirection.
- *
- * Warning: remember to add the required connectors to the parent - * {@link Component} and to declare them in the list of required connectors on - * the {@link Application#getConnectorService()} property.
- *
- * Note that in this mode, the headers of HTTP requests, stored in the request's - * attributes, are removed before dispatching. Also, when a HTTP response comes - * back the headers are also removed. You can control this behavior by setting - * the {@link #headersCleaning} attribute or by overriding the - * {@link #rewrite(Request)} or {@link #rewrite(Response)}. - * - * @see Context#getServerDispatcher() - */ - public static final int MODE_SERVER_INBOUND = 7; - - /** - * In this mode, the call is sent to {@link Application#getOutboundRoot()} or if - * null to {@link Context#getClientDispatcher()}. Once the selected client - * connector has completed the request handling, the response is normally - * returned to the client. In this case, you can view the {@link Redirector} as - * acting as a transparent server-side proxy. Note: this is a server-side - * redirection.
- *
- * Warning: remember to add the required connectors to the parent - * {@link Component} and to declare them in the list of required connectors on - * the {@link Application#getConnectorService()} property.
- *
- * Note that in this mode, the headers of HTTP requests, stored in the request's - * attributes, are removed before dispatching. Also, when a HTTP response comes - * back the headers are also removed. You can control this behavior by setting - * the {@link #headersCleaning} attribute or by overriding the - * {@link #rewrite(Request)} or {@link #rewrite(Response)}. - * - * @see Application#getOutboundRoot() - * @see Context#getClientDispatcher() - */ - public static final int MODE_SERVER_OUTBOUND = 6; - - /** - * Indicates if all headers of HTTP requests stored in the request's attributes, - * must be removed before the redirection. If set to true, it removes all - * headers, otherwise it keeps only the extension (or non HTTP standard) headers - */ - protected volatile boolean headersCleaning; - - /** The redirection mode. */ - protected volatile int mode; - - /** The target URI pattern. */ - protected volatile String targetTemplate; - - /** - * Constructor for the client dispatcher mode. - * - * @param context The context. - * @param targetTemplate The template to build the target URI. - * @see org.restlet.routing.Template - */ - public Redirector(Context context, String targetTemplate) { - this(context, targetTemplate, MODE_SERVER_OUTBOUND); - } - - /** - * Constructor. - * - * @param context The context. - * @param targetPattern The pattern to build the target URI (using - * StringTemplate syntax and the CallModel for variables). - * @param mode The redirection mode. - */ - public Redirector(Context context, String targetPattern, int mode) { - super(context); - this.targetTemplate = targetPattern; - this.mode = mode; - this.headersCleaning = true; - } - - /** - * Computes the new location of the given reference, after applying the - * redirection template. Returns null in case it cannot compute the new - * reference. - * - * @param locationRef The reference to translate. - * @param request The current request. - * @return The new location of the given reference. - */ - private String getLocation(Reference locationRef, Request request) { - Reference resourceRef = request.getResourceRef(); - Reference baseRef = resourceRef.getBaseRef(); - - Template rt = new Template(this.targetTemplate); - rt.setLogger(getLogger()); - int matched = rt.parse(locationRef.toString(), request); - - if (matched > 0) { - String remainingPart = (String) request.getAttributes().get("rr"); - - if (remainingPart != null) { - return baseRef.toString() + remainingPart; - } - } - - return null; - } - - /** - * Returns the redirection mode. - * - * @return The redirection mode. - */ - public int getMode() { - return this.mode; - } - - /** - * Returns the target reference to redirect to by automatically resolving URI - * template variables found using the {@link Template} class using the request - * and response as data models. - * - * @param request The request to handle. - * @param response The response to update. - * @return The target reference to redirect to. - */ - protected Reference getTargetRef(Request request, Response response) { - // Create the template - Template rt = new Template(this.targetTemplate); - rt.setLogger(getLogger()); - - // Return the formatted target URI - if (new Reference(this.targetTemplate).isRelative()) { - // Be sure to keep the resource's base reference. - return new Reference(request.getResourceRef(), rt.format(request, response)); - } - - return new Reference(rt.format(request, response)); - } - - /** - * Returns the target URI pattern. - * - * @return The target URI pattern. - */ - public String getTargetTemplate() { - return this.targetTemplate; - } - - /** - * Handles a call by redirecting using the selected redirection mode. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public void handle(Request request, Response response) { - // Generate the target reference - Reference targetRef = getTargetRef(request, response); - - switch (this.mode) { - case MODE_CLIENT_PERMANENT: - if (request.isLoggable()) { - getLogger().log(Level.FINE, "Permanently redirecting client to: " + targetRef); - } - - response.redirectPermanent(targetRef); - break; - - case MODE_CLIENT_FOUND: - if (request.isLoggable()) { - getLogger().log(Level.FINE, "Redirecting client to found location: " + targetRef); - } - - response.setLocationRef(targetRef); - response.setStatus(Status.REDIRECTION_FOUND); - break; - - case MODE_CLIENT_SEE_OTHER: - if (request.isLoggable()) { - getLogger().log(Level.FINE, "Redirecting client to another location: " + targetRef); - } - - response.redirectSeeOther(targetRef); - break; - - case MODE_CLIENT_TEMPORARY: - if (request.isLoggable()) { - getLogger().log(Level.FINE, "Temporarily redirecting client to: " + targetRef); - } - - response.redirectTemporary(targetRef); - break; - - case MODE_SERVER_OUTBOUND: - if (request.isLoggable()) { - getLogger().log(Level.FINE, "Redirecting via client dispatcher to: " + targetRef); - } - - outboundServerRedirect(targetRef, request, response); - break; - - case MODE_SERVER_INBOUND: - if (request.isLoggable()) { - getLogger().log(Level.FINE, "Redirecting via server dispatcher to: " + targetRef); - } - - inboundServerRedirect(targetRef, request, response); - break; - } - } - - /** - * Redirects a given call to a target reference. In the default implementation, - * the request HTTP headers, stored in the request's attributes, are removed - * before dispatching. After dispatching, the response HTTP headers are also - * removed to prevent conflicts with the main call. - * - * @param targetRef The target reference with URI variables resolved. - * @param request The request to handle. - * @param response The response to update. - */ - protected void inboundServerRedirect(Reference targetRef, Request request, Response response) { - serverRedirect(getContext().getServerDispatcher(), targetRef, request, response); - } - - /** - * Indicates if the headers must be cleaned. - * - * @return True if the headers must be cleaned. - */ - public boolean isHeadersCleaning() { - return headersCleaning; - } - - /** - * Redirects a given call to a target reference. In the default implementation, - * the request HTTP headers, stored in the request's attributes, are removed - * before dispatching. After dispatching, the response HTTP headers are also - * removed to prevent conflicts with the main call. - * - * @param targetRef The target reference with URI variables resolved. - * @param request The request to handle. - * @param response The response to update. - */ - protected void outboundServerRedirect(Reference targetRef, Request request, Response response) { - Restlet next = (getApplication() == null) ? null : getApplication().getOutboundRoot(); - - if (next == null) { - next = getContext().getClientDispatcher(); - } - - serverRedirect(next, targetRef, request, response); - if (response.getEntity() != null - && !request.getResourceRef().getScheme().equalsIgnoreCase(targetRef.getScheme())) { - // Distinct protocol, this data cannot be exposed. - response.getEntity().setLocationRef((Reference) null); - } - } - - /** - * Optionally rewrites the response entity returned in the - * {@link #MODE_SERVER_INBOUND} and {@link #MODE_SERVER_OUTBOUND} modes. By - * default, it just returns the initial entity without any modification. - * - * @param initialEntity The initial entity returned. - * @return The rewritten entity. - */ - protected Representation rewrite(Representation initialEntity) { - return initialEntity; - } - - /** - * Optionally updates the request sent in the {@link #MODE_SERVER_INBOUND} and - * {@link #MODE_SERVER_OUTBOUND} modes. By default, it leverages the - * {@link #headersCleaning} attribute in order to clean the headers: if set to - * true, it removes all headers, otherwise it keeps only the extension (or non - * HTTP standard) headers
- * - * @param initialRequest The initial request returned. - */ - protected void rewrite(Request initialRequest) { - if (isHeadersCleaning()) { - initialRequest.getAttributes().remove(HeaderConstants.ATTRIBUTE_HEADERS); - } else { - HeaderUtils.keepExtensionHeadersOnly(initialRequest); - } - } - - /** - * Optionally updates the response sent in the {@link #MODE_SERVER_INBOUND} and - * {@link #MODE_SERVER_OUTBOUND} modes. By default, it leverages the - * {@link #headersCleaning} attribute in order to clean the headers: if set to - * true, it removes all headers, otherwise it keeps only the extension (or non - * HTTP standard) headers
- * - * @param initialResponse The initial response returned. - */ - protected void rewrite(Response initialResponse) { - if (isHeadersCleaning()) { - initialResponse.getAttributes().remove(HeaderConstants.ATTRIBUTE_HEADERS); - } else { - HeaderUtils.keepExtensionHeadersOnly(initialResponse); - } - } - - /** - * Rewrite the location of the response, and the Location of the entity, if any. - * - * @param request The request to handle. - * @param response The response to update. - */ - public void rewriteLocation(Request request, Response response) { - if (response.getLocationRef() != null) { - Reference locationRef = response.getLocationRef(); - - String newLocation = getLocation(locationRef, request); - if (newLocation != null) { - response.setLocationRef(newLocation); - } - } - if (response.getEntity() != null && response.getEntity().getLocationRef() != null) { - Reference locationRef = response.getEntity().getLocationRef(); - - String newLocation = getLocation(locationRef, request); - if (newLocation != null) { - response.getEntity().setLocationRef(newLocation); - } - } - } - - /** - * Redirects a given call on the server-side to a next Restlet with a given - * target reference. In the default implementation, the request HTTP headers, - * stored in the request's attributes, are removed before dispatching. After - * dispatching, the response HTTP headers are also removed to prevent conflicts - * with the main call. - * - * @param next The next Restlet to forward the call to. - * @param targetRef The target reference with URI variables resolved. - * @param request The request to handle. - * @param response The response to update. - */ - protected void serverRedirect(Restlet next, Reference targetRef, Request request, Response response) { - if (next == null) { - getLogger().warning("No next Restlet provided for server redirection to " + targetRef); - } else { - // Save the base URI if it exists as we might need it for - // redirections - Reference resourceRef = request.getResourceRef(); - - // Reset the protocol and let the dispatcher handle the protocol - request.setProtocol(null); - - // Update the request to cleanly go to the target URI - request.setResourceRef(targetRef); - rewrite(request); - next.handle(request, response); - - request.setResourceRef(resourceRef); - // Allow for response rewriting and clean the headers - response.setEntity(rewrite(response.getEntity())); - rewrite(response); - - // In case of redirection, we may have to rewrite the redirect URI - rewriteLocation(request, response); - } - } - - /** - * Indicates if the headers must be cleaned. - * - * @param headersCleaning True if the headers must be cleaned. - */ - public void setHeadersCleaning(boolean headersCleaning) { - this.headersCleaning = headersCleaning; - } - - /** - * Sets the redirection mode. - * - * @param mode The redirection mode. - */ - public void setMode(int mode) { - this.mode = mode; - } - - /** - * Sets the target URI pattern. - * - * @param targetTemplate The target URI pattern. - */ - public void setTargetTemplate(String targetTemplate) { - this.targetTemplate = targetTemplate; - } - -} \ No newline at end of file + /** + * In this mode, the client is simply redirected to the URI generated from the target URI + * pattern using the {@link Status#REDIRECTION_FOUND} status. Note: this is a client-side + * redirection.
+ * + * @see Status#REDIRECTION_FOUND + */ + public static final int MODE_CLIENT_FOUND = 2; + + /** + * In this mode, the client is permanently redirected to the URI generated from the target URI + * pattern, using the {@link Status#REDIRECTION_PERMANENT} status. Note: this is a client-side + * redirection.
+ * + * @see Status#REDIRECTION_PERMANENT + */ + public static final int MODE_CLIENT_PERMANENT = 1; + + /** + * In this mode, the client is simply redirected to the URI generated from the target URI + * pattern using the {@link Status#REDIRECTION_SEE_OTHER} status. Note: this is a client-side + * redirection.
+ * + * @see Status#REDIRECTION_SEE_OTHER + */ + public static final int MODE_CLIENT_SEE_OTHER = 3; + + /** + * In this mode, the client is temporarily redirected to the URI generated from the target URI + * pattern using the {@link Status#REDIRECTION_TEMPORARY} status. Note: this is a client-side + * redirection.
+ * + * @see Status#REDIRECTION_TEMPORARY + */ + public static final int MODE_CLIENT_TEMPORARY = 4; + + /** + * In this mode, the call is sent to {@link Context#getServerDispatcher()}. Once the selected + * client connector has completed the request handling, the response is normally returned to the + * client. In this case, you can view the Redirector as acting as a transparent proxy Restlet. + * Note: this is a server-side redirection.
+ *
+ * Warning: remember to add the required connectors to the parent {@link Component} and to + * declare them in the list of required connectors on the {@link + * Application#getConnectorService()} property.
+ *
+ * Note that in this mode, the headers of HTTP requests, stored in the request's attributes, are + * removed before dispatching. Also, when an HTTP response comes back, the headers are also + * removed. You can control this behavior by setting the {@link #headersCleaning} attribute or + * by overriding the {@link #rewrite(Request)} or {@link #rewrite(Response)}. + * + * @see Context#getServerDispatcher() + */ + public static final int MODE_SERVER_INBOUND = 7; + + /** + * In this mode, the call is sent to {@link Application#getOutboundRoot()} or if null to {@link + * Context#getClientDispatcher()}. Once the selected client connector has completed the request + * handling, the response is normally returned to the client. In this case, you can view the + * {@link Redirector} as acting as a transparent server-side proxy. Note: this is a server-side + * redirection.
+ *
+ * Warning: remember to add the required connectors to the parent {@link Component} and to + * declare them in the list of required connectors on the {@link + * Application#getConnectorService()} property.
+ *
+ * Note that in this mode, the headers of HTTP requests, stored in the request's attributes, are + * removed before dispatching. Also, when an HTTP response comes back, the headers are also + * removed. You can control this behavior by setting the {@link #headersCleaning} attribute or + * by overriding the {@link #rewrite(Request)} or {@link #rewrite(Response)}. + * + * @see Application#getOutboundRoot() + * @see Context#getClientDispatcher() + */ + public static final int MODE_SERVER_OUTBOUND = 6; + + /** + * Indicates if all headers of HTTP requests stored in the request's attributes must be removed + * before the redirection. If set to true, it removes all headers, otherwise it keeps only the + * extension (or non-HTTP standard) headers + */ + protected volatile boolean headersCleaning; + + /** The redirection mode. */ + protected volatile int mode; + + /** The target URI pattern. */ + protected volatile String targetTemplate; + + /** + * Constructor for the client dispatcher mode. + * + * @param context The context. + * @param targetTemplate The template to build the target URI. + * @see org.restlet.routing.Template + */ + public Redirector(Context context, String targetTemplate) { + this(context, targetTemplate, MODE_SERVER_OUTBOUND); + } + + /** + * Constructor. + * + * @param context The context. + * @param targetPattern The pattern to build the target URI (using StringTemplate syntax and the + * CallModel for variables). + * @param mode The redirection mode. + */ + public Redirector(Context context, String targetPattern, int mode) { + super(context); + this.targetTemplate = targetPattern; + this.mode = mode; + this.headersCleaning = true; + } + + /** + * Computes the new location of the given reference after applying the redirection template. + * Returns null in case it cannot compute the new reference. + * + * @param locationRef The reference to translate. + * @param request The current request. + * @return The new location of the given reference. + */ + private String getLocation(Reference locationRef, Request request) { + Reference resourceRef = request.getResourceRef(); + Reference baseRef = resourceRef.getBaseRef(); + + Template rt = new Template(this.targetTemplate); + rt.setLogger(getLogger()); + int matched = rt.parse(locationRef.toString(), request); + + if (matched > 0) { + String remainingPart = (String) request.getAttributes().get("rr"); + + if (remainingPart != null) { + return baseRef.toString() + remainingPart; + } + } + + return null; + } + + /** + * Returns the redirection mode. + * + * @return The redirection mode. + */ + public int getMode() { + return this.mode; + } + + /** + * Returns the target reference to redirect to by automatically resolving URI template variables + * found using the {@link Template} class using the request and response as data models. + * + * @param request The request to handle. + * @param response The response to update. + * @return The target reference to redirect to. + */ + protected Reference getTargetRef(Request request, Response response) { + // Create the template + Template rt = new Template(this.targetTemplate); + rt.setLogger(getLogger()); + + // Return the formatted target URI + if (new Reference(this.targetTemplate).isRelative()) { + // Be sure to keep the resource's base reference. + return new Reference(request.getResourceRef(), rt.format(request, response)); + } + + return new Reference(rt.format(request, response)); + } + + /** + * Returns the target URI pattern. + * + * @return The target URI pattern. + */ + public String getTargetTemplate() { + return this.targetTemplate; + } + + /** + * Handles a call by redirecting using the selected redirection mode. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public void handle(Request request, Response response) { + // Generate the target reference + Reference targetRef = getTargetRef(request, response); + + switch (this.mode) { + case MODE_CLIENT_PERMANENT: + if (request.isLoggable()) { + getLogger().log(Level.FINE, "Permanently redirecting client to: " + targetRef); + } + + response.redirectPermanent(targetRef); + break; + + case MODE_CLIENT_FOUND: + if (request.isLoggable()) { + getLogger() + .log(Level.FINE, "Redirecting client to found location: " + targetRef); + } + + response.setLocationRef(targetRef); + response.setStatus(Status.REDIRECTION_FOUND); + break; + + case MODE_CLIENT_SEE_OTHER: + if (request.isLoggable()) { + getLogger() + .log( + Level.FINE, + "Redirecting client to another location: " + targetRef); + } + + response.redirectSeeOther(targetRef); + break; + + case MODE_CLIENT_TEMPORARY: + if (request.isLoggable()) { + getLogger().log(Level.FINE, "Temporarily redirecting client to: " + targetRef); + } + + response.redirectTemporary(targetRef); + break; + + case MODE_SERVER_OUTBOUND: + if (request.isLoggable()) { + getLogger() + .log(Level.FINE, "Redirecting via client dispatcher to: " + targetRef); + } + + outboundServerRedirect(targetRef, request, response); + break; + + case MODE_SERVER_INBOUND: + if (request.isLoggable()) { + getLogger() + .log(Level.FINE, "Redirecting via server dispatcher to: " + targetRef); + } + + inboundServerRedirect(targetRef, request, response); + break; + } + } + + /** + * Redirects a given call to a target reference. In the default implementation, the request HTTP + * headers, stored in the request's attributes, are removed before dispatching. After + * dispatching, the response HTTP headers are also removed to prevent conflicts with the main + * call. + * + * @param targetRef The target reference with URI variables resolved. + * @param request The request to handle. + * @param response The response to update. + */ + protected void inboundServerRedirect(Reference targetRef, Request request, Response response) { + serverRedirect(getContext().getServerDispatcher(), targetRef, request, response); + } + + /** + * Indicates if the headers must be cleaned. + * + * @return True if the headers must be cleaned. + */ + public boolean isHeadersCleaning() { + return headersCleaning; + } + + /** + * Redirects a given call to a target reference. In the default implementation, the request HTTP + * headers, stored in the request's attributes, are removed before dispatching. After + * dispatching, the response HTTP headers are also removed to prevent conflicts with the main + * call. + * + * @param targetRef The target reference with URI variables resolved. + * @param request The request to handle. + * @param response The response to update. + */ + protected void outboundServerRedirect(Reference targetRef, Request request, Response response) { + Restlet next = (getApplication() == null) ? null : getApplication().getOutboundRoot(); + + if (next == null) { + next = getContext().getClientDispatcher(); + } + + serverRedirect(next, targetRef, request, response); + if (response.getEntity() != null + && !request.getResourceRef().getScheme().equalsIgnoreCase(targetRef.getScheme())) { + // Distinct protocol, this data cannot be exposed. + response.getEntity().setLocationRef((Reference) null); + } + } + + /** + * Optionally rewrites the response entity returned in the {@link #MODE_SERVER_INBOUND} and + * {@link #MODE_SERVER_OUTBOUND} modes. By default, it just returns the initial entity without + * any modification. + * + * @param initialEntity The initial entity returned. + * @return The rewritten entity. + */ + protected Representation rewrite(Representation initialEntity) { + return initialEntity; + } + + /** + * Optionally updates the request sent in the {@link #MODE_SERVER_INBOUND} and {@link + * #MODE_SERVER_OUTBOUND} modes. By default, it leverages the {@link #headersCleaning} attribute + * to clean the headers: if set to true, it removes all headers, otherwise it keeps only the + * extension (or non-HTTP standard) headers
+ * + * @param initialRequest The initial request returned. + */ + protected void rewrite(Request initialRequest) { + if (isHeadersCleaning()) { + initialRequest.getAttributes().remove(HeaderConstants.ATTRIBUTE_HEADERS); + } else { + HeaderUtils.keepExtensionHeadersOnly(initialRequest); + } + } + + /** + * Optionally updates the response sent in the {@link #MODE_SERVER_INBOUND} and {@link + * #MODE_SERVER_OUTBOUND} modes. By default, it leverages the {@link #headersCleaning} attribute + * to clean the headers: if set to true, it removes all headers, otherwise it keeps only the + * extension (or non-HTTP standard) headers
+ * + * @param initialResponse The initial response returned. + */ + protected void rewrite(Response initialResponse) { + if (isHeadersCleaning()) { + initialResponse.getAttributes().remove(HeaderConstants.ATTRIBUTE_HEADERS); + } else { + HeaderUtils.keepExtensionHeadersOnly(initialResponse); + } + } + + /** + * Rewrite the location of the response, and the Location of the entity, if any. + * + * @param request The request to handle. + * @param response The response to update. + */ + public void rewriteLocation(Request request, Response response) { + if (response.getLocationRef() != null) { + Reference locationRef = response.getLocationRef(); + + String newLocation = getLocation(locationRef, request); + if (newLocation != null) { + response.setLocationRef(newLocation); + } + } + if (response.getEntity() != null && response.getEntity().getLocationRef() != null) { + Reference locationRef = response.getEntity().getLocationRef(); + + String newLocation = getLocation(locationRef, request); + if (newLocation != null) { + response.getEntity().setLocationRef(newLocation); + } + } + } + + /** + * Redirects a given call on the server-side to a next Restlet with a given target reference. In + * the default implementation, the request HTTP headers, stored in the request's attributes, are + * removed before dispatching. After dispatching, the response HTTP headers are also removed to + * prevent conflicts with the main call. + * + * @param next The next Restlet to forward the call to. + * @param targetRef The target reference with URI variables resolved. + * @param request The request to handle. + * @param response The response to update. + */ + protected void serverRedirect( + Restlet next, Reference targetRef, Request request, Response response) { + if (next == null) { + getLogger().warning("No next Restlet provided for server redirection to " + targetRef); + } else { + // Save the base URI if it exists as we might need it for + // redirections + Reference resourceRef = request.getResourceRef(); + + // Reset the protocol and let the dispatcher handle the protocol + request.setProtocol(null); + + // Update the request to cleanly go to the target URI + request.setResourceRef(targetRef); + rewrite(request); + next.handle(request, response); + + request.setResourceRef(resourceRef); + // Allow for response rewriting and clean the headers + response.setEntity(rewrite(response.getEntity())); + rewrite(response); + + // In case of redirection, we may have to rewrite the redirect URI + rewriteLocation(request, response); + } + } + + /** + * Indicates if the headers must be cleaned. + * + * @param headersCleaning True if the headers must be cleaned. + */ + public void setHeadersCleaning(boolean headersCleaning) { + this.headersCleaning = headersCleaning; + } + + /** + * Sets the redirection mode. + * + * @param mode The redirection mode. + */ + public void setMode(int mode) { + this.mode = mode; + } + + /** + * Sets the target URI pattern. + * + * @param targetTemplate The target URI pattern. + */ + public void setTargetTemplate(String targetTemplate) { + this.targetTemplate = targetTemplate; + } +} diff --git a/org.restlet/src/main/java/org/restlet/routing/Route.java b/org.restlet/src/main/java/org/restlet/routing/Route.java index 93665ad0eb..1df38cc2a5 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Route.java +++ b/org.restlet/src/main/java/org/restlet/routing/Route.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; import org.restlet.Request; @@ -14,67 +13,67 @@ import org.restlet.Restlet; /** - * Filter scoring the affinity of calls with the attached Restlet. The score is - * used by an associated Router in order to determine the most appropriate - * Restlet for a given call.
+ * Filter scoring the affinity of calls with the attached Restlet. The score is used by an + * associated Router to determine the most appropriate Restlet for a given call.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @see org.restlet.routing.Template * @author Jerome Louvel */ public abstract class Route extends Filter { - /** The parent router. */ - private volatile Router router; - - /** - * Constructor behaving as a simple extractor filter. - * - * @param next The next Restlet. - */ - public Route(Restlet next) { - this(null, next); - } + /** The parent router. */ + private volatile Router router; - /** - * Constructor. - * - * @param router The parent router. - * @param next The next Restlet. - */ - public Route(Router router, Restlet next) { - super((router != null) ? router.getContext() : (next != null) ? next.getContext() : null, next); - this.router = router; - } + /** + * Constructor behaving as a simple extractor filter. + * + * @param next The next Restlet. + */ + public Route(Restlet next) { + this(null, next); + } - /** - * Returns the parent router. - * - * @return The parent router. - */ - public Router getRouter() { - return this.router; - } + /** + * Constructor. + * + * @param router The parent router. + * @param next The next Restlet. + */ + public Route(Router router, Restlet next) { + super( + (router != null) ? router.getContext() : (next != null) ? next.getContext() : null, + next); + this.router = router; + } - /** - * Returns the score for a given call (between 0 and 1.0). - * - * @param request The request to score. - * @param response The response to score. - * @return The score for a given call (between 0 and 1.0). - */ - public abstract float score(Request request, Response response); + /** + * Returns the parent router. + * + * @return The parent router. + */ + public Router getRouter() { + return this.router; + } - /** - * Sets the parent router. - * - * @param router The parent router. - */ - public void setRouter(Router router) { - this.router = router; - } + /** + * Returns the score for a given call (between 0 and 1.0). + * + * @param request The request to score. + * @param response The response to score. + * @return The score for a given call (between 0 and 1.0). + */ + public abstract float score(Request request, Response response); + /** + * Sets the parent router. + * + * @param router The parent router. + */ + public void setRouter(Router router) { + this.router = router; + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/Router.java b/org.restlet/src/main/java/org/restlet/routing/Router.java index bdcdab3ff8..d6826b640c 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Router.java +++ b/org.restlet/src/main/java/org/restlet/routing/Router.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -19,739 +19,703 @@ import org.restlet.resource.ServerResource; import org.restlet.util.RouteList; -import java.util.logging.Level; - /** - * Restlet routing calls to one of the attached routes. Each route can compute - * an affinity score for each call depending on various criteria. The attach() - * method allow the creation of routes based on URI patterns matching the - * beginning of a the resource reference's remaining part.
+ * Restlet routing calls to one of the attached routes. Each route can compute an affinity score for + * each call depending on various criteria. The attach() method allows the creation of routes based + * on URI patterns matching the beginning of a resource reference's remaining part.
*
- * In addition, several routing modes are supported, implementing various - * algorithms: + * In addition, several routing modes are supported, implementing various algorithms: + * *

    - *
  • Best match
  • - *
  • First match (default)
  • - *
  • Last match
  • - *
  • Random match
  • - *
  • Round-robin
  • - *
  • Custom
  • + *
  • Best match + *
  • First match (default) + *
  • Last match + *
  • Random match + *
  • Round-robin + *
  • Custom *
+ * *
- * Note that for routes using URI patterns will update the resource reference's - * base reference during the routing if they are selected. It is also important - * to know that the routing is very strict about path separators in your URI - * patterns. Finally, you can modify the list of routes while handling incoming - * calls as the delegation code is ensured to be thread-safe.
+ * Note that for routes using URI patterns will update the resource reference's base reference + * during the routing if they are selected. It is also important to know that the routing is very + * strict about path separators in your URI patterns. Finally, you can modify the list of routes + * while handling incoming calls as the delegation code is ensured to be thread-safe.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel */ public class Router extends Restlet { - /** - * Each call will be routed to the route with the best score if the required - * score is reached. See {@link RouteList#getBest(Request, Response, float)} - * method for implementation details. - */ - public static final int MODE_BEST_MATCH = 1; - - /** - * Each call will be routed according to a custom mode. Override the - * {@link #getCustom(Request, Response)} method to provide your own logic. - */ - public static final int MODE_CUSTOM = 6; - - /** - * Each call is routed to the first route if the required score is reached. If - * the required score is not reached, then the route is skipped and the next one - * is considered. See {@link RouteList#getFirst(Request, Response, float)} - * method for implementation details. - */ - public static final int MODE_FIRST_MATCH = 2; - - /** - * Each call will be routed to the last route if the required score is reached. - * If the required score is not reached, then the route is skipped and the - * previous one is considered. See - * {@link RouteList#getLast(Request, Response, float)} method for implementation - * details. - */ - public static final int MODE_LAST_MATCH = 3; - - /** - * Each call is routed to the next route target if the required score is - * reached. The next route is relative to the previous call routed (round robin - * mode). If the required score is not reached, then the route is skipped and - * the next one is considered. If the last route is reached, the first route - * will be considered. See {@link RouteList#getNext(Request, Response, float)} - * method for implementation details. - */ - public static final int MODE_NEXT_MATCH = 4; - - /** - * Each call will be randomly routed to one of the routes that reached the - * required score. If the random route selected is not a match, then the - * immediate next route is evaluated until one matching route is found. If we - * get back to the initial random route selected with no match, then we return - * null. Unless all the routes score above the required score, this mode will - * result in non-uniform distribution of calls. See - * {@link RouteList#getRandom(Request, Response, float)} method for - * implementation details. - */ - public static final int MODE_RANDOM_MATCH = 5; - - /** The default matching mode to use when selecting routes based on URIs. */ - private volatile int defaultMatchingMode; - - /** - * The default setting for whether the routing should be done on URIs with or - * without taking into account query string. - */ - private volatile boolean defaultMatchingQuery; - - /** The default route tested if no other one was available. */ - private volatile Route defaultRoute; - - /** - * The maximum number of attempts if no attachment could be matched on the first - * attempt. - */ - private volatile int maxAttempts; - - /** The minimum score required to have a match. */ - private volatile float requiredScore; - - /** The delay (in milliseconds) before a new attempt. */ - private volatile long retryDelay; - - /** The modifiable list of routes. */ - private volatile RouteList routes; - - /** The routing mode. */ - private volatile int routingMode; - - /** - * Constructor. Note that usage of this constructor is not recommended as the - * Router won't have a proper context set. In general you will prefer to use the - * other constructor and pass it the parent application's context or eventually - * the parent component's context if you don't use applications. - */ - public Router() { - this(null); - } - - /** - * Constructor. - * - * @param context The context. - */ - public Router(Context context) { - super(context); - this.routes = new RouteList(); - this.defaultMatchingMode = Template.MODE_EQUALS; - this.defaultMatchingQuery = false; - this.defaultRoute = null; - this.routingMode = MODE_FIRST_MATCH; - this.requiredScore = 0.5F; - this.maxAttempts = 1; - this.retryDelay = 500L; - } - - /** - * Attaches a target Restlet to this router with an empty URI pattern. A new - * route using the matching mode returned by {@link #getMatchingMode(Restlet)} - * will be added routing to the target when any call is received. - * - * @param target The target Restlet to attach. - * @return The created route. - */ - public TemplateRoute attach(Restlet target) { - return attach(target, getMatchingMode(target)); - } - - /** - * Attaches a target Restlet to this router with an empty URI pattern. A new - * route will be added routing to the target when any call is received. - * - * @param target The target Restlet to attach. - * @param matchingMode The matching mode. - * @return The created route. - */ - public TemplateRoute attach(Restlet target, int matchingMode) { - return attach("", target, matchingMode); - } - - /** - * Attaches a target Resource class to this router based on a given URI pattern. - * A new route using the matching mode returned by - * {@link #getMatchingMode(Restlet)} will be added routing to the target when - * calls with a URI matching the pattern will be received. - * - * @param pathTemplate The URI path template that must match the relative part - * of the resource URI. - * @param targetClass The target Resource class to attach. - * @return The created route. - */ - public TemplateRoute attach(String pathTemplate, Class targetClass) { - return attach(pathTemplate, createFinder(targetClass)); - } - - /** - * Attaches a target Resource class to this router based on a given URI pattern. - * A new route will be added routing to the target when calls with a URI - * matching the pattern will be received. - * - * @param pathTemplate The URI path template that must match the relative part - * of the resource URI. - * @param targetClass The target Resource class to attach. - * @param matchingMode The matching mode. - * @return The created route. - */ - public TemplateRoute attach(String pathTemplate, Class targetClass, int matchingMode) { - return attach(pathTemplate, createFinder(targetClass), matchingMode); - } - - /** - * Attaches a target Restlet to this router based on a given URI pattern. A new - * route using the matching mode returned by {@link #getMatchingMode(Restlet)} - * will be added routing to the target when calls with a URI matching the - * pattern will be received. - * - * @param pathTemplate The URI path template that must match the relative part - * of the resource URI. - * @param target The target Restlet to attach. - * @return The created route. - */ - public TemplateRoute attach(String pathTemplate, Restlet target) { - return attach(pathTemplate, target, getMatchingMode(target)); - } - - /** - * Attaches a target Restlet to this router based on a given URI pattern. A new - * route will be added routing to the target when calls with a URI matching the - * pattern will be received. - * - * @param pathTemplate The URI path template that must match the relative part - * of the resource URI. - * @param target The target Restlet to attach. - * @param matchingMode The matching mode. - * @return The created route. - */ - public TemplateRoute attach(String pathTemplate, Restlet target, int matchingMode) { - TemplateRoute result = createRoute(pathTemplate, target, matchingMode); - getRoutes().add(result); - return result; - } - - /** - * Attaches a Resource class to this router as the default target to invoke when - * no route matches. It actually sets a default route that scores all calls to - * 1.0. - * - * @param defaultTargetClass The target Resource class to attach. - * @return The created route. - */ - public TemplateRoute attachDefault(Class defaultTargetClass) { - return attachDefault(createFinder(defaultTargetClass)); - } - - /** - * Attaches a Restlet to this router as the default target to invoke when no - * route matches. It actually sets a default route that scores all calls to 1.0. - * - * @param defaultTarget The Restlet to use as the default target. - * @return The created route. - */ - public TemplateRoute attachDefault(Restlet defaultTarget) { - TemplateRoute result = createRoute("", defaultTarget); - result.setMatchingMode(Template.MODE_STARTS_WITH); - setDefaultRoute(result); - return result; - } - - /** - * Creates a new route for the given URI pattern and target. The route will - * match the URI query string depending on the result of - * {@link #getDefaultMatchingQuery()} and the matching mode will be given by - * {@link #getMatchingMode(Restlet)}. - * - * @param uriPattern The URI pattern that must match the relative part of the - * resource URI. - * @param target The target Restlet to attach. - * @return The created route. - */ - protected TemplateRoute createRoute(String uriPattern, Restlet target) { - return createRoute(uriPattern, target, getMatchingMode(target)); - } - - /** - * Creates a new route for the given URI pattern, target and matching mode. The - * route will match the URI query string depending on the result of - * {@link #getDefaultMatchingQuery()}. - * - * @param uriPattern The URI pattern that must match the relative part of the - * resource URI. - * @param target The target Restlet to attach. - * @param matchingMode The matching mode. - * @return The created route. - */ - protected TemplateRoute createRoute(String uriPattern, Restlet target, int matchingMode) { - TemplateRoute result = new TemplateRoute(this, uriPattern, target); - result.getTemplate().setMatchingMode(matchingMode); - result.setMatchingQuery(getDefaultMatchingQuery()); - return result; - } - - /** - * Detaches the target from this router. All routes routing to this target - * Restlet are removed from the list of routes and the default route is set to - * null. - * - * @param targetClass The target class to detach. - */ - public void detach(Class targetClass) { - for (int i = getRoutes().size() - 1; i >= 0; i--) { - Restlet target = getRoutes().get(i).getNext(); - - if (target != null && Finder.class.isAssignableFrom(target.getClass())) { - Finder finder = (Finder) target; - - if (finder.getTargetClass().equals(targetClass)) { - getRoutes().remove(i); - } - } - } - - if (getDefaultRoute() != null) { - Restlet target = getDefaultRoute().getNext(); - - if (target != null && Finder.class.isAssignableFrom(target.getClass())) { - Finder finder = (Finder) target; - - if (finder.getTargetClass().equals(targetClass)) { - setDefaultRoute(null); - } - } - } - } - - /** - * Detaches the target from this router. All routes routing to this target - * Restlet are removed from the list of routes and the default route is set to - * null. - * - * @param target The target Restlet to detach. - */ - public void detach(Restlet target) { - getRoutes().removeAll(target); - if ((getDefaultRoute() != null) && (getDefaultRoute().getNext() == target)) { - setDefaultRoute(null); - } - } - - /** - * Effectively handles the call using the selected next {@link Restlet}, - * typically the selected {@link Route}. By default, it just invokes the next - * Restlet. - * - * @param next The next Restlet to invoke. - * @param request The request. - * @param response The response. - */ - protected void doHandle(Restlet next, Request request, Response response) { - next.handle(request, response); - } - - /** - * Returns the matched route according to a custom algorithm. To use in - * combination of the {@link #MODE_CUSTOM} option. The default implementation - * (to be overridden), returns null. - * - * @param request The request to handle. - * @param response The response to update. - * @return The matched route if available or null. - */ - protected Route getCustom(Request request, Response response) { - return null; - } - - /** - * Returns the default matching mode to use when selecting routes based on URIs. - * By default it returns {@link Template#MODE_EQUALS}. - * - * @return The default matching mode. - */ - public int getDefaultMatchingMode() { - return this.defaultMatchingMode; - } - - /** - * Returns the default setting for whether the routing should be done on URIs - * with or without taking into account query string. By default, it returns - * false. - * - * @return the default setting for whether the routing should be done on URIs - * with or without taking into account query string. - */ - public boolean getDefaultMatchingQuery() { - return this.defaultMatchingQuery; - } - - /** - * Returns the default route to test if no other one was available after - * retrying the maximum number of attempts. - * - * @return The default route tested if no other one was available. - */ - public Route getDefaultRoute() { - return this.defaultRoute; - } - - /** - * Returns the matching mode for the target Restlet. By default it returns - * {@link #getDefaultMatchingMode()}. If the target is an instance of - * {@link Directory} or {@link Router} then the mode returned is - * {@link Template#MODE_STARTS_WITH} to allow further routing by those objects. - * If the target is an instance of {@link Filter}, then it returns the matching - * mode for the {@link Filter#getNext()} Restlet recursively. - * - * @param target The target Restlet. - * @return The preferred matching mode. - */ - protected int getMatchingMode(Restlet target) { - int result = getDefaultMatchingMode(); - - if ((target instanceof Directory) || (target instanceof Router)) { - result = Template.MODE_STARTS_WITH; - } else if (target instanceof Filter) { - result = getMatchingMode(((Filter) target).getNext()); - } - - return result; - } - - /** - * Returns the maximum number of attempts if no attachment could be matched on - * the first attempt. This is useful when the attachment scoring is dynamic and - * therefore could change on a retry. The default value is set to 1. - * - * @return The maximum number of attempts if no attachment could be matched on - * the first attempt. - */ - public int getMaxAttempts() { - return this.maxAttempts; - } - - /** - * Returns the next Restlet if available. - * - * @param request The request to handle. - * @param response The response to update. - * @return The next Restlet if available or null. - */ - public Restlet getNext(Request request, Response response) { - Route result = null; - - for (int i = 0; (result == null) && (i < getMaxAttempts()); i++) { - if (i > 0) { - // Before attempting another time, let's - // sleep during the "retryDelay" set. - try { - Thread.sleep(getRetryDelay()); - } catch (InterruptedException e) { - // MITRE, CWE-391 - Unchecked Error Condition - Thread.currentThread().interrupt(); - } - } - - if (this.routes != null) { - // Select the routing mode - switch (getRoutingMode()) { - case MODE_BEST_MATCH: - result = getRoutes().getBest(request, response, getRequiredScore()); - break; - - case MODE_FIRST_MATCH: - result = getRoutes().getFirst(request, response, getRequiredScore()); - break; - - case MODE_LAST_MATCH: - result = getRoutes().getLast(request, response, getRequiredScore()); - break; - - case MODE_NEXT_MATCH: - result = getRoutes().getNext(request, response, getRequiredScore()); - break; - - case MODE_RANDOM_MATCH: - result = getRoutes().getRandom(request, response, getRequiredScore()); - break; - - case MODE_CUSTOM: - result = getCustom(request, response); - break; - } - } - } - - if (result == null) { - // If nothing matched in the routes list, - // check the default route - if ((getDefaultRoute() != null) && (getDefaultRoute().score(request, response) >= getRequiredScore())) { - result = getDefaultRoute(); - } else { - // No route could be found - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } - } - - if (request.isLoggable()) { - logRoute(result); - } - - return result; - } - - /** - * Returns the minimum score required to have a match. By default, it returns - * {@code 0.5}. - * - * @return The minimum score required to have a match. - */ - public float getRequiredScore() { - return this.requiredScore; - } - - /** - * Returns the delay in milliseconds before a new attempt is made. The default - * value is {@code 500}. - * - * @return The delay in milliseconds before a new attempt is made. - */ - public long getRetryDelay() { - return this.retryDelay; - } - - /** - * Returns the modifiable list of routes. Creates a new instance if no one has - * been set. - * - * @return The modifiable list of routes. - */ - public RouteList getRoutes() { - return this.routes; - } - - /** - * Returns the routing mode. By default, it returns the - * {@link #MODE_FIRST_MATCH} mode. - * - * @return The routing mode. - */ - public int getRoutingMode() { - return this.routingMode; - } - - /** - * Handles a call by invoking the next Restlet if it is available. - * - * @param request The request to handle. - * @param response The response to update. - */ - @Override - public void handle(Request request, Response response) { - super.handle(request, response); - Restlet next = getNext(request, response); - - if (next != null) { - doHandle(next, request, response); - } else { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } - } - - /** - * Logs the route selected. - * - * @param route The route selected. - */ - protected void logRoute(Route route) { - if (getLogger().isLoggable(Level.FINE)) { - if (getDefaultRoute() == route) { - getLogger().fine("The default route was selected"); - } else { - getLogger().fine("Selected route: " + route); - } - } - } - - /** - * Attaches a permanent redirection to this router based on a given URI pattern. - * The client is expected to reuse the same method for the new request. - * - * @param pathTemplate The URI path template that must match the relative part - * of the resource URI. - * @param targetUri The target URI. - * @return The created route. - */ - public TemplateRoute redirectPermanent(String pathTemplate, String targetUri) { - return attach(pathTemplate, new Redirector(getContext(), targetUri, Redirector.MODE_CLIENT_PERMANENT)); - } - - /** - * Attaches a redirection to this router based on a given URI pattern. It - * redirects the client to a different URI that SHOULD be retrieved using a GET - * method on that resource. This method exists primarily to allow the output of - * a POST-activated script to redirect the user agent to a selected resource. - * The new URI is not a substitute reference for the originally requested - * resource. - * - * @param pathTemplate The URI path template that must match the relative part - * of the resource URI. - * @param targetUri The target URI. - * @return The created route. - */ - - public TemplateRoute redirectSeeOther(String pathTemplate, String targetUri) { - return attach(pathTemplate, new Redirector(getContext(), targetUri, Redirector.MODE_CLIENT_SEE_OTHER)); - } - - /** - * Attaches a temporary redirection to this router based on a given URI pattern. - * The client is expected to reuse the same method for the new request. - * - * @param pathTemplate The URI path template that must match the relative part - * of the resource URI. - * @param targetUri The target URI. - * @return The created route. - */ - - public TemplateRoute redirectTemporary(String pathTemplate, String targetUri) { - return attach(pathTemplate, new Redirector(getContext(), targetUri, Redirector.MODE_CLIENT_TEMPORARY)); - } - - /** - * Sets the default matching mode to use when selecting routes based on URIs. By - * default it is set to {@link Template#MODE_EQUALS}. - * - * @param defaultMatchingMode The default matching mode. - */ - public void setDefaultMatchingMode(int defaultMatchingMode) { - this.defaultMatchingMode = defaultMatchingMode; - } - - /** - * Sets the default setting for whether the routing should be done on URIs with - * or without taking into account query string. By default, it is set to false. - * - * @param defaultMatchingQuery The default setting for whether the routing - * should be done on URIs with or without taking - * into account query string. - * - */ - public void setDefaultMatchingQuery(boolean defaultMatchingQuery) { - this.defaultMatchingQuery = defaultMatchingQuery; - } - - /** - * Sets the default route tested if no other one was available. - * - * @param defaultRoute The default route tested if no other one was available. - */ - public void setDefaultRoute(Route defaultRoute) { - this.defaultRoute = defaultRoute; - } - - /** - * Sets the maximum number of attempts if no attachment could be matched on the - * first attempt. This is useful when the attachment scoring is dynamic and - * therefore could change on a retry. - * - * @param maxAttempts The maximum number of attempts. - */ - public void setMaxAttempts(int maxAttempts) { - this.maxAttempts = maxAttempts; - } - - /** - * Sets the score required to have a match. By default, it is set to - * {@code 0.5}. - * - * @param score The score required to have a match. - */ - public void setRequiredScore(float score) { - this.requiredScore = score; - } - - /** - * Sets the delay in milliseconds before a new attempt is made. By default, it - * is set to {@code 500}. - * - * @param retryDelay The delay in milliseconds before a new attempt is made. - */ - public void setRetryDelay(long retryDelay) { - this.retryDelay = retryDelay; - } - - /** - * Sets the modifiable list of routes. - * - * @param routes The modifiable list of routes. - */ - public void setRoutes(RouteList routes) { - this.routes = routes; - } - - /** - * Sets the routing mode. By default, it is set to the {@link #MODE_FIRST_MATCH} - * mode. - * - * @param routingMode The routing mode. - */ - public void setRoutingMode(int routingMode) { - this.routingMode = routingMode; - } - - /** - * Starts the filter and the attached routes. - */ - @Override - public synchronized void start() throws Exception { - if (isStopped()) { - for (Route route : getRoutes()) { - route.start(); - } - - if (getDefaultRoute() != null) { - getDefaultRoute().start(); - } - - // Must be invoked as a last step - super.start(); - } - } - - /** - * Stops the filter and the attached routes. - */ - @Override - public synchronized void stop() throws Exception { - if (isStarted()) { - // Must be invoked as a first step - super.stop(); - - if (getDefaultRoute() != null) { - getDefaultRoute().stop(); - } - - for (Route route : getRoutes()) { - route.stop(); - } - } - } - + /** + * Each call will be routed to the route with the best score if the required score is reached. + * See {@link RouteList#getBest(Request, Response, float)} method for implementation details. + */ + public static final int MODE_BEST_MATCH = 1; + + /** + * Each call will be routed according to a custom mode. Override the {@link #getCustom(Request, + * Response)} method to provide your own logic. + */ + public static final int MODE_CUSTOM = 6; + + /** + * Each call is routed to the first route if the required score is reached. If the required + * score is not reached, then the route is skipped and the next one is considered. See {@link + * RouteList#getFirst(Request, Response, float)} method for implementation details. + */ + public static final int MODE_FIRST_MATCH = 2; + + /** + * Each call will be routed to the last route if the required score is reached. If the required + * score is not reached, then the route is skipped and the previous one is considered. See + * {@link RouteList#getLast(Request, Response, float)} method for implementation details. + */ + public static final int MODE_LAST_MATCH = 3; + + /** + * Each call is routed to the next route target if the required score is reached. The next route + * is relative to the previous call routed (round-robin mode). If the required score is not + * reached, then the route is skipped and the next one is considered. If the last route is + * reached, the first route will be considered. See {@link RouteList#getNext(Request, Response, + * float)} method for implementation details. + */ + public static final int MODE_NEXT_MATCH = 4; + + /** + * Each call will be randomly routed to one of the routes that reached the required score. If + * the random route selected is not a match, then the immediate next route is evaluated until + * one matching route is found. If we get back to the initial random route selected with no + * match, then we return null. Unless all the routes score above the required score, this mode + * will result in non-uniform distribution of calls. See {@link RouteList#getRandom(Request, + * Response, float)} method for implementation details. + */ + public static final int MODE_RANDOM_MATCH = 5; + + /** The default matching mode to use when selecting routes based on URIs. */ + private volatile int defaultMatchingMode; + + /** + * The default setting for whether the routing should be done on URIs with or without taking + * into account query string. + */ + private volatile boolean defaultMatchingQuery; + + /** The default route tested if no other one was available. */ + private volatile Route defaultRoute; + + /** The maximum number of attempts if no attachment could be matched on the first attempt. */ + private volatile int maxAttempts; + + /** The minimum score required to have a match. */ + private volatile float requiredScore; + + /** The delay (in milliseconds) before a new attempt. */ + private volatile long retryDelay; + + /** The modifiable list of routes. */ + private volatile RouteList routes; + + /** The routing mode. */ + private volatile int routingMode; + + /** + * Constructor. Note that usage of this constructor is not recommended as the Router won't have + * a proper context set. In general, you will prefer to use the other constructor and pass it + * the parent application's context or eventually the parent component's context if you don't + * use applications. + */ + public Router() { + this(null); + } + + /** + * Constructor. + * + * @param context The context. + */ + public Router(Context context) { + super(context); + this.routes = new RouteList(); + this.defaultMatchingMode = Template.MODE_EQUALS; + this.defaultMatchingQuery = false; + this.defaultRoute = null; + this.routingMode = MODE_FIRST_MATCH; + this.requiredScore = 0.5F; + this.maxAttempts = 1; + this.retryDelay = 500L; + } + + /** + * Attaches a target Restlet to this router with an empty URI pattern. A new route using the + * matching mode returned by {@link #getMatchingMode(Restlet)} will be added routing to the + * target when any call is received. + * + * @param target The target Restlet to attach. + * @return The created route. + */ + public TemplateRoute attach(Restlet target) { + return attach(target, getMatchingMode(target)); + } + + /** + * Attaches a target Restlet to this router with an empty URI pattern. A new route will be added + * routing to the target when any call is received. + * + * @param target The target Restlet to attach. + * @param matchingMode The matching mode. + * @return The created route. + */ + public TemplateRoute attach(Restlet target, int matchingMode) { + return attach("", target, matchingMode); + } + + /** + * Attaches a target Resource class to this router based on a given URI pattern. A new route + * using the matching mode returned by {@link #getMatchingMode(Restlet)} will be added routing + * to the target when calls with a URI matching the pattern will be received. + * + * @param pathTemplate The URI path template that must match the relative part of the resource + * URI. + * @param targetClass The target Resource class to attach. + * @return The created route. + */ + public TemplateRoute attach(String pathTemplate, Class targetClass) { + return attach(pathTemplate, createFinder(targetClass)); + } + + /** + * Attaches a target Resource class to this router based on a given URI pattern. A new route + * will be added routing to the target when calls with a URI matching the pattern are received. + * + * @param pathTemplate The URI path template that must match the relative part of the resource + * URI. + * @param targetClass The target Resource class to attach. + * @param matchingMode The matching mode. + * @return The created route. + */ + public TemplateRoute attach( + String pathTemplate, Class targetClass, int matchingMode) { + return attach(pathTemplate, createFinder(targetClass), matchingMode); + } + + /** + * Attaches a target Restlet to this router based on a given URI pattern. A new route using the + * matching mode returned by {@link #getMatchingMode(Restlet)} will be added routing to the + * target when calls with a URI matching the pattern will be received. + * + * @param pathTemplate The URI path template that must match the relative part of the resource + * URI. + * @param target The target Restlet to attach. + * @return The created route. + */ + public TemplateRoute attach(String pathTemplate, Restlet target) { + return attach(pathTemplate, target, getMatchingMode(target)); + } + + /** + * Attaches a target Restlet to this router based on a given URI pattern. A new route will be + * added routing to the target when calls with a URI matching the pattern are received. + * + * @param pathTemplate The URI path template that must match the relative part of the resource + * URI. + * @param target The target Restlet to attach. + * @param matchingMode The matching mode. + * @return The created route. + */ + public TemplateRoute attach(String pathTemplate, Restlet target, int matchingMode) { + TemplateRoute result = createRoute(pathTemplate, target, matchingMode); + getRoutes().add(result); + return result; + } + + /** + * Attaches a Resource class to this router as the default target to invoke when no route + * matches. It actually sets a default route that scores all calls to 1.0. + * + * @param defaultTargetClass The target Resource class to attach. + * @return The created route. + */ + public TemplateRoute attachDefault(Class defaultTargetClass) { + return attachDefault(createFinder(defaultTargetClass)); + } + + /** + * Attaches a Restlet to this router as the default target to invoke when no route matches. It + * actually sets a default route that scores all calls to 1.0. + * + * @param defaultTarget The Restlet to use as the default target. + * @return The created route. + */ + public TemplateRoute attachDefault(Restlet defaultTarget) { + TemplateRoute result = createRoute("", defaultTarget); + result.setMatchingMode(Template.MODE_STARTS_WITH); + setDefaultRoute(result); + return result; + } + + /** + * Creates a new route for the given URI pattern and target. The route will match the URI query + * string depending on the result of {@link #getDefaultMatchingQuery()} and the matching mode + * will be given by {@link #getMatchingMode(Restlet)}. + * + * @param uriPattern The URI pattern that must match the relative part of the resource URI. + * @param target The target Restlet to attach. + * @return The created route. + */ + protected TemplateRoute createRoute(String uriPattern, Restlet target) { + return createRoute(uriPattern, target, getMatchingMode(target)); + } + + /** + * Creates a new route for the given URI pattern, target, and matching mode. The route will + * match the URI query string depending on the result of {@link #getDefaultMatchingQuery()}. + * + * @param uriPattern The URI pattern that must match the relative part of the resource URI. + * @param target The target Restlet to attach. + * @param matchingMode The matching mode. + * @return The created route. + */ + protected TemplateRoute createRoute(String uriPattern, Restlet target, int matchingMode) { + TemplateRoute result = new TemplateRoute(this, uriPattern, target); + result.getTemplate().setMatchingMode(matchingMode); + result.setMatchingQuery(getDefaultMatchingQuery()); + return result; + } + + /** + * Detaches the target from this router. All routes to this target Restlet are removed from the + * list of routes, and the default route is set to null. + * + * @param targetClass The target class to detach. + */ + public void detach(Class targetClass) { + for (int i = getRoutes().size() - 1; i >= 0; i--) { + Restlet target = getRoutes().get(i).getNext(); + + if (target != null && Finder.class.isAssignableFrom(target.getClass())) { + Finder finder = (Finder) target; + + if (finder.getTargetClass().equals(targetClass)) { + getRoutes().remove(i); + } + } + } + + if (getDefaultRoute() != null) { + Restlet target = getDefaultRoute().getNext(); + + if (target != null && Finder.class.isAssignableFrom(target.getClass())) { + Finder finder = (Finder) target; + + if (finder.getTargetClass().equals(targetClass)) { + setDefaultRoute(null); + } + } + } + } + + /** + * Detaches the target from this router. All routes to this target Restlet are removed from the + * list of routes, and the default route is set to null. + * + * @param target The target Restlet to detach. + */ + public void detach(Restlet target) { + getRoutes().removeAll(target); + if ((getDefaultRoute() != null) && (getDefaultRoute().getNext() == target)) { + setDefaultRoute(null); + } + } + + /** + * Effectively handles the call using the selected next {@link Restlet}, typically the selected + * {@link Route}. By default, it just invokes the next Restlet. + * + * @param next The next Restlet to invoke. + * @param request The request. + * @param response The response. + */ + protected void doHandle(Restlet next, Request request, Response response) { + next.handle(request, response); + } + + /** + * Returns the matched route according to a custom algorithm. To use in combination of the + * {@link #MODE_CUSTOM} option. The default implementation (to be overridden), returns null. + * + * @param request The request to handle. + * @param response The response to update. + * @return The matched route if available or null. + */ + protected Route getCustom(Request request, Response response) { + return null; + } + + /** + * Returns the default matching mode to use when selecting routes based on URIs. By default, it + * returns {@link Template#MODE_EQUALS}. + * + * @return The default matching mode. + */ + public int getDefaultMatchingMode() { + return this.defaultMatchingMode; + } + + /** + * Returns the default setting for whether the routing should be done on URIs with or without + * taking into account the query string. By default, it returns false. + * + * @return the default setting for whether the routing should be done on URIs with or without + * taking into account query string. + */ + public boolean getDefaultMatchingQuery() { + return this.defaultMatchingQuery; + } + + /** + * Returns the default route to test if no other one was available after retrying the maximum + * number of attempts. + * + * @return The default route tested if no other one was available. + */ + public Route getDefaultRoute() { + return this.defaultRoute; + } + + /** + * Returns the matching mode for the target Restlet. By default, it returns {@link + * #getDefaultMatchingMode()}. If the target is an instance of {@link Directory} or {@link + * Router} then the mode returned is {@link Template#MODE_STARTS_WITH} to allow further routing + * by those objects. If the target is an instance of {@link Filter}, then it returns the + * matching mode for the {@link Filter#getNext()} Restlet recursively. + * + * @param target The target Restlet. + * @return The preferred matching mode. + */ + protected int getMatchingMode(Restlet target) { + int result = getDefaultMatchingMode(); + + if ((target instanceof Directory) || (target instanceof Router)) { + result = Template.MODE_STARTS_WITH; + } else if (target instanceof Filter) { + result = getMatchingMode(((Filter) target).getNext()); + } + + return result; + } + + /** + * Returns the maximum number of attempts if no attachment could be matched on the first + * attempt. This is useful when the attachment scoring is dynamic and therefore could change on + * a retry. The default value is set to 1. + * + * @return The maximum number of attempts if no attachment could be matched on the first + * attempt. + */ + public int getMaxAttempts() { + return this.maxAttempts; + } + + /** + * Returns the next Restlet if available. + * + * @param request The request to handle. + * @param response The response to update. + * @return The next Restlet if available or null. + */ + public Restlet getNext(Request request, Response response) { + Route result = null; + + for (int i = 0; (result == null) && (i < getMaxAttempts()); i++) { + if (i > 0) { + // Before attempting another time, let's + // sleep during the "retryDelay" set. + try { + Thread.sleep(getRetryDelay()); + } catch (InterruptedException e) { + // MITRE, CWE-391 - Unchecked Error Condition + Thread.currentThread().interrupt(); + } + } + + if (this.routes != null) { + // Select the routing mode + switch (getRoutingMode()) { + case MODE_BEST_MATCH: + result = getRoutes().getBest(request, response, getRequiredScore()); + break; + + case MODE_FIRST_MATCH: + result = getRoutes().getFirst(request, response, getRequiredScore()); + break; + + case MODE_LAST_MATCH: + result = getRoutes().getLast(request, response, getRequiredScore()); + break; + + case MODE_NEXT_MATCH: + result = getRoutes().getNext(request, response, getRequiredScore()); + break; + + case MODE_RANDOM_MATCH: + result = getRoutes().getRandom(request, response, getRequiredScore()); + break; + + case MODE_CUSTOM: + result = getCustom(request, response); + break; + } + } + } + + if (result == null) { + // If nothing matched in the routes list, + // check the default route + if ((getDefaultRoute() != null) + && (getDefaultRoute().score(request, response) >= getRequiredScore())) { + result = getDefaultRoute(); + } else { + // No route could be found + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } + } + + if (request.isLoggable()) { + logRoute(result); + } + + return result; + } + + /** + * Returns the minimum score required to have a match. By default, it returns {@code 0.5}. + * + * @return The minimum score required to have a match. + */ + public float getRequiredScore() { + return this.requiredScore; + } + + /** + * Returns the delay in milliseconds before a new attempt is made. The default value is {@code + * 500}. + * + * @return The delay in milliseconds before a new attempt is made. + */ + public long getRetryDelay() { + return this.retryDelay; + } + + /** + * Returns the modifiable list of routes. Creates a new instance if no one has been set. + * + * @return The modifiable list of routes. + */ + public RouteList getRoutes() { + return this.routes; + } + + /** + * Returns the routing mode. By default, it returns the {@link #MODE_FIRST_MATCH} mode. + * + * @return The routing mode. + */ + public int getRoutingMode() { + return this.routingMode; + } + + /** + * Handles a call by invoking the next Restlet if it is available. + * + * @param request The request to handle. + * @param response The response to update. + */ + @Override + public void handle(Request request, Response response) { + super.handle(request, response); + Restlet next = getNext(request, response); + + if (next != null) { + doHandle(next, request, response); + } else { + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } + } + + /** + * Logs the route selected. + * + * @param route The route selected. + */ + protected void logRoute(Route route) { + if (getLogger().isLoggable(Level.FINE)) { + if (getDefaultRoute() == route) { + getLogger().fine("The default route was selected"); + } else { + getLogger().fine("Selected route: " + route); + } + } + } + + /** + * Attaches a permanent redirection to this router based on a given URI pattern. The client is + * expected to reuse the same method for the new request. + * + * @param pathTemplate The URI path template that must match the relative part of the resource + * URI. + * @param targetUri The target URI. + * @return The created route. + */ + public TemplateRoute redirectPermanent(String pathTemplate, String targetUri) { + return attach( + pathTemplate, + new Redirector(getContext(), targetUri, Redirector.MODE_CLIENT_PERMANENT)); + } + + /** + * Attaches a redirection to this router based on a given URI pattern. It redirects the client + * to a different URI that SHOULD be retrieved using a GET method on that resource. This method + * exists primarily to allow the output of a POST-activated script to redirect the user agent to + * a selected resource. The new URI is not a substitute reference for the originally requested + * resource. + * + * @param pathTemplate The URI path template that must match the relative part of the resource + * URI. + * @param targetUri The target URI. + * @return The created route. + */ + public TemplateRoute redirectSeeOther(String pathTemplate, String targetUri) { + return attach( + pathTemplate, + new Redirector(getContext(), targetUri, Redirector.MODE_CLIENT_SEE_OTHER)); + } + + /** + * Attaches a temporary redirection to this router based on a given URI pattern. The client is + * expected to reuse the same method for the new request. + * + * @param pathTemplate The URI path template that must match the relative part of the resource + * URI. + * @param targetUri The target URI. + * @return The created route. + */ + public TemplateRoute redirectTemporary(String pathTemplate, String targetUri) { + return attach( + pathTemplate, + new Redirector(getContext(), targetUri, Redirector.MODE_CLIENT_TEMPORARY)); + } + + /** + * Sets the default matching mode to use when selecting routes based on URIs. By default it is + * set to {@link Template#MODE_EQUALS}. + * + * @param defaultMatchingMode The default matching mode. + */ + public void setDefaultMatchingMode(int defaultMatchingMode) { + this.defaultMatchingMode = defaultMatchingMode; + } + + /** + * Sets the default setting for whether the routing should be done on URIs with or without + * taking into account the query string. By default, it is set to false. + * + * @param defaultMatchingQuery The default setting for whether the routing should be done on + * URIs with or without taking into account query string. + */ + public void setDefaultMatchingQuery(boolean defaultMatchingQuery) { + this.defaultMatchingQuery = defaultMatchingQuery; + } + + /** + * Sets the default route tested if no other one was available. + * + * @param defaultRoute The default route tested if no other one was available. + */ + public void setDefaultRoute(Route defaultRoute) { + this.defaultRoute = defaultRoute; + } + + /** + * Sets the maximum number of attempts if no attachment could be matched on the first attempt. + * This is useful when the attachment scoring is dynamic and therefore could change on a retry. + * + * @param maxAttempts The maximum number of attempts. + */ + public void setMaxAttempts(int maxAttempts) { + this.maxAttempts = maxAttempts; + } + + /** + * Sets the score required to have a match. By default, it is set to {@code 0.5}. + * + * @param score The score required to have a match. + */ + public void setRequiredScore(float score) { + this.requiredScore = score; + } + + /** + * Sets the delay in milliseconds before a new attempt is made. By default, it is set to {@code + * 500}. + * + * @param retryDelay The delay in milliseconds before a new attempt is made. + */ + public void setRetryDelay(long retryDelay) { + this.retryDelay = retryDelay; + } + + /** + * Sets the modifiable list of routes. + * + * @param routes The modifiable list of routes. + */ + public void setRoutes(RouteList routes) { + this.routes = routes; + } + + /** + * Sets the routing mode. By default, it is set to the {@link #MODE_FIRST_MATCH} mode. + * + * @param routingMode The routing mode. + */ + public void setRoutingMode(int routingMode) { + this.routingMode = routingMode; + } + + /** Starts the filter and the attached routes. */ + @Override + public synchronized void start() throws Exception { + if (isStopped()) { + for (Route route : getRoutes()) { + route.start(); + } + + if (getDefaultRoute() != null) { + getDefaultRoute().start(); + } + + // Must be invoked as a last step + super.start(); + } + } + + /** Stops the filter and the attached routes. */ + @Override + public synchronized void stop() throws Exception { + if (isStarted()) { + // Must be invoked as a first step + super.stop(); + + if (getDefaultRoute() != null) { + getDefaultRoute().stop(); + } + + for (Route route : getRoutes()) { + route.stop(); + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/Template.java b/org.restlet/src/main/java/org/restlet/routing/Template.java index b04a207776..6121ad3dc8 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Template.java +++ b/org.restlet/src/main/java/org/restlet/routing/Template.java @@ -1,20 +1,13 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.Reference; -import org.restlet.util.Resolver; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -23,810 +16,841 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Reference; +import org.restlet.util.Resolver; /** - * String template with a pluggable model. Supports both formatting and parsing. - * The template variables can be inserted using the "{name}" syntax and - * described using the modifiable map of variable descriptors. When no - * descriptor is found for a given variable, the template logic uses its default - * variable property initialized using the default {@link Variable} - * constructor.
+ * String template with a pluggable model. Supports both formatting and parsing. The template + * variables can be inserted using the "{name}" syntax and described using the modifiable map of + * variable descriptors. When no descriptor is found for a given variable, the template logic uses + * its default variable property initialized using the default {@link Variable} constructor.
*
- * Note that the variable descriptors can be changed before the first parsing or - * matching call. After that point, changes won't be taken into account.
+ * Note that the variable descriptors can be changed before the first parsing or matching call. + * After that point, changes won't be taken into account.
*
- * Format and parsing methods are specially available to deal with requests and - * response. See {@link #format(Request, Response)} and - * {@link #parse(String, Request)}. - * + * Format and parsing methods are especially available to deal with requests and response. See + * {@link #format(Request, Response)} and {@link #parse(String, Request)}. + * * @see Resolver - * @see URI Template - * specification + * @see URI Template specification * @author Jerome Louvel */ public class Template { - /** Mode where all characters must match the template and size be identical. */ - public static final int MODE_EQUALS = 2; - - /** Mode where characters at the beginning must match the template. */ - public static final int MODE_STARTS_WITH = 1; - - /** - * Appends to a pattern a repeating group of a given content based on a class of - * characters. - * - * @param pattern The pattern to append to. - * @param content The content of the group. - * @param required Indicates if the group is required. - */ - private static void appendClass(StringBuilder pattern, String content, boolean required) { - - pattern.append("("); - - if (content.equals(".")) { - // Special case for the TYPE_ALL variable type because the - // dot looses its meaning inside a character class - pattern.append(content); - } else { - pattern.append("[").append(content).append(']'); - } - - if (required) { - pattern.append("+"); - } else { - pattern.append("*"); - } - - pattern.append(")"); - } - - /** - * Appends to a pattern a repeating group of a given content based on a - * non-capturing group. - * - * @param pattern The pattern to append to. - * @param content The content of the group. - * @param required Indicates if the group is required. - */ - private static void appendGroup(StringBuilder pattern, String content, boolean required) { - pattern.append("((?:").append(content).append(')'); - - if (required) { - pattern.append("+"); - } else { - pattern.append("*"); - } - - pattern.append(")"); - } - - /** - * Returns the Regex pattern string corresponding to a variable. - * - * @param variable The variable. - * @return The Regex pattern string corresponding to a variable. - */ - private static String getVariableRegex(Variable variable) { - String result = null; - - if (variable.isFixed()) { - result = "(" + Pattern.quote(variable.getDefaultValue()) + ")"; - } else { - // Expressions to create character classes - final String ALL = "."; - final String ALPHA = "a-zA-Z"; - final String DIGIT = "\\d"; - final String ALPHA_DIGIT = ALPHA + DIGIT; - final String HEXA = DIGIT + "ABCDEFabcdef"; - final String URI_UNRESERVED = ALPHA_DIGIT + "\\-\\.\\_\\~"; - final String URI_GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"; - final String URI_SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="; - final String URI_RESERVED = URI_GEN_DELIMS + URI_SUB_DELIMS; - final String WORD = "\\w"; - - // Basic rules expressed by the HTTP rfc. - final String CRLF = "\\r\\n"; - final String CTL = "\\p{Cntrl}"; - final String LWS = CRLF + "\\ \\t"; - final String SEPARATOR = "\\(\\)\\<\\>\\@\\,\\;\\:\\[\\]\"\\/\\\\?\\=\\{\\}\\ \\t"; - final String TOKEN = "[^" + SEPARATOR + "]"; - final String COMMENT = "[^" + CTL + "]" + "[^\\(\\)]" + LWS; - final String COMMENT_ATTRIBUTE = "[^\\;\\(\\)]"; - - // Expressions to create non-capturing groups - final String PCT_ENCODED = "\\%[" + HEXA + "][" + HEXA + "]"; - // final String PCHAR = "[" + URI_UNRESERVED + "]|(?:" + PCT_ENCODED - // + ")|[" + URI_SUB_DELIMS + "]|\\:|\\@"; - final String PCHAR = "[" + URI_UNRESERVED + URI_SUB_DELIMS + "\\:\\@]|(?:" + PCT_ENCODED + ")"; - final String QUERY = PCHAR + "|\\/|\\?"; - final String FRAGMENT = QUERY; - final String URI_PATH = PCHAR + "|\\/"; - final String URI_ALL = "[" + URI_RESERVED + URI_UNRESERVED + "]|(?:" + PCT_ENCODED + ")"; - - // Special case of query parameter characters - final String QUERY_PARAM_DELIMS = "\\!\\$\\'\\(\\)\\*\\+\\,\\;"; - final String QUERY_PARAM_CHAR = "[" + URI_UNRESERVED + QUERY_PARAM_DELIMS + "\\:\\@]|(?:" + PCT_ENCODED - + ")"; - final String QUERY_PARAM = QUERY_PARAM_CHAR + "|\\/|\\?"; - - final StringBuilder coreRegex = new StringBuilder(); - - switch (variable.getType()) { - case Variable.TYPE_ALL: - appendClass(coreRegex, ALL, variable.isRequired()); - break; - case Variable.TYPE_ALPHA: - appendClass(coreRegex, ALPHA, variable.isRequired()); - break; - case Variable.TYPE_DIGIT: - appendClass(coreRegex, DIGIT, variable.isRequired()); - break; - case Variable.TYPE_ALPHA_DIGIT: - appendClass(coreRegex, ALPHA_DIGIT, variable.isRequired()); - break; - case Variable.TYPE_URI_ALL: - appendGroup(coreRegex, URI_ALL, variable.isRequired()); - break; - case Variable.TYPE_URI_UNRESERVED: - appendClass(coreRegex, URI_UNRESERVED, variable.isRequired()); - break; - case Variable.TYPE_WORD: - appendClass(coreRegex, WORD, variable.isRequired()); - break; - case Variable.TYPE_URI_FRAGMENT: - appendGroup(coreRegex, FRAGMENT, variable.isRequired()); - break; - case Variable.TYPE_URI_PATH: - appendGroup(coreRegex, URI_PATH, variable.isRequired()); - break; - case Variable.TYPE_URI_QUERY: - appendGroup(coreRegex, QUERY, variable.isRequired()); - break; - case Variable.TYPE_URI_QUERY_PARAM: - appendGroup(coreRegex, QUERY_PARAM, variable.isRequired()); - break; - case Variable.TYPE_URI_SEGMENT: - appendGroup(coreRegex, PCHAR, variable.isRequired()); - break; - case Variable.TYPE_TOKEN: - appendClass(coreRegex, TOKEN, variable.isRequired()); - break; - case Variable.TYPE_COMMENT: - appendClass(coreRegex, COMMENT, variable.isRequired()); - break; - case Variable.TYPE_COMMENT_ATTRIBUTE: - appendClass(coreRegex, COMMENT_ATTRIBUTE, variable.isRequired()); - break; - } - - result = coreRegex.toString(); - } - - return result; - } - - /** The default variable to use when no matching variable descriptor exists. */ - private volatile Variable defaultVariable; - - /** True if the variables must be encoded when formatting the template. */ - private volatile boolean encodingVariables; - - /** The logger to use. */ - private volatile Logger logger; - - /** The matching mode to use when parsing a formatted reference. */ - private volatile int matchingMode; - - /** The pattern to use for formatting or parsing. */ - private volatile String pattern; - - /** The internal Regex pattern. */ - private volatile Pattern regexPattern; - - /** The sequence of Regex variable names as found in the pattern string. */ - private volatile List regexVariables; - - /** The map of variables associated to the route's template. */ - private final Map variables; - - /** - * Default constructor. Each variable matches any sequence of characters by - * default. When parsing, the template will attempt to match the whole template. - * When formatting, the variable are replaced by an empty string if they don't - * exist in the model. - * - * @param pattern The pattern to use for formatting or parsing. - */ - public Template(String pattern) { - this(pattern, MODE_EQUALS, Variable.TYPE_ALL, "", true, false); - } - - /** - * Constructor. - * - * @param pattern The pattern to use for formatting or parsing. - * @param matchingMode The matching mode to use when parsing a formatted - * reference. - */ - public Template(String pattern, int matchingMode) { - this(pattern, matchingMode, Variable.TYPE_ALL, "", true, false); - } - - /** - * Constructor. - * - * @param pattern The pattern to use for formatting or parsing. - * @param matchingMode The matching mode to use when parsing a formatted - * reference. - * @param defaultType The default type of variables with no descriptor. - * @param defaultDefaultValue The default value for null variables with no - * descriptor. - * @param defaultRequired The default required flag for variables with no - * descriptor. - * @param defaultFixed The default fixed value for variables with no - * descriptor. - */ - public Template(String pattern, int matchingMode, int defaultType, String defaultDefaultValue, - boolean defaultRequired, boolean defaultFixed) { - this(pattern, matchingMode, defaultType, defaultDefaultValue, defaultRequired, defaultFixed, false); - } - - /** - * Constructor. - * - * @param pattern The pattern to use for formatting or parsing. - * @param matchingMode The matching mode to use when parsing a formatted - * reference. - * @param defaultType The default type of variables with no descriptor. - * @param defaultDefaultValue The default value for null variables with no - * descriptor. - * @param defaultRequired The default required flag for variables with no - * descriptor. - * @param defaultFixed The default fixed value for variables with no - * descriptor. - * @param encodingVariables True if the variables must be encoded when - * formatting the template. - */ - public Template(String pattern, int matchingMode, int defaultType, String defaultDefaultValue, - boolean defaultRequired, boolean defaultFixed, boolean encodingVariables) { - this.logger = (logger == null) ? Context.getCurrentLogger() : logger; - this.pattern = pattern; - this.defaultVariable = new Variable(defaultType, defaultDefaultValue, defaultRequired, defaultFixed); - this.matchingMode = matchingMode; - this.variables = new ConcurrentHashMap(); - this.regexPattern = null; - this.encodingVariables = encodingVariables; - } - - /** - * Creates a formatted string based on the given map of values. - * - * @param values The values to use when formatting. - * @return The formatted string. - * @see Resolver#createResolver(Map) - */ - public String format(Map values) { - return format(Resolver.createResolver(values)); - } - - /** - * Creates a formatted string based on the given request and response. - * - * @param request The request to use as a model. - * @param response The response to use as a model. - * @return The formatted string. - * @see Resolver#createResolver(Request, Response) - */ - public String format(Request request, Response response) { - return format(Resolver.createResolver(request, response)); - } - - /** - * Creates a formatted string based on the given variable resolver. - * - * @param resolver The variable resolver to use. - * @return The formatted string. - */ - public String format(Resolver resolver) { - final StringBuilder result = new StringBuilder(); - StringBuilder varBuffer = null; - char next; - boolean inVariable = false; - final int patternLength = getPattern().length(); - for (int i = 0; i < patternLength; i++) { - next = getPattern().charAt(i); - - if (inVariable) { - if (Reference.isUnreserved(next)) { - // Append to the variable name - varBuffer.append(next); - } else if (next == '}') { - // End of variable detected - if (varBuffer.length() == 0) { - getLogger().warning("Empty pattern variables are not allowed : " + this.regexPattern); - } else { - final String varName = varBuffer.toString(); - Object varValue = resolver.resolve(varName); - - Variable var = getVariables().get(varName); - - // Use the default values instead - if (varValue == null) { - if (var == null) { - var = getDefaultVariable(); - } - - if (var != null) { - varValue = var.getDefaultValue(); - } - } - - String varValueString = (varValue == null) ? null : varValue.toString(); - - if (this.encodingVariables) { - // In case the values must be encoded. - if (var != null) { - result.append(var.encode(varValueString)); - } else { - result.append(Reference.encode(varValueString)); - } - } else { - if ((var != null) && var.isEncodingOnFormat()) { - result.append(Reference.encode(varValueString)); - } else { - result.append(varValueString); - } - } - - // Reset the variable name buffer - varBuffer = new StringBuilder(); - } - inVariable = false; - } else { - getLogger().warning( - "An invalid character was detected inside a pattern variable : " + this.regexPattern); - } - } else { - if (next == '{') { - inVariable = true; - varBuffer = new StringBuilder(); - } else if (next == '}') { - getLogger().warning( - "An invalid character was detected inside a pattern variable : " + this.regexPattern); - } else { - result.append(next); - } - } - } - return result.toString(); - } - - /** - * Returns the default variable. - * - * @return The default variable. - */ - public Variable getDefaultVariable() { - return this.defaultVariable; - } - - /** - * Returns the logger to use. - * - * @return The logger to use. - */ - public Logger getLogger() { - return this.logger; - } - - /** - * Returns the matching mode to use when parsing a formatted reference. - * - * @return The matching mode to use when parsing a formatted reference. - */ - public int getMatchingMode() { - return this.matchingMode; - } - - /** - * Returns the pattern to use for formatting or parsing. - * - * @return The pattern to use for formatting or parsing. - */ - public String getPattern() { - return this.pattern; - } - - /** - * Compiles the URI pattern into a Regex pattern. - * - * @return The Regex pattern. - */ - private Pattern getRegexPattern() { - if (this.regexPattern == null) { - synchronized (this) { - if (this.regexPattern == null) { - getRegexVariables().clear(); - final StringBuilder patternBuffer = new StringBuilder(); - StringBuilder varBuffer = null; - char next; - boolean inVariable = false; - for (int i = 0; i < getPattern().length(); i++) { - next = getPattern().charAt(i); - - if (inVariable) { - if (Reference.isUnreserved(next)) { - // Append to the variable name - varBuffer.append(next); - } else if (next == '}') { - // End of variable detected - if (varBuffer.length() == 0) { - getLogger() - .warning("Empty pattern variables are not allowed : " + this.regexPattern); - } else { - final String varName = varBuffer.toString(); - final int varIndex = getRegexVariables().indexOf(varName); - - if (varIndex != -1) { - // The variable is used several times in - // the pattern, ensure that this - // constraint is enforced when parsing. - patternBuffer.append("\\").append(varIndex + 1); - } else { - // New variable detected. Insert a - // capturing group. - getRegexVariables().add(varName); - Variable var = getVariables().get(varName); - if (var == null) { - var = getDefaultVariable(); - } - patternBuffer.append(getVariableRegex(var)); - } - - // Reset the variable name buffer - varBuffer = new StringBuilder(); - } - inVariable = false; - - } else { - getLogger().warning("An invalid character was detected inside a pattern variable : " - + this.regexPattern); - } - } else { - if (next == '{') { - inVariable = true; - varBuffer = new StringBuilder(); - } else if (next == '}') { - getLogger().warning("An invalid character was detected inside a pattern variable : " - + this.regexPattern); - } else { - patternBuffer.append(quote(next)); - } - } - } - - this.regexPattern = Pattern.compile(patternBuffer.toString()); - } - } - } - - return this.regexPattern; - } - - /** - * Returns the sequence of Regex variable names as found in the pattern string. - * - * @return The sequence of Regex variable names as found in the pattern string. - */ - private List getRegexVariables() { - // Lazy initialization with double-check. - List rv = this.regexVariables; - if (rv == null) { - synchronized (this) { - rv = this.regexVariables; - if (rv == null) { - this.regexVariables = rv = new CopyOnWriteArrayList(); - } - } - } - return rv; - } - - /** - * Returns the list of variable names in the template. - * - * @return The list of variable names. - */ - public List getVariableNames() { - final List result = new ArrayList(); - StringBuilder varBuffer = null; - char next; - boolean inVariable = false; - final String pattern = getPattern(); - - for (int i = 0; i < pattern.length(); i++) { - next = pattern.charAt(i); - - if (inVariable) { - if (Reference.isUnreserved(next)) { - // Append to the variable name - varBuffer.append(next); - } else if (next == '}') { - // End of variable detected - if (varBuffer.length() == 0) { - getLogger().warning("Empty pattern variables are not allowed : " + this.pattern); - } else { - result.add(varBuffer.toString()); - - // Reset the variable name buffer - varBuffer = new StringBuilder(); - } - - inVariable = false; - } else { - getLogger() - .warning("An invalid character was detected inside a pattern variable : " + this.pattern); - } - } else { - if (next == '{') { - inVariable = true; - varBuffer = new StringBuilder(); - } else if (next == '}') { - getLogger() - .warning("An invalid character was detected inside a pattern variable : " + this.pattern); - } - } - } - - return result; - } - - /** - * Returns the modifiable map of variable descriptors. Creates a new instance if - * no one has been set. Note that those variables are only descriptors that can - * influence the way parsing and formatting is done, they don't contain the - * actual value parsed. - * - * @return The modifiable map of variables. - */ - public synchronized Map getVariables() { - return this.variables; - } - - /** - * Indicates if the variables must be encoded when formatting the template. - * - * @return True if the variables must be encoded when formatting the template, - * false otherwise. - */ - public boolean isEncodingVariables() { - return this.encodingVariables; - } - - /** - * Indicates if the current pattern matches the given formatted string. - * - * @param formattedString The formatted string to match. - * @return The number of matched characters or -1 if the match failed. - */ - public int match(String formattedString) { - int result = -1; - - try { - if (formattedString != null) { - final Matcher matcher = getRegexPattern().matcher(formattedString); - - if ((getMatchingMode() == MODE_EQUALS) && matcher.matches()) { - result = matcher.end(); - } else if ((getMatchingMode() == MODE_STARTS_WITH) && matcher.lookingAt()) { - result = matcher.end(); - } - } - } catch (StackOverflowError soe) { - getLogger().warning( - "StackOverflowError exception encountered while matching this string : " + formattedString); - } - - return result; - } - - /** - * Attempts to parse a formatted reference. If the parsing succeeds, the given - * request's attributes are updated.
- * Note that the values parsed are directly extracted from the formatted - * reference and are therefore not percent-decoded. - * - * @see Reference#decode(String) - * - * @param formattedString The string to parse. - * @param variables The map of variables to update. - * @return The number of matched characters or -1 if no character matched. - */ - public int parse(String formattedString, Map variables) { - return parse(formattedString, variables, true); - } - - /** - * Attempts to parse a formatted reference. If the parsing succeeds, the given - * request's attributes are updated.
- * Note that the values parsed are directly extracted from the formatted - * reference and are therefore not percent-decoded. - * - * @see Reference#decode(String) - * - * @param formattedString The string to parse. - * @param variables The map of variables to update. - * @param loggable True if the parsing should be logged. - * @return The number of matched characters or -1 if no character matched. - */ - public int parse(String formattedString, Map variables, boolean loggable) { - int result = -1; - - if (formattedString != null) { - try { - Matcher matcher = getRegexPattern().matcher(formattedString); - boolean matched = ((getMatchingMode() == MODE_EQUALS) && matcher.matches()) - || ((getMatchingMode() == MODE_STARTS_WITH) && matcher.lookingAt()); - - if (matched) { - // Update the number of matched characters - result = matcher.end(); - - // Update the attributes with the variables value - String attributeName = null; - String attributeValue = null; - - for (int i = 0; i < getRegexVariables().size(); i++) { - attributeName = getRegexVariables().get(i); - attributeValue = matcher.group(i + 1); - Variable var = getVariables().get(attributeName); - - if ((var != null) && var.isDecodingOnParse()) { - attributeValue = Reference.decode(attributeValue); - } - - if (loggable) { - getLogger().fine("Template variable \"" + attributeName + "\" matched with value \"" - + attributeValue + "\""); - } - - variables.put(attributeName, attributeValue); - } - } - } catch (StackOverflowError soe) { - getLogger().warning( - "StackOverflowError exception encountered while matching this string : " + formattedString); - } - } - - return result; - } - - /** - * Attempts to parse a formatted reference. If the parsing succeeds, the given - * request's attributes are updated.
- * Note that the values parsed are directly extracted from the formatted - * reference and are therefore not percent-decoded. - * - * @see Reference#decode(String) - * - * @param formattedString The string to parse. - * @param request The request to update. - * @return The number of matched characters or -1 if no character matched. - */ - public int parse(String formattedString, Request request) { - return parse(formattedString, request.getAttributes(), request.isLoggable()); - } - - /** - * Quotes special characters that could be taken for special Regex characters. - * - * @param character The character to quote if necessary. - * @return The quoted character. - */ - private String quote(char character) { - switch (character) { - case '[': - return "\\["; - case ']': - return "\\]"; - case '.': - return "\\."; - case '\\': - return "\\\\"; - case '$': - return "\\$"; - case '^': - return "\\^"; - case '?': - return "\\?"; - case '*': - return "\\*"; - case '|': - return "\\|"; - case '(': - return "\\("; - case ')': - return "\\)"; - case ':': - return "\\:"; - case '-': - return "\\-"; - case '!': - return "\\!"; - case '<': - return "\\<"; - case '>': - return "\\>"; - default: - return Character.toString(character); - } - } - - /** - * Sets the variable to use, if no variable is given. - * - * @param defaultVariable - */ - public void setDefaultVariable(Variable defaultVariable) { - this.defaultVariable = defaultVariable; - } - - /** - * Indicates if the variables must be encoded when formatting the template. - * - * @param encodingVariables True if the variables must be encoded when - * formatting the template. - */ - public void setEncodingVariables(boolean encodingVariables) { - this.encodingVariables = encodingVariables; - } - - /** - * Sets the logger to use. - * - * @param logger The logger to use. - */ - public void setLogger(Logger logger) { - this.logger = logger; - } - - /** - * Sets the matching mode to use when parsing a formatted reference. - * - * @param matchingMode The matching mode to use when parsing a formatted - * reference. - */ - public void setMatchingMode(int matchingMode) { - this.matchingMode = matchingMode; - } - - /** - * Sets the pattern to use for formatting or parsing. - * - * @param pattern The pattern to use for formatting or parsing. - */ - public void setPattern(String pattern) { - this.pattern = pattern; - this.regexPattern = null; - } - - /** - * Sets the modifiable map of variables. - * - * @param variables The modifiable map of variables. - */ - public void setVariables(Map variables) { - synchronized (this.variables) { - if (variables != this.variables) { - this.variables.clear(); - - if (variables != null) { - this.variables.putAll(variables); - } - } - } - } - + /** Mode where all characters must match the template and size be identical. */ + public static final int MODE_EQUALS = 2; + + /** Mode where characters at the beginning must match the template. */ + public static final int MODE_STARTS_WITH = 1; + + /** + * Appends to a pattern a repeating group of a given content based on a class of characters. + * + * @param pattern The pattern to append to. + * @param content The content of the group. + * @param required Indicates if the group is required. + */ + private static void appendClass(StringBuilder pattern, String content, boolean required) { + + pattern.append("("); + + if (content.equals(".")) { + // Special case for the TYPE_ALL variable type because the + // dot loses its meaning inside a character class + pattern.append(content); + } else { + pattern.append("[").append(content).append(']'); + } + + if (required) { + pattern.append("+"); + } else { + pattern.append("*"); + } + + pattern.append(")"); + } + + /** + * Appends to a pattern a repeating group of a given content based on a non-capturing group. + * + * @param pattern The pattern to append to. + * @param content The content of the group. + * @param required Indicates if the group is required. + */ + private static void appendGroup(StringBuilder pattern, String content, boolean required) { + pattern.append("((?:").append(content).append(')'); + + if (required) { + pattern.append("+"); + } else { + pattern.append("*"); + } + + pattern.append(")"); + } + + /** + * Returns the Regex pattern string corresponding to a variable. + * + * @param variable The variable. + * @return The Regex pattern string corresponding to a variable. + */ + private static String getVariableRegex(Variable variable) { + String result = null; + + if (variable.isFixed()) { + result = "(" + Pattern.quote(variable.getDefaultValue()) + ")"; + } else { + // Expressions to create character classes + final String ALL = "."; + final String ALPHA = "a-zA-Z"; + final String DIGIT = "\\d"; + final String ALPHA_DIGIT = ALPHA + DIGIT; + final String HEXA = DIGIT + "ABCDEFabcdef"; + final String URI_UNRESERVED = ALPHA_DIGIT + "\\-\\.\\_\\~"; + final String URI_GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"; + final String URI_SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="; + final String URI_RESERVED = URI_GEN_DELIMS + URI_SUB_DELIMS; + final String WORD = "\\w"; + + // Basic rules expressed by the HTTP rfc. + final String CRLF = "\\r\\n"; + final String CTL = "\\p{Cntrl}"; + final String LWS = CRLF + "\\ \\t"; + final String SEPARATOR = "\\(\\)\\<\\>\\@\\,\\;\\:\\[\\]\"\\/\\\\?\\=\\{\\}\\ \\t"; + final String TOKEN = "[^" + SEPARATOR + "]"; + final String COMMENT = "[^" + CTL + "]" + "[^\\(\\)]" + LWS; + final String COMMENT_ATTRIBUTE = "[^\\;\\(\\)]"; + + // Expressions to create non-capturing groups + final String PCT_ENCODED = "\\%[" + HEXA + "][" + HEXA + "]"; + // final String PCHAR = "[" + URI_UNRESERVED + "]|(?:" + PCT_ENCODED + // + ")|[" + URI_SUB_DELIMS + "]|\\:|\\@"; + final String PCHAR = + "[" + URI_UNRESERVED + URI_SUB_DELIMS + "\\:\\@]|(?:" + PCT_ENCODED + ")"; + final String QUERY = PCHAR + "|\\/|\\?"; + final String FRAGMENT = QUERY; + final String URI_PATH = PCHAR + "|\\/"; + final String URI_ALL = + "[" + URI_RESERVED + URI_UNRESERVED + "]|(?:" + PCT_ENCODED + ")"; + + // Special case of query parameter characters + final String QUERY_PARAM_DELIMS = "\\!\\$\\'\\(\\)\\*\\+\\,\\;"; + final String QUERY_PARAM_CHAR = + "[" + URI_UNRESERVED + QUERY_PARAM_DELIMS + "\\:\\@]|(?:" + PCT_ENCODED + ")"; + final String QUERY_PARAM = QUERY_PARAM_CHAR + "|\\/|\\?"; + + final StringBuilder coreRegex = new StringBuilder(); + + switch (variable.getType()) { + case Variable.TYPE_ALL: + appendClass(coreRegex, ALL, variable.isRequired()); + break; + case Variable.TYPE_ALPHA: + appendClass(coreRegex, ALPHA, variable.isRequired()); + break; + case Variable.TYPE_DIGIT: + appendClass(coreRegex, DIGIT, variable.isRequired()); + break; + case Variable.TYPE_ALPHA_DIGIT: + appendClass(coreRegex, ALPHA_DIGIT, variable.isRequired()); + break; + case Variable.TYPE_URI_ALL: + appendGroup(coreRegex, URI_ALL, variable.isRequired()); + break; + case Variable.TYPE_URI_UNRESERVED: + appendClass(coreRegex, URI_UNRESERVED, variable.isRequired()); + break; + case Variable.TYPE_WORD: + appendClass(coreRegex, WORD, variable.isRequired()); + break; + case Variable.TYPE_URI_FRAGMENT: + appendGroup(coreRegex, FRAGMENT, variable.isRequired()); + break; + case Variable.TYPE_URI_PATH: + appendGroup(coreRegex, URI_PATH, variable.isRequired()); + break; + case Variable.TYPE_URI_QUERY: + appendGroup(coreRegex, QUERY, variable.isRequired()); + break; + case Variable.TYPE_URI_QUERY_PARAM: + appendGroup(coreRegex, QUERY_PARAM, variable.isRequired()); + break; + case Variable.TYPE_URI_SEGMENT: + appendGroup(coreRegex, PCHAR, variable.isRequired()); + break; + case Variable.TYPE_TOKEN: + appendClass(coreRegex, TOKEN, variable.isRequired()); + break; + case Variable.TYPE_COMMENT: + appendClass(coreRegex, COMMENT, variable.isRequired()); + break; + case Variable.TYPE_COMMENT_ATTRIBUTE: + appendClass(coreRegex, COMMENT_ATTRIBUTE, variable.isRequired()); + break; + } + + result = coreRegex.toString(); + } + + return result; + } + + /** The default variable to use when no matching variable descriptor exists. */ + private volatile Variable defaultVariable; + + /** True if the variables must be encoded when formatting the template. */ + private volatile boolean encodingVariables; + + /** The logger to use. */ + private volatile Logger logger; + + /** The matching mode to use when parsing a formatted reference. */ + private volatile int matchingMode; + + /** The pattern to use for formatting or parsing. */ + private volatile String pattern; + + /** The internal Regex pattern. */ + private volatile Pattern regexPattern; + + /** The sequence of Regex variable names as found in the pattern string. */ + private volatile List regexVariables; + + /** The map of variables associated with the route's template. */ + private final Map variables; + + /** + * Default constructor. Each variable matches any sequence of characters by default. When + * parsing, the template will attempt to match the whole template. When formatting, the variable + * is replaced by an empty string if they don't exist in the model. + * + * @param pattern The pattern to use for formatting or parsing. + */ + public Template(String pattern) { + this(pattern, MODE_EQUALS, Variable.TYPE_ALL, "", true, false); + } + + /** + * Constructor. + * + * @param pattern The pattern to use for formatting or parsing. + * @param matchingMode The matching mode to use when parsing a formatted reference. + */ + public Template(String pattern, int matchingMode) { + this(pattern, matchingMode, Variable.TYPE_ALL, "", true, false); + } + + /** + * Constructor. + * + * @param pattern The pattern to use for formatting or parsing. + * @param matchingMode The matching mode to use when parsing a formatted reference. + * @param defaultType The default type of variables with no descriptor. + * @param defaultDefaultValue The default value for null variables with no descriptor. + * @param defaultRequired The default required flag for variables with no descriptor. + * @param defaultFixed The default fixed value for variables with no descriptor. + */ + public Template( + String pattern, + int matchingMode, + int defaultType, + String defaultDefaultValue, + boolean defaultRequired, + boolean defaultFixed) { + this( + pattern, + matchingMode, + defaultType, + defaultDefaultValue, + defaultRequired, + defaultFixed, + false); + } + + /** + * Constructor. + * + * @param pattern The pattern to use for formatting or parsing. + * @param matchingMode The matching mode to use when parsing a formatted reference. + * @param defaultType The default type of variables with no descriptor. + * @param defaultDefaultValue The default value for null variables with no descriptor. + * @param defaultRequired The default required flag for variables with no descriptor. + * @param defaultFixed The default fixed value for variables with no descriptor. + * @param encodingVariables True if the variables must be encoded when formatting the template. + */ + public Template( + String pattern, + int matchingMode, + int defaultType, + String defaultDefaultValue, + boolean defaultRequired, + boolean defaultFixed, + boolean encodingVariables) { + this.logger = (logger == null) ? Context.getCurrentLogger() : logger; + this.pattern = pattern; + this.defaultVariable = + new Variable(defaultType, defaultDefaultValue, defaultRequired, defaultFixed); + this.matchingMode = matchingMode; + this.variables = new ConcurrentHashMap<>(); + this.regexPattern = null; + this.encodingVariables = encodingVariables; + } + + /** + * Creates a formatted string based on the given map of values. + * + * @param values The values to use when formatting. + * @return The formatted string. + * @see Resolver#createResolver(Map) + */ + public String format(Map values) { + return format(Resolver.createResolver(values)); + } + + /** + * Creates a formatted string based on the given request and response. + * + * @param request The request to use as a model. + * @param response The response to use as a model. + * @return The formatted string. + * @see Resolver#createResolver(Request, Response) + */ + public String format(Request request, Response response) { + return format(Resolver.createResolver(request, response)); + } + + /** + * Creates a formatted string based on the given variable resolver. + * + * @param resolver The variable resolver to use. + * @return The formatted string. + */ + public String format(Resolver resolver) { + final StringBuilder result = new StringBuilder(); + StringBuilder varBuffer = null; + char next; + boolean inVariable = false; + final int patternLength = getPattern().length(); + for (int i = 0; i < patternLength; i++) { + next = getPattern().charAt(i); + + if (inVariable) { + if (Reference.isUnreserved(next)) { + // Append to the variable name + varBuffer.append(next); + } else if (next == '}') { + // End of variable detected + if (varBuffer.isEmpty()) { + getLogger() + .warning( + "Empty pattern variables are not allowed : " + + this.regexPattern); + } else { + final String varName = varBuffer.toString(); + Object varValue = resolver.resolve(varName); + + Variable var = getVariables().get(varName); + + // Use the default values instead + if (varValue == null) { + if (var == null) { + var = getDefaultVariable(); + } + + if (var != null) { + varValue = var.getDefaultValue(); + } + } + + String varValueString = (varValue == null) ? null : varValue.toString(); + + if (this.encodingVariables) { + // In case the values must be encoded. + if (var != null) { + result.append(var.encode(varValueString)); + } else { + result.append(Reference.encode(varValueString)); + } + } else { + if ((var != null) && var.isEncodingOnFormat()) { + result.append(Reference.encode(varValueString)); + } else { + result.append(varValueString); + } + } + + // Reset the variable name buffer + varBuffer = new StringBuilder(); + } + inVariable = false; + } else { + getLogger() + .warning( + "An invalid character was detected inside a pattern variable : " + + this.regexPattern); + } + } else { + if (next == '{') { + inVariable = true; + varBuffer = new StringBuilder(); + } else if (next == '}') { + getLogger() + .warning( + "An invalid character was detected inside a pattern variable : " + + this.regexPattern); + } else { + result.append(next); + } + } + } + return result.toString(); + } + + /** + * Returns the default variable. + * + * @return The default variable. + */ + public Variable getDefaultVariable() { + return this.defaultVariable; + } + + /** + * Returns the logger to use. + * + * @return The logger to use. + */ + public Logger getLogger() { + return this.logger; + } + + /** + * Returns the matching mode to use when parsing a formatted reference. + * + * @return The matching mode to use when parsing a formatted reference. + */ + public int getMatchingMode() { + return this.matchingMode; + } + + /** + * Returns the pattern to use for formatting or parsing. + * + * @return The pattern to use for formatting or parsing. + */ + public String getPattern() { + return this.pattern; + } + + /** + * Compiles the URI pattern into a Regex pattern. + * + * @return The Regex pattern. + */ + private Pattern getRegexPattern() { + if (this.regexPattern == null) { + synchronized (this) { + if (this.regexPattern == null) { + getRegexVariables().clear(); + final StringBuilder patternBuffer = new StringBuilder(); + StringBuilder varBuffer = null; + char next; + boolean inVariable = false; + for (int i = 0; i < getPattern().length(); i++) { + next = getPattern().charAt(i); + + if (inVariable) { + if (Reference.isUnreserved(next)) { + // Append to the variable name + varBuffer.append(next); + } else if (next == '}') { + // End of variable detected + if (varBuffer.isEmpty()) { + getLogger() + .warning( + "Empty pattern variables are not allowed : " + + this.regexPattern); + } else { + final String varName = varBuffer.toString(); + final int varIndex = getRegexVariables().indexOf(varName); + + if (varIndex != -1) { + // The variable is used several times in + // the pattern, ensure that this + // constraint is enforced when parsing. + patternBuffer.append("\\").append(varIndex + 1); + } else { + // New variable detected. Insert a + // capturing group. + getRegexVariables().add(varName); + Variable var = getVariables().get(varName); + if (var == null) { + var = getDefaultVariable(); + } + patternBuffer.append(getVariableRegex(var)); + } + + // Reset the variable name buffer + varBuffer = new StringBuilder(); + } + inVariable = false; + + } else { + getLogger() + .warning( + "An invalid character was detected inside a pattern variable : " + + this.regexPattern); + } + } else { + if (next == '{') { + inVariable = true; + varBuffer = new StringBuilder(); + } else if (next == '}') { + getLogger() + .warning( + "An invalid character was detected inside a pattern variable : " + + this.regexPattern); + } else { + patternBuffer.append(quote(next)); + } + } + } + + this.regexPattern = Pattern.compile(patternBuffer.toString()); + } + } + } + + return this.regexPattern; + } + + /** + * Returns the sequence of Regex variable names as found in the pattern string. + * + * @return The sequence of Regex variable names as found in the pattern string. + */ + private List getRegexVariables() { + // Lazy initialization with double-check. + List rv = this.regexVariables; + if (rv == null) { + synchronized (this) { + rv = this.regexVariables; + if (rv == null) { + this.regexVariables = rv = new CopyOnWriteArrayList<>(); + } + } + } + return rv; + } + + /** + * Returns the list of variable names in the template. + * + * @return The list of variable names. + */ + public List getVariableNames() { + final List result = new ArrayList<>(); + StringBuilder varBuffer = null; + char next; + boolean inVariable = false; + final String pattern = getPattern(); + + for (int i = 0; i < pattern.length(); i++) { + next = pattern.charAt(i); + + if (inVariable) { + if (Reference.isUnreserved(next)) { + // Append to the variable name + varBuffer.append(next); + } else if (next == '}') { + // End of variable detected + if (varBuffer.isEmpty()) { + getLogger() + .warning( + "Empty pattern variables are not allowed : " + + this.pattern); + } else { + result.add(varBuffer.toString()); + + // Reset the variable name buffer + varBuffer = new StringBuilder(); + } + + inVariable = false; + } else { + getLogger() + .warning( + "An invalid character was detected inside a pattern variable : " + + this.pattern); + } + } else { + if (next == '{') { + inVariable = true; + varBuffer = new StringBuilder(); + } else if (next == '}') { + getLogger() + .warning( + "An invalid character was detected inside a pattern variable : " + + this.pattern); + } + } + } + + return result; + } + + /** + * Returns the modifiable map of variable descriptors. Creates a new instance if no one has been + * set. Note that those variables are only descriptors that can influence the way parsing and + * formatting are done; they don't contain the actual value parsed. + * + * @return The modifiable map of variables. + */ + public synchronized Map getVariables() { + return this.variables; + } + + /** + * Indicates if the variables must be encoded when formatting the template. + * + * @return True if the variables must be encoded when formatting the template, false otherwise. + */ + public boolean isEncodingVariables() { + return this.encodingVariables; + } + + /** + * Indicates if the current pattern matches the given formatted string. + * + * @param formattedString The formatted string to match. + * @return The number of matched characters or -1 if the match failed. + */ + public int match(String formattedString) { + int result = -1; + + try { + if (formattedString != null) { + final Matcher matcher = getRegexPattern().matcher(formattedString); + + if ((getMatchingMode() == MODE_EQUALS) && matcher.matches()) { + result = matcher.end(); + } else if ((getMatchingMode() == MODE_STARTS_WITH) && matcher.lookingAt()) { + result = matcher.end(); + } + } + } catch (StackOverflowError soe) { + getLogger() + .warning( + "StackOverflowError exception encountered while matching this string : " + + formattedString); + } + + return result; + } + + /** + * Attempts to parse a formatted reference. If the parsing succeeds, the given request's + * attributes are updated.
+ * Note that the values parsed are directly extracted from the formatted reference and are + * therefore not percent-decoded. + * + * @see Reference#decode(String) + * @param formattedString The string to parse. + * @param variables The map of variables to update. + * @return The number of matched characters or -1 if no character matched. + */ + public int parse(String formattedString, Map variables) { + return parse(formattedString, variables, true); + } + + /** + * Attempts to parse a formatted reference. If the parsing succeeds, the given request's + * attributes are updated.
+ * Note that the values parsed are directly extracted from the formatted reference and are + * therefore not percent-decoded. + * + * @see Reference#decode(String) + * @param formattedString The string to parse. + * @param variables The map of variables to update. + * @param loggable True if the parsing should be logged. + * @return The number of matched characters or -1 if no character matched. + */ + public int parse(String formattedString, Map variables, boolean loggable) { + int result = -1; + + if (formattedString != null) { + try { + Matcher matcher = getRegexPattern().matcher(formattedString); + boolean matched = + ((getMatchingMode() == MODE_EQUALS) && matcher.matches()) + || ((getMatchingMode() == MODE_STARTS_WITH) && matcher.lookingAt()); + + if (matched) { + // Update the number of matched characters + result = matcher.end(); + + // Update the attributes with the variables value + String attributeName = null; + String attributeValue = null; + + for (int i = 0; i < getRegexVariables().size(); i++) { + attributeName = getRegexVariables().get(i); + attributeValue = matcher.group(i + 1); + Variable var = getVariables().get(attributeName); + + if ((var != null) && var.isDecodingOnParse()) { + attributeValue = Reference.decode(attributeValue); + } + + if (loggable) { + getLogger() + .fine( + "Template variable \"" + + attributeName + + "\" matched with value \"" + + attributeValue + + "\""); + } + + variables.put(attributeName, attributeValue); + } + } + } catch (StackOverflowError soe) { + getLogger() + .warning( + "StackOverflowError exception encountered while matching this string : " + + formattedString); + } + } + + return result; + } + + /** + * Attempts to parse a formatted reference. If the parsing succeeds, the given request's + * attributes are updated.
+ * Note that the values parsed are directly extracted from the formatted reference and are + * therefore not percent-decoded. + * + * @see Reference#decode(String) + * @param formattedString The string to parse. + * @param request The request to update. + * @return The number of matched characters or -1 if no character matched. + */ + public int parse(String formattedString, Request request) { + return parse(formattedString, request.getAttributes(), request.isLoggable()); + } + + /** + * Quotes special characters that could be taken for special Regex characters. + * + * @param character The character to quote if necessary. + * @return The quoted character. + */ + private String quote(char character) { + switch (character) { + case '[': + return "\\["; + case ']': + return "\\]"; + case '.': + return "\\."; + case '\\': + return "\\\\"; + case '$': + return "\\$"; + case '^': + return "\\^"; + case '?': + return "\\?"; + case '*': + return "\\*"; + case '|': + return "\\|"; + case '(': + return "\\("; + case ')': + return "\\)"; + case ':': + return "\\:"; + case '-': + return "\\-"; + case '!': + return "\\!"; + case '<': + return "\\<"; + case '>': + return "\\>"; + default: + return Character.toString(character); + } + } + + /** + * Sets the variable to use if no variable is given. + * + * @param defaultVariable + */ + public void setDefaultVariable(Variable defaultVariable) { + this.defaultVariable = defaultVariable; + } + + /** + * Indicates if the variables must be encoded when formatting the template. + * + * @param encodingVariables True if the variables must be encoded when formatting the template. + */ + public void setEncodingVariables(boolean encodingVariables) { + this.encodingVariables = encodingVariables; + } + + /** + * Sets the logger to use. + * + * @param logger The logger to use. + */ + public void setLogger(Logger logger) { + this.logger = logger; + } + + /** + * Sets the matching mode to use when parsing a formatted reference. + * + * @param matchingMode The matching mode to use when parsing a formatted reference. + */ + public void setMatchingMode(int matchingMode) { + this.matchingMode = matchingMode; + } + + /** + * Sets the pattern to use for formatting or parsing. + * + * @param pattern The pattern to use for formatting or parsing. + */ + public void setPattern(String pattern) { + this.pattern = pattern; + this.regexPattern = null; + } + + /** + * Sets the modifiable map of variables. + * + * @param variables The modifiable map of variables. + */ + public void setVariables(Map variables) { + synchronized (this.variables) { + if (variables != this.variables) { + this.variables.clear(); + + if (variables != null) { + this.variables.putAll(variables); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java b/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java index 0ed7857070..0b80ae68f5 100644 --- a/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java +++ b/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java @@ -1,245 +1,269 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import java.util.logging.Level; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; import org.restlet.data.Reference; import org.restlet.data.Status; -import java.util.logging.Level; - /** - * Filter scoring the affinity of calls with the attached Restlet. The score is - * used by an associated Router to determine the most appropriate - * Restlet for a given call. The routing is based on a reference template.
+ * Filter scoring the affinity of calls with the attached Restlet. The score is used by an + * associated Router to determine the most appropriate Restlet for a given call. The routing is + * based on a reference template.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @see org.restlet.routing.Template * @author Jerome Louvel */ public class TemplateRoute extends Route { - /** - * Indicates whether the query part should be taken into account when matching a - * reference with the template. - */ - private volatile boolean matchingQuery; - - /** The reference template to match. */ - private volatile Template template; - - /** - * Constructor behaving as a simple extractor filter. - * - * @param next The next Restlet. - */ - public TemplateRoute(Restlet next) { - this(null, (Template) null, next); - } - - /** - * Constructor. The URIs will be matched against the template using the - * {@link Template#MODE_STARTS_WITH} matching mode. This can be changed by - * getting the template and calling {@link Template#setMatchingMode(int)} with - * {@link Template#MODE_EQUALS} for exact matching. - * - * @param router The parent router. - * @param uriTemplate The URI template. - * @param next The next Restlet. - */ - public TemplateRoute(Router router, String uriTemplate, Restlet next) { - this(router, new Template(uriTemplate, Template.MODE_STARTS_WITH, Variable.TYPE_URI_SEGMENT, "", true, false), - next); - } - - /** - * Constructor. - * - * @param router The parent router. - * @param template The URI template. - * @param next The next Restlet. - */ - public TemplateRoute(Router router, Template template, Restlet next) { - super(router, next); - this.matchingQuery = router == null || router.getDefaultMatchingQuery(); - this.template = template; - } - - /** - * Allows filtering before its handling by the target Restlet. By default, it - * parses the template variable, adjusts the base reference of the target - * resource's reference. - * - * @param request The request to filter. - * @param response The response to filter. - * @return The continuation status. - */ - @Override - protected int beforeHandle(Request request, Response response) { - // 1 - Parse the template variables and adjust the base reference - if (getTemplate() != null) { - String remainingPart = request.getResourceRef().getRemainingPart(false, isMatchingQuery()); - int matchedLength = getTemplate().parse(remainingPart, request); - - if (matchedLength == 0) { - if (request.isLoggable() && getLogger().isLoggable(Level.FINER)) { - getLogger().finer("No characters were matched"); - } - } else if (matchedLength > 0) { - if (request.isLoggable() && getLogger().isLoggable(Level.FINER)) { - getLogger().finer(matchedLength + " characters were matched"); - } - - // Updates the context - String matchedPart = remainingPart.substring(0, matchedLength); - Reference baseRef = request.getResourceRef().getBaseRef(); - - if (baseRef == null) { - baseRef = new Reference(matchedPart); - } else { - baseRef = new Reference(baseRef.toString(false, false) + matchedPart); - } - - request.getResourceRef().setBaseRef(baseRef); - - if (request.isLoggable()) { - if (getLogger().isLoggable(Level.FINE)) { - remainingPart = request.getResourceRef().getRemainingPart(false, isMatchingQuery()); + /** + * Indicates whether the query part should be taken into account when matching a reference with + * the template. + */ + private volatile boolean matchingQuery; + + /** The reference template to match. */ + private volatile Template template; + + /** + * Constructor behaving as a simple extractor filter. + * + * @param next The next Restlet. + */ + public TemplateRoute(Restlet next) { + this(null, (Template) null, next); + } + + /** + * Constructor. The URIs will be matched against the template using the {@link + * Template#MODE_STARTS_WITH} matching mode. This can be changed by getting the template and + * calling {@link Template#setMatchingMode(int)} with {@link Template#MODE_EQUALS} for exact + * matching. + * + * @param router The parent router. + * @param uriTemplate The URI template. + * @param next The next Restlet. + */ + public TemplateRoute(Router router, String uriTemplate, Restlet next) { + this( + router, + new Template( + uriTemplate, + Template.MODE_STARTS_WITH, + Variable.TYPE_URI_SEGMENT, + "", + true, + false), + next); + } + + /** + * Constructor. + * + * @param router The parent router. + * @param template The URI template. + * @param next The next Restlet. + */ + public TemplateRoute(Router router, Template template, Restlet next) { + super(router, next); + this.matchingQuery = router == null || router.getDefaultMatchingQuery(); + this.template = template; + } + + /** + * Allows filtering before its handling by the target Restlet. By default, it parses the + * template variable, adjusts the base reference of the target resource's reference. + * + * @param request The request to filter. + * @param response The response to filter. + * @return The continuation status. + */ + @Override + protected int beforeHandle(Request request, Response response) { + // 1 - Parse the template variables and adjust the base reference + if (getTemplate() != null) { + String remainingPart = + request.getResourceRef().getRemainingPart(false, isMatchingQuery()); + int matchedLength = getTemplate().parse(remainingPart, request); + + if (matchedLength == 0) { + if (request.isLoggable() && getLogger().isLoggable(Level.FINER)) { + getLogger().finer("No characters were matched"); + } + } else if (matchedLength > 0) { + if (request.isLoggable() && getLogger().isLoggable(Level.FINER)) { + getLogger().finer(matchedLength + " characters were matched"); + } + + // Updates the context + String matchedPart = remainingPart.substring(0, matchedLength); + Reference baseRef = request.getResourceRef().getBaseRef(); + + if (baseRef == null) { + baseRef = new Reference(matchedPart); + } else { + baseRef = new Reference(baseRef.toString(false, false) + matchedPart); + } + + request.getResourceRef().setBaseRef(baseRef); + + if (request.isLoggable()) { + if (getLogger().isLoggable(Level.FINE)) { + remainingPart = + request.getResourceRef().getRemainingPart(false, isMatchingQuery()); if (remainingPart == null || remainingPart.isEmpty()) { - getLogger().fine("New base URI: \"" + request.getResourceRef().getBaseRef() - + "\". No remaining part to match"); + getLogger() + .fine( + "New base URI: \"" + + request.getResourceRef().getBaseRef() + + "\". No remaining part to match"); } else { - getLogger().fine("New base URI: \"" + request.getResourceRef().getBaseRef() - + "\". New remaining part: \"" + remainingPart + "\""); + getLogger() + .fine( + "New base URI: \"" + + request.getResourceRef().getBaseRef() + + "\". New remaining part: \"" + + remainingPart + + "\""); } } - if (getLogger().isLoggable(Level.FINER)) { - getLogger().finer("Delegating the call to the target Restlet"); - } - } - } else { - if (request.isLoggable() && getLogger().isLoggable(Level.FINE)) { - getLogger().fine("Unable to match this pattern: " + getTemplate().getPattern()); - } - - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } - } - - return CONTINUE; - } - - /** - * Returns the matching mode to use on the template when parsing a formatted - * reference. - * - * @return The matching mode to use. - */ - public int getMatchingMode() { - return getTemplate().getMatchingMode(); - } - - /** - * Returns the reference template to match. - * - * @return The reference template to match. - */ - public Template getTemplate() { - return this.template; - } - - /** - * Indicates whether the query part should be taken into account when matching a - * reference with the template. - * - * @return True if the query part of the reference should be taken into account, - * false otherwise. - */ - public boolean isMatchingQuery() { - return this.matchingQuery; - } - - /** - * Returns the score for a given call (between 0 and 1.0). - * - * @param request The request to score. - * @param response The response to score. - * @return The score for a given call (between 0 and 1.0). - */ - public float score(Request request, Response response) { - float result = 0F; - - if ((getRouter() != null) && (request.getResourceRef() != null) && (getTemplate() != null)) { - final String remainingPart = request.getResourceRef().getRemainingPart(false, isMatchingQuery()); - if (remainingPart != null) { - final int matchedLength = getTemplate().match(remainingPart); - - if (matchedLength != -1) { - final float totalLength = remainingPart.length(); - - if (totalLength > 0.0F) { - result = getRouter().getRequiredScore() - + (1.0F - getRouter().getRequiredScore()) * (matchedLength / totalLength); - } else { - result = 1.0F; - } - } - } - - if (request.isLoggable() && getLogger().isLoggable(Level.FINER)) { - getLogger().finer("Call score for the \"" + getTemplate().getPattern() + "\" URI pattern: " + result); - } - } - - return result; - } - - /** - * Sets the matching mode to use on the template when parsing a formatted - * reference. - * - * @param matchingMode The matching mode to use. - */ - public void setMatchingMode(int matchingMode) { - getTemplate().setMatchingMode(matchingMode); - } - - /** - * Sets whether the matching should be done on the URI with or without query - * string. - * - * @param matchingQuery True if the matching should be done with the query - * string, false otherwise. - */ - public void setMatchingQuery(boolean matchingQuery) { - this.matchingQuery = matchingQuery; - } - - /** - * Sets the reference template to match. - * - * @param template The reference template to match. - */ - public void setTemplate(Template template) { - this.template = template; - } - - @Override - public String toString() { - return "\"" + ((getTemplate() == null) ? super.toString() : getTemplate().getPattern()) + "\" -> " - + ((getNext() == null) ? "null" : getNext().toString()); - } + if (getLogger().isLoggable(Level.FINER)) { + getLogger().finer("Delegating the call to the target Restlet"); + } + } + } else { + if (request.isLoggable() && getLogger().isLoggable(Level.FINE)) { + getLogger().fine("Unable to match this pattern: " + getTemplate().getPattern()); + } + + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } + } + + return CONTINUE; + } + + /** + * Returns the matching mode to use on the template when parsing a formatted reference. + * + * @return The matching mode to use. + */ + public int getMatchingMode() { + return getTemplate().getMatchingMode(); + } + + /** + * Returns the reference template to match. + * + * @return The reference template to match. + */ + public Template getTemplate() { + return this.template; + } + + /** + * Indicates whether the query part should be taken into account when matching a reference with + * the template. + * + * @return True if the query part of the reference should be taken into account, false + * otherwise. + */ + public boolean isMatchingQuery() { + return this.matchingQuery; + } + + /** + * Returns the score for a given call (between 0 and 1.0). + * + * @param request The request to score. + * @param response The response to score. + * @return The score for a given call (between 0 and 1.0). + */ + public float score(Request request, Response response) { + float result = 0F; + + if ((getRouter() != null) + && (request.getResourceRef() != null) + && (getTemplate() != null)) { + final String remainingPart = + request.getResourceRef().getRemainingPart(false, isMatchingQuery()); + if (remainingPart != null) { + final int matchedLength = getTemplate().match(remainingPart); + + if (matchedLength != -1) { + final float totalLength = remainingPart.length(); + + if (totalLength > 0.0F) { + result = + getRouter().getRequiredScore() + + (1.0F - getRouter().getRequiredScore()) + * (matchedLength / totalLength); + } else { + result = 1.0F; + } + } + } + + if (request.isLoggable() && getLogger().isLoggable(Level.FINER)) { + getLogger() + .finer( + "Call score for the \"" + + getTemplate().getPattern() + + "\" URI pattern: " + + result); + } + } + + return result; + } + + /** + * Sets the matching mode to use on the template when parsing a formatted reference. + * + * @param matchingMode The matching mode to use. + */ + public void setMatchingMode(int matchingMode) { + getTemplate().setMatchingMode(matchingMode); + } + + /** + * Sets whether the matching should be done on the URI with or without query string. + * + * @param matchingQuery True if the matching should be done with the query string, false + * otherwise. + */ + public void setMatchingQuery(boolean matchingQuery) { + this.matchingQuery = matchingQuery; + } + + /** + * Sets the reference template to match. + * + * @param template The reference template to match. + */ + public void setTemplate(Template template) { + this.template = template; + } + + @Override + public String toString() { + return "\"" + + ((getTemplate() == null) ? super.toString() : getTemplate().getPattern()) + + "\" -> " + + ((getNext() == null) ? "null" : getNext().toString()); + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/Validator.java b/org.restlet/src/main/java/org/restlet/routing/Validator.java index bf824d981f..db5d61bf77 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Validator.java +++ b/org.restlet/src/main/java/org/restlet/routing/Validator.java @@ -1,177 +1,175 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.regex.Pattern; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; import org.restlet.data.Status; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.regex.Pattern; - /** - * Filter validating attributes from a call. Validation is verified based on - * regex pattern matching.
+ * Filter validating attributes from a call. Validation is verified based on regex pattern matching. + *
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @author Jerome Louvel * @see Pattern */ public class Validator extends Filter { - /** Internal class holding validation information. */ - private static final class ValidateInfo { - /** Name of the attribute to look for. */ - protected String attribute; - - /** Format of the attribute value, using Regex pattern syntax. */ - protected String format; - - /** Indicates if the attribute presence is required. */ - protected boolean required; - - /** - * Constructor. - * - * @param attribute Name of the attribute to look for. - * @param required Indicates if the attribute presence is required. - * @param format Format of the attribute value, using Regex pattern syntax. - */ - public ValidateInfo(String attribute, boolean required, String format) { - this.attribute = attribute; - this.required = required; - this.format = format; - } - } - - /** The list of attribute validations. */ - private volatile List validations; - - /** - * Constructor. - */ - public Validator() { - this(null); - } - - /** - * Constructor. - * - * @param context The context. - */ - public Validator(Context context) { - this(context, null); - } - - /** - * Constructor. - * - * @param context The context. - * @param next The next Restlet. - */ - public Validator(Context context, Restlet next) { - super(context, next); - } - - /** - * Allows filtering before its handling by the target Restlet. By default it - * parses the template variable, adjust the base reference, then extracts the - * attributes from form parameters (query, cookies, entity) and finally tries to - * validate the variables as indicated by the - * {@link #validate(String, boolean, String)} method. - * - * @param request The request to filter. - * @param response The response to filter. - * @return The {@link Filter#CONTINUE} status. - */ - @Override - protected int beforeHandle(Request request, Response response) { - if (this.validations != null) { - for (ValidateInfo validate : getValidations()) { - if (validate.required && !request.getAttributes().containsKey(validate.attribute)) { - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Unable to find the \"" + validate.attribute - + "\" attribute in the request. Please check your request."); - } else if (validate.format != null) { - Object value = request.getAttributes().get(validate.attribute); - - if ((value != null) && !Pattern.matches(validate.format, value.toString())) { - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, - "Unable to validate the value of the \"" + validate.attribute - + "\" attribute. The expected format is: " + validate.format - + " (Java Regex). Please check your request."); - } - } - } - } - - return CONTINUE; - } - - /** - * Returns the list of attribute validations. - * - * @return The list of attribute validations. - */ - private List getValidations() { - // Lazy initialization with double-check. - List v = this.validations; - if (v == null) { - synchronized (this) { - v = this.validations; - if (v == null) { - this.validations = v = new CopyOnWriteArrayList(); - } - } - } - return v; - } - - /** - * Checks the request attributes for presence or format. If the check fails, - * then a response status CLIENT_ERROR_BAD_REQUEST is returned with the proper - * status description. - * - * @param attribute Name of the attribute to look for. - * @param required Indicates if the attribute presence is required. - * @param format Format of the attribute value, using Regex pattern syntax. - */ - public void validate(String attribute, boolean required, String format) { - getValidations().add(new ValidateInfo(attribute, required, format)); - } - - /** - * Checks the request attributes for format only. If the check fails, then a - * response status CLIENT_ERROR_BAD_REQUEST is returned with the proper status - * description. - * - * @param attribute Name of the attribute to look for. - * @param format Format of the attribute value, using Regex pattern syntax. - */ - public void validateFormat(String attribute, String format) { - getValidations().add(new ValidateInfo(attribute, false, format)); - } - - /** - * Checks the request attributes for presence only. If the check fails, then a - * response status CLIENT_ERROR_BAD_REQUEST is returned with the proper status - * description. - * - * @param attribute Name of the attribute to look for. - */ - public void validatePresence(String attribute) { - getValidations().add(new ValidateInfo(attribute, true, null)); - } + /** Internal class holding validation information. */ + private static final class ValidateInfo { + /** Name of the attribute to look for. */ + protected String attribute; + + /** Format of the attribute value, using Regex pattern syntax. */ + protected String format; + + /** Indicates if the attribute presence is required. */ + protected boolean required; + + /** + * Constructor. + * + * @param attribute Name of the attribute to look for. + * @param required Indicates if the attribute presence is required. + * @param format Format of the attribute value, using Regex pattern syntax. + */ + public ValidateInfo(String attribute, boolean required, String format) { + this.attribute = attribute; + this.required = required; + this.format = format; + } + } + + /** The list of attribute validations. */ + private volatile List validations; + + /** Constructor. */ + public Validator() { + this(null); + } + + /** + * Constructor. + * + * @param context The context. + */ + public Validator(Context context) { + this(context, null); + } + + /** + * Constructor. + * + * @param context The context. + * @param next The next Restlet. + */ + public Validator(Context context, Restlet next) { + super(context, next); + } + + /** + * Allows filtering before its handling by the target Restlet. By default, it parses the + * template variable, adjust the base reference, then extracts the attributes from form + * parameters (query, cookies, entity) and finally tries to validate the variables as indicated + * by the {@link #validate(String, boolean, String)} method. + * + * @param request The request to filter. + * @param response The response to filter. + * @return The {@link Filter#CONTINUE} status. + */ + @Override + protected int beforeHandle(Request request, Response response) { + if (this.validations != null) { + for (ValidateInfo validate : getValidations()) { + if (validate.required && !request.getAttributes().containsKey(validate.attribute)) { + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, + "Unable to find the \"" + + validate.attribute + + "\" attribute in the request. Please check your request."); + } else if (validate.format != null) { + Object value = request.getAttributes().get(validate.attribute); + + if ((value != null) && !Pattern.matches(validate.format, value.toString())) { + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, + "Unable to validate the value of the \"" + + validate.attribute + + "\" attribute. The expected format is: " + + validate.format + + " (Java Regex). Please check your request."); + } + } + } + } + + return CONTINUE; + } + + /** + * Returns the list of attribute validations. + * + * @return The list of attribute validations. + */ + private List getValidations() { + // Lazy initialization with double-check. + List v = this.validations; + if (v == null) { + synchronized (this) { + v = this.validations; + if (v == null) { + this.validations = v = new CopyOnWriteArrayList<>(); + } + } + } + return v; + } + + /** + * Checks the request attributes for presence or format. If the check fails, then a response + * status CLIENT_ERROR_BAD_REQUEST is returned with the proper status description. + * + * @param attribute Name of the attribute to look for. + * @param required Indicates if the attribute presence is required. + * @param format Format of the attribute value, using Regex pattern syntax. + */ + public void validate(String attribute, boolean required, String format) { + getValidations().add(new ValidateInfo(attribute, required, format)); + } + + /** + * Checks the request attributes for format only. If the check fails, then a response status + * CLIENT_ERROR_BAD_REQUEST is returned with the proper status description. + * + * @param attribute Name of the attribute to look for. + * @param format Format of the attribute value, using Regex pattern syntax. + */ + public void validateFormat(String attribute, String format) { + getValidations().add(new ValidateInfo(attribute, false, format)); + } + + /** + * Checks the request attributes for presence only. If the check fails, then a response status + * CLIENT_ERROR_BAD_REQUEST is returned with the proper status description. + * + * @param attribute Name of the attribute to look for. + */ + public void validatePresence(String attribute) { + getValidations().add(new ValidateInfo(attribute, true, null)); + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/Variable.java b/org.restlet/src/main/java/org/restlet/routing/Variable.java index ae61cf52e8..c16b9802cf 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Variable.java +++ b/org.restlet/src/main/java/org/restlet/routing/Variable.java @@ -1,275 +1,271 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; import org.restlet.data.Reference; /** * Variable descriptor for reference templates. - * + * * @see Template * @author Jerome Louvel */ public final class Variable { - /** Matches all characters. */ - public static final int TYPE_ALL = 1; + /** Matches all characters. */ + public static final int TYPE_ALL = 1; - /** Matches all alphabetical characters. */ - public static final int TYPE_ALPHA = 2; + /** Matches all alphabetical characters. */ + public static final int TYPE_ALPHA = 2; - /** Matches all alphabetical and digital characters. */ - public static final int TYPE_ALPHA_DIGIT = 3; - - /** Matches any TEXT excluding "(" and ")". */ - public static final int TYPE_COMMENT = 4; - - /** Matches any TEXT inside a comment excluding ";". */ - public static final int TYPE_COMMENT_ATTRIBUTE = 5; - - /** Matches all digital characters. */ - public static final int TYPE_DIGIT = 6; - - /** Matches any CHAR except CTLs or separators. */ - public static final int TYPE_TOKEN = 7; - - /** Matches all URI characters. */ - public static final int TYPE_URI_ALL = 8; - - /** Matches URI fragment characters. */ - public static final int TYPE_URI_FRAGMENT = 9; - - /** Matches URI path characters (not the query or the fragment parts). */ - public static final int TYPE_URI_PATH = 10; - - /** Matches URI query characters. */ - public static final int TYPE_URI_QUERY = 11; - - /** Matches URI query parameter characters (name or value). */ - public static final int TYPE_URI_QUERY_PARAM = 12; - - /** Matches URI scheme characters. */ - public static final int TYPE_URI_SCHEME = 13; - - /** Matches URI segment characters. */ - public static final int TYPE_URI_SEGMENT = 14; - - /** Matches unreserved URI characters. */ - public static final int TYPE_URI_UNRESERVED = 15; - - /** Matches all alphabetical and digital characters plus the underscore. */ - public static final int TYPE_WORD = 16; - - /** Indicates if the parsed value must be decoded. */ - private volatile boolean decodingOnParse; - - /** The default value to use if the key couldn't be found in the model. */ - private volatile String defaultValue; - - /** Indicates if the formatted value must be encoded. */ - private volatile boolean encodingOnFormat; - - /** - * Indicates if the value is fixed, in which case the "defaultValue" property is - * always used. - */ - private volatile boolean fixed; - - /** Indicates if the variable is required or optional. */ - private volatile boolean required; - - /** The type of variable. See TYPE_* constants. */ - private volatile int type; - - /** - * Default constructor. Type is TYPE_ALL, default value is "", required is true - * and fixed is false. - */ - public Variable() { - this(Variable.TYPE_ALL, "", true, false); - } - - /** - * Constructor. Default value is "", required is true and fixed is false. - * - * @param type The type of variable. See TYPE_* constants. - */ - public Variable(int type) { - this(type, "", true, false); - } - - /** - * Constructor. - * - * @param type The type of variable. See TYPE_* constants. - * @param defaultValue The default value to use if the key couldn't be found in - * the model. - * @param required Indicates if the variable is required or optional. - * @param fixed Indicates if the value is fixed, in which case the - * "defaultValue" property is always used. - */ - public Variable(int type, String defaultValue, boolean required, boolean fixed) { - this(type, defaultValue, required, fixed, false, false); - } - - /** - * Constructor. - * - * @param type The type of variable. See TYPE_* constants. - * @param defaultValue The default value to use if the key couldn't be found - * in the model. - * @param required Indicates if the variable is required or optional. - * @param fixed Indicates if the value is fixed, in which case the - * "defaultValue" property is always used. - * @param decodingOnParse Indicates if the parsed value must be decoded. - * @param encodingOnFormat Indicates if the formatted value must be encoded. - */ - public Variable(int type, String defaultValue, boolean required, boolean fixed, boolean decodingOnParse, - boolean encodingOnFormat) { - this.type = type; - this.defaultValue = defaultValue; - this.required = required; - this.fixed = fixed; - this.decodingOnParse = decodingOnParse; - this.encodingOnFormat = encodingOnFormat; - } - - /** - * According to the type of the variable, encodes the value given in parameters. - * - * @param value The value to encode. - * @return The encoded value, according to the variable type. - */ - public String encode(String value) { + /** Matches all alphabetical and digital characters. */ + public static final int TYPE_ALPHA_DIGIT = 3; + + /** Matches any TEXT excluding "(" and ")". */ + public static final int TYPE_COMMENT = 4; + + /** Matches any TEXT inside a comment excluding ";". */ + public static final int TYPE_COMMENT_ATTRIBUTE = 5; + + /** Matches all digital characters. */ + public static final int TYPE_DIGIT = 6; + + /** Matches any CHAR except CTLs or separators. */ + public static final int TYPE_TOKEN = 7; + + /** Matches all URI characters. */ + public static final int TYPE_URI_ALL = 8; + + /** Matches URI fragment characters. */ + public static final int TYPE_URI_FRAGMENT = 9; + + /** Matches URI path characters (not the query or the fragment parts). */ + public static final int TYPE_URI_PATH = 10; + + /** Matches URI query characters. */ + public static final int TYPE_URI_QUERY = 11; + + /** Matches URI query parameter characters (name or value). */ + public static final int TYPE_URI_QUERY_PARAM = 12; + + /** Matches URI scheme characters. */ + public static final int TYPE_URI_SCHEME = 13; + + /** Matches URI segment characters. */ + public static final int TYPE_URI_SEGMENT = 14; + + /** Matches unreserved URI characters. */ + public static final int TYPE_URI_UNRESERVED = 15; + + /** Matches all alphabetical and digital characters plus the underscore. */ + public static final int TYPE_WORD = 16; + + /** Indicates if the parsed value must be decoded. */ + private volatile boolean decodingOnParse; + + /** The default value to use if the key couldn't be found in the model. */ + private volatile String defaultValue; + + /** Indicates if the formatted value must be encoded. */ + private volatile boolean encodingOnFormat; + + /** + * Indicates if the value is fixed, in which case the "defaultValue" property is always used. + */ + private volatile boolean fixed; + + /** Indicates if the variable is required or optional. */ + private volatile boolean required; + + /** The type of variable. See TYPE_* constants. */ + private volatile int type; + + /** + * Default constructor. Type is TYPE_ALL, default value is "", required is true, and fixed is + * false. + */ + public Variable() { + this(Variable.TYPE_ALL, "", true, false); + } + + /** + * Constructor. Default value is "", required is true and fixed is false. + * + * @param type The type of variable. See TYPE_* constants. + */ + public Variable(int type) { + this(type, "", true, false); + } + + /** + * Constructor. + * + * @param type The type of variable. See TYPE_* constants. + * @param defaultValue The default value to use if the key couldn't be found in the model. + * @param required Indicates if the variable is required or optional. + * @param fixed Indicates if the value is fixed, in which case the "defaultValue" property is + * always used. + */ + public Variable(int type, String defaultValue, boolean required, boolean fixed) { + this(type, defaultValue, required, fixed, false, false); + } + + /** + * Constructor. + * + * @param type The type of variable. See TYPE_* constants. + * @param defaultValue The default value to use if the key couldn't be found in the model. + * @param required Indicates if the variable is required or optional. + * @param fixed Indicates if the value is fixed, in which case the "defaultValue" property is + * always used. + * @param decodingOnParse Indicates if the parsed value must be decoded. + * @param encodingOnFormat Indicates if the formatted value must be encoded. + */ + public Variable( + int type, + String defaultValue, + boolean required, + boolean fixed, + boolean decodingOnParse, + boolean encodingOnFormat) { + this.type = type; + this.defaultValue = defaultValue; + this.required = required; + this.fixed = fixed; + this.decodingOnParse = decodingOnParse; + this.encodingOnFormat = encodingOnFormat; + } + + /** + * According to the type of the variable, encodes the value given in parameters. + * + * @param value The value to encode. + * @return The encoded value, according to the variable type. + */ + public String encode(String value) { return switch (this.type) { case Variable.TYPE_URI_ALL, - Variable.TYPE_URI_UNRESERVED, - Variable.TYPE_URI_FRAGMENT, - Variable.TYPE_URI_PATH, - Variable.TYPE_URI_QUERY, - Variable.TYPE_URI_SEGMENT -> Reference.encode(value); + Variable.TYPE_URI_UNRESERVED, + Variable.TYPE_URI_FRAGMENT, + Variable.TYPE_URI_PATH, + Variable.TYPE_URI_QUERY, + Variable.TYPE_URI_SEGMENT -> + Reference.encode(value); default -> value; }; - } - - /** - * Returns the default value to use if the key couldn't be found in the model. - * - * @return The default value to use if the key couldn't be found in the model. - */ - public String getDefaultValue() { - return this.defaultValue; - } - - /** - * Returns the type of variable. See TYPE_* constants. - * - * @return The type of variable. See TYPE_* constants. - */ - public int getType() { - return this.type; - } - - /** - * Indicates if the parsed value must be decoded. - * - * @return True if the parsed value must be decoded, false otherwise. - */ - public boolean isDecodingOnParse() { - return this.decodingOnParse; - } - - /** - * Indicates if the formatted value must be encoded. - * - * @return True if the formatted value must be encoded, false otherwise. - */ - public boolean isEncodingOnFormat() { - return this.encodingOnFormat; - } - - /** - * Returns true if the value is fixed, in which case the "defaultValue" property - * is always used. - * - * @return True if the value is fixed, in which case the "defaultValue" property - * is always used. - */ - public boolean isFixed() { - return this.fixed; - } - - /** - * Returns true if the variable is required or optional. - * - * @return True if the variable is required or optional. - */ - public boolean isRequired() { - return this.required; - } - - /** - * Indicates if the parsed value must be decoded. - * - * @param decodingOnParse True if the parsed value must be decoded, false - * otherwise. - */ - public void setDecodingOnParse(boolean decodingOnParse) { - this.decodingOnParse = decodingOnParse; - } - - /** - * Sets the default value to use if the key couldn't be found in the model. - * - * @param defaultValue The default value to use if the key couldn't be found in - * the model. - */ - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } - - /** - * Indicates if the formatted value must be encoded. - * - * @param encodingOnFormat True if the formatted value must be encoded, false - * otherwise. - */ - public void setEncodingOnFormat(boolean encodingOnFormat) { - this.encodingOnFormat = encodingOnFormat; - } - - /** - * Indicates if the value is fixed - * - * @param fixed True if the value is fixed - */ - public void setFixed(boolean fixed) { - this.fixed = fixed; - } - - /** - * Indicates if the variable is required or optional. - * - * @param required True if the variable is required or optional. - */ - public void setRequired(boolean required) { - this.required = required; - } - - /** - * Sets the type of variable. See TYPE_* constants. - * - * @param type The type of variable. - */ - public void setType(int type) { - this.type = type; - } - + } + + /** + * Returns the default value to use if the key couldn't be found in the model. + * + * @return The default value to use if the key couldn't be found in the model. + */ + public String getDefaultValue() { + return this.defaultValue; + } + + /** + * Returns the type of variable. See TYPE_* constants. + * + * @return The type of variable. See TYPE_* constants. + */ + public int getType() { + return this.type; + } + + /** + * Indicates if the parsed value must be decoded. + * + * @return True if the parsed value must be decoded, false otherwise. + */ + public boolean isDecodingOnParse() { + return this.decodingOnParse; + } + + /** + * Indicates if the formatted value must be encoded. + * + * @return True if the formatted value must be encoded, false otherwise. + */ + public boolean isEncodingOnFormat() { + return this.encodingOnFormat; + } + + /** + * Returns true if the value is fixed, in which case the "defaultValue" property is always used. + * + * @return True if the value is fixed, in which case the "defaultValue" property is always used. + */ + public boolean isFixed() { + return this.fixed; + } + + /** + * Returns true if the variable is required or optional. + * + * @return True if the variable is required or optional. + */ + public boolean isRequired() { + return this.required; + } + + /** + * Indicates if the parsed value must be decoded. + * + * @param decodingOnParse True if the parsed value must be decoded, false otherwise. + */ + public void setDecodingOnParse(boolean decodingOnParse) { + this.decodingOnParse = decodingOnParse; + } + + /** + * Sets the default value to use if the key couldn't be found in the model. + * + * @param defaultValue The default value to use if the key couldn't be found in the model. + */ + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + /** + * Indicates if the formatted value must be encoded. + * + * @param encodingOnFormat True if the formatted value must be encoded, false otherwise. + */ + public void setEncodingOnFormat(boolean encodingOnFormat) { + this.encodingOnFormat = encodingOnFormat; + } + + /** + * Indicates if the value is fixed + * + * @param fixed True if the value is fixed + */ + public void setFixed(boolean fixed) { + this.fixed = fixed; + } + + /** + * Indicates if the variable is required or optional. + * + * @param required True if the variable is required or optional. + */ + public void setRequired(boolean required) { + this.required = required; + } + + /** + * Sets the type of variable. See TYPE_* constants. + * + * @param type The type of variable. + */ + public void setType(int type) { + this.type = type; + } } diff --git a/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java b/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java index 0587f371ff..c320432d33 100644 --- a/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java +++ b/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import java.net.InetAddress; +import java.net.UnknownHostException; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -16,467 +17,462 @@ import org.restlet.resource.Finder; import org.restlet.resource.ServerResource; -import java.net.InetAddress; -import java.net.UnknownHostException; - /** - * Router of calls from Server connectors to Restlets. The attached Restlets are - * typically Applications.
+ * Router of calls from Server connectors to Restlets. The attached Restlets are typically + * Applications.
*
* A virtual host is defined along three properties: + * *

    - *
  • request's {@link Request#getHostRef()}: the URI of the host that received - * the request. Note that the same IP address can correspond to multiple domain - * names and therefore receive request with different "hostRef" URIs.
  • - *
  • request's {@link Request#getResourceRef()}: the URI of the target - * resource of the request. If this reference is relative, then it is based on - * the "hostRef", otherwise it is maintained as received. This difference is - * useful for resources identified by URNs or for Web proxies or Web - * caches.
  • - *
  • response's {@link Response#getServerInfo()}: the information about the - * server connector receiving the requests such as it IP address and port - * number.
  • + *
  • request's {@link Request#getHostRef()}: the URI of the host that received the request. Note + * that the same IP address can correspond to multiple domain names and therefore receive + * request with different "hostRef" URIs. + *
  • request's {@link Request#getResourceRef()}: the URI of the target resource of the request. + * If this reference is relative, then it is based on the "hostRef", otherwise it is + * maintained as received. This difference is useful for resources identified by URNs or for + * Web proxies or Web caches. + *
  • response's {@link Response#getServerInfo()}: the information about the server connector + * receiving the requests such as it IP address and port number. *
- * When creating a new instance, you can define Java regular expressions ( - * {@link java.util.regex.Pattern}) that must match the domain name, port, - * scheme for references or IP address and port number for server information. - * The default values match everything.
+ * + * When creating a new instance, you can define Java regular expressions ( {@link + * java.util.regex.Pattern}) that must match the domain name, port, scheme for references or IP + * address and port number for server information. The default values match everything.
*
- * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * + * Concurrency note: instances of this class or its subclasses can be invoked by several threads at + * the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * * @see java.util.regex.Pattern - * @see Wikipedia - - * Virtual Hosting - * @see Apache - Virtual - * Hosting + * @see Wikipedia - Virtual Hosting + * @see Apache - Virtual Hosting * @author Jerome Louvel */ public class VirtualHost extends Router { - private static final ThreadLocal CURRENT = new ThreadLocal(); - - /** - * Returns the virtual host code associated to the current thread. - * - * This variable is stored internally as a thread local variable and updated - * each time a call is routed by a virtual host. - * - * @return The current context. - */ - public static Integer getCurrent() { - return CURRENT.get(); - } - - /** - * Returns the IP address of a given domain name. - * - * @param domain The domain name. - * @return The IP address. - */ - public static String getIpAddress(String domain) { - String result = null; - - try { - result = InetAddress.getByName(domain).getHostAddress(); - } catch (UnknownHostException e) { - } - - return result; - } - - /** - * Returns the local host IP address. - * - * @return The local host IP address. - */ - public static String getLocalHostAddress() { - String result = null; - - try { - result = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - } - - return result; - } - - /** - * Returns the local host name. - * - * @return The local host name. - */ - public static String getLocalHostName() { - String result = null; - - try { - result = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - } - - return result; - } - - /** - * Sets the virtual host code associated with the current thread. - * - * @param code The thread's virtual host code. - */ - public static void setCurrent(Integer code) { - CURRENT.set(code); - } - - /** The hostRef host domain pattern to match. */ - private volatile String hostDomain; - - /** The hostRef host port pattern to match. */ - private volatile String hostPort; - - /** The hostRef scheme pattern to match. */ - private volatile String hostScheme; - - /** The parent component's context. */ - private volatile Context parentContext; - - /** The resourceRef host domain pattern to match. */ - private volatile String resourceDomain; - - /** The resourceRef host port pattern to match. */ - private volatile String resourcePort; - - /** The resourceRef scheme pattern to match. */ - private volatile String resourceScheme; - - /** The listening server address pattern to match. */ - private volatile String serverAddress; - - /** The listening server port pattern to match. */ - private volatile String serverPort; - - /** - * Constructor. Note that usage of this constructor is not recommended as the - * virtual host won't have a proper context set. In general you will prefer to - * use the other constructor and pass it the parent component's context. - */ - public VirtualHost() { - this(null); - } - - /** - * Constructor. Accepts all incoming requests by default, use the set methods to - * restrict the matchable patterns. - * - * @param parentContext The parent component's context. - */ - public VirtualHost(Context parentContext) { - this(parentContext, ".*", ".*", ".*", ".*", ".*", ".*", ".*", ".*"); - } - - /** - * Constructor. - * - * @param parentContext The parent component's context. - * @param hostDomain The hostRef host domain pattern to match. - * @param hostPort The hostRef host port pattern to match. - * @param hostScheme The hostRef scheme protocol pattern to match. - * @param resourceDomain The resourceRef host domain pattern to match. - * @param resourcePort The resourceRef host port pattern to match. - * @param resourceScheme The resourceRef scheme protocol pattern to match. - * @param serverAddress The listening server address pattern to match. - * @param serverPort The listening server port pattern to match. - * @see java.util.regex.Pattern - */ - public VirtualHost(Context parentContext, String hostDomain, String hostPort, String hostScheme, - String resourceDomain, String resourcePort, String resourceScheme, String serverAddress, - String serverPort) { - super((parentContext == null) ? null : parentContext.createChildContext()); - - // Override Router's default modes - setDefaultMatchingMode(Template.MODE_STARTS_WITH); - setRoutingMode(MODE_BEST_MATCH); - - this.parentContext = parentContext; - - this.hostDomain = hostDomain; - this.hostPort = hostPort; - this.hostScheme = hostScheme; - - this.resourceDomain = resourceDomain; - this.resourcePort = resourcePort; - this.resourceScheme = resourceScheme; - - this.serverAddress = serverAddress; - this.serverPort = serverPort; - } - - /** - * Attaches a target Restlet to this router with an empty URI pattern. A new - * route will be added routing to the target when any call is received. - * - * In addition to super class behavior, this method will set the context of the - * target if it is empty by creating a protected context via the - * {@link Context#createChildContext()} method. - * - * @param target The target Restlet to attach. - * @return The created route. - */ - @Override - public TemplateRoute attach(Restlet target) { - checkContext(target); - return super.attach(target); - } - - /** - * Attaches a target Restlet to this router based on a given URI pattern. A new - * route will be added routing to the target when calls with a URI matching the - * pattern will be received. - * - * In addition to super class behavior, this method will set the context of the - * target if it is empty by creating a protected context via the - * {@link Context#createChildContext()} method. - * - * @param uriPattern The URI pattern that must match the relative part of the - * resource URI. - * @param target The target Restlet to attach. - * @return The created route. - */ - @Override - public TemplateRoute attach(String uriPattern, Restlet target) { - checkContext(target); - return super.attach(uriPattern, target); - } - - /** - * Attaches a Restlet to this router as the default target to invoke when no - * route matches. It actually sets a default route that scores all calls to 1.0. - * - * In addition to super class behavior, this method will set the context of the - * target if it is empty by creating a protected context via the - * {@link Context#createChildContext()} method. - * - * @param defaultTarget The Restlet to use as the default target. - * @return The created route. - */ - @Override - public TemplateRoute attachDefault(Restlet defaultTarget) { - checkContext(defaultTarget); - return super.attachDefault(defaultTarget); - } - - /** - * Checks the context and sets it if necessary. - * - * @param target The target Restlet. - */ - protected void checkContext(Restlet target) { - if ((target.getContext() == null) && (this.parentContext != null)) { - target.setContext(this.parentContext.createChildContext()); - } - } - - /** - * Creates a new finder instance based on the "targetClass" property. - * - * In addition to super class behavior, this method will set the context of the - * finder by creating a protected context via the - * {@link Context#createChildContext()} method. - * - * @param targetClass The target Resource class to attach. - * @return The new finder instance. - */ - @Override - public Finder createFinder(Class targetClass) { - Finder result = super.createFinder(targetClass); - result.setContext(getContext().createChildContext()); - return result; - } - - @Override - protected TemplateRoute createRoute(String uriPattern, Restlet target, int matchingMode) { - TemplateRoute result = new TemplateRoute(this, uriPattern, target) { - @Override - protected int beforeHandle(Request request, Response response) { - final int result = super.beforeHandle(request, response); - - // Set the request's root reference - request.setRootRef(request.getResourceRef().getBaseRef()); - - // Save the hash code of the current host - setCurrent(VirtualHost.this.hashCode()); - - return result; - } - }; - - result.getTemplate().setMatchingMode(matchingMode); - result.setMatchingQuery(getDefaultMatchingQuery()); - return result; - } - - /** - * Returns the hostRef host domain to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @return The hostRef host domain to match. - */ - public String getHostDomain() { - return this.hostDomain; - } - - /** - * Returns the hostRef host port to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @return The hostRef host port to match. - */ - public String getHostPort() { - return this.hostPort; - } - - /** - * Returns the hostRef scheme to match. See the {@link java.util.regex.Pattern} - * class for details on the syntax. - * - * @return The hostRef scheme to match. - */ - public String getHostScheme() { - return this.hostScheme; - } - - /** - * Returns the resourceRef host domain to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @return The resourceRef host domain to match. - */ - public String getResourceDomain() { - return this.resourceDomain; - } - - /** - * Returns the resourceRef host port to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @return The resourceRef host port to match. - */ - public String getResourcePort() { - return this.resourcePort; - } - - /** - * Returns the resourceRef scheme to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @return The resourceRef scheme to match. - */ - public String getResourceScheme() { - return this.resourceScheme; - } - - /** - * Returns the listening server address. See the {@link java.util.regex.Pattern} - * class for details on the syntax. - * - * @return The listening server address. - */ - public String getServerAddress() { - return this.serverAddress; - } - - /** - * Returns the listening server port. See the {@link java.util.regex.Pattern} - * class for details on the syntax. - * - * @return The listening server port. - */ - public String getServerPort() { - return this.serverPort; - } - - @Override - public void setContext(Context parentContext) { - this.parentContext = parentContext; - super.setContext((parentContext == null) ? null : parentContext.createChildContext()); - } - - /** - * Sets the hostRef host domain to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @param hostDomain The hostRef host domain to match. - */ - public void setHostDomain(String hostDomain) { - this.hostDomain = hostDomain; - } - - /** - * Sets the hostRef host port to match. See the {@link java.util.regex.Pattern} - * class for details on the syntax. - * - * @param hostPort The hostRef host port to match. - */ - public void setHostPort(String hostPort) { - this.hostPort = hostPort; - } - - /** - * Sets the hostRef scheme to match. See the {@link java.util.regex.Pattern} - * class for details on the syntax. - * - * @param hostScheme The hostRef scheme to match. - */ - public void setHostScheme(String hostScheme) { - this.hostScheme = hostScheme; - } - - /** - * Sets the resourceRef host domain to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @param resourceDomain The resourceRef host domain to match. - */ - public void setResourceDomain(String resourceDomain) { - this.resourceDomain = resourceDomain; - } - - /** - * Sets the resourceRef host port to match. See the - * {@link java.util.regex.Pattern} class for details on the syntax. - * - * @param resourcePort The resourceRef host port to match. - */ - public void setResourcePort(String resourcePort) { - this.resourcePort = resourcePort; - } - - /** - * Sets the resourceRef scheme to match. See the {@link java.util.regex.Pattern} - * class for details on the syntax. - * - * @param resourceScheme The resourceRef scheme to match. - */ - public void setResourceScheme(String resourceScheme) { - this.resourceScheme = resourceScheme; - } - - /** - * Sets the listening server address. See the {@link java.util.regex.Pattern} - * class for details on the syntax. - * - * @param serverAddress The listening server address. - */ - public void setServerAddress(String serverAddress) { - this.serverAddress = serverAddress; - } - - /** - * Sets the listening server port. See the {@link java.util.regex.Pattern} class - * for details on the syntax. - * - * @param serverPort The listening server port. - */ - public void setServerPort(String serverPort) { - this.serverPort = serverPort; - } - + private static final ThreadLocal CURRENT = new ThreadLocal(); + + /** + * Returns the virtual host code associated with the current thread. + * + *

This variable is stored internally as a thread local variable and updated each time a call + * is routed by a virtual host. + * + * @return The current context. + */ + public static Integer getCurrent() { + return CURRENT.get(); + } + + /** + * Returns the IP address of a given domain name. + * + * @param domain The domain name. + * @return The IP address. + */ + public static String getIpAddress(String domain) { + String result = null; + + try { + result = InetAddress.getByName(domain).getHostAddress(); + } catch (UnknownHostException ignored) { + } + + return result; + } + + /** + * Returns the local host IP address. + * + * @return The local host IP address. + */ + public static String getLocalHostAddress() { + String result = null; + + try { + result = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException ignored) { + } + + return result; + } + + /** + * Returns the local host name. + * + * @return The local host name. + */ + public static String getLocalHostName() { + String result = null; + + try { + result = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException ignored) { + } + + return result; + } + + /** + * Sets the virtual host code associated with the current thread. + * + * @param code The thread's virtual host code. + */ + public static void setCurrent(Integer code) { + CURRENT.set(code); + } + + /** The hostRef host domain pattern to match. */ + private volatile String hostDomain; + + /** The hostRef host port pattern to match. */ + private volatile String hostPort; + + /** The hostRef scheme pattern to match. */ + private volatile String hostScheme; + + /** The parent component's context. */ + private volatile Context parentContext; + + /** The resourceRef host domain pattern to match. */ + private volatile String resourceDomain; + + /** The resourceRef host port pattern to match. */ + private volatile String resourcePort; + + /** The resourceRef scheme pattern to match. */ + private volatile String resourceScheme; + + /** The listening server address pattern to match. */ + private volatile String serverAddress; + + /** The listening server port pattern to match. */ + private volatile String serverPort; + + /** + * Constructor. Note that usage of this constructor is not recommended as the virtual host won't + * have a proper context set. In general, you will prefer to use the other constructor and pass + * it the parent component's context. + */ + public VirtualHost() { + this(null); + } + + /** + * Constructor. Accepts all incoming requests by default, use the set methods to restrict the + * matchable patterns. + * + * @param parentContext The parent component's context. + */ + public VirtualHost(Context parentContext) { + this(parentContext, ".*", ".*", ".*", ".*", ".*", ".*", ".*", ".*"); + } + + /** + * Constructor. + * + * @param parentContext The parent component's context. + * @param hostDomain The hostRef host domain pattern to match. + * @param hostPort The hostRef host port pattern to match. + * @param hostScheme The hostRef scheme protocol pattern to match. + * @param resourceDomain The resourceRef host domain pattern to match. + * @param resourcePort The resourceRef host port pattern to match. + * @param resourceScheme The resourceRef scheme protocol pattern to match. + * @param serverAddress The listening server address pattern to match. + * @param serverPort The listening server port pattern to match. + * @see java.util.regex.Pattern + */ + public VirtualHost( + Context parentContext, + String hostDomain, + String hostPort, + String hostScheme, + String resourceDomain, + String resourcePort, + String resourceScheme, + String serverAddress, + String serverPort) { + super((parentContext == null) ? null : parentContext.createChildContext()); + + // Override Router's default modes + setDefaultMatchingMode(Template.MODE_STARTS_WITH); + setRoutingMode(MODE_BEST_MATCH); + + this.parentContext = parentContext; + + this.hostDomain = hostDomain; + this.hostPort = hostPort; + this.hostScheme = hostScheme; + + this.resourceDomain = resourceDomain; + this.resourcePort = resourcePort; + this.resourceScheme = resourceScheme; + + this.serverAddress = serverAddress; + this.serverPort = serverPort; + } + + /** + * Attaches a target Restlet to this router with an empty URI pattern. A new route will be added + * routing to the target when any call is received. + * + *

In addition to super class behavior, this method will set the context of the target if it + * is empty by creating a protected context via the {@link Context#createChildContext()} method. + * + * @param target The target Restlet to attach. + * @return The created route. + */ + @Override + public TemplateRoute attach(Restlet target) { + checkContext(target); + return super.attach(target); + } + + /** + * Attaches a target Restlet to this router based on a given URI pattern. A new route will be + * added routing to the target when calls with a URI matching the pattern are received. + * + *

In addition to super class behavior, this method will set the context of the target if it + * is empty by creating a protected context via the {@link Context#createChildContext()} method. + * + * @param uriPattern The URI pattern that must match the relative part of the resource URI. + * @param target The target Restlet to attach. + * @return The created route. + */ + @Override + public TemplateRoute attach(String uriPattern, Restlet target) { + checkContext(target); + return super.attach(uriPattern, target); + } + + /** + * Attaches a Restlet to this router as the default target to invoke when no route matches. It + * actually sets a default route that scores all calls to 1.0. + * + *

In addition to super class behavior, this method will set the context of the target if it + * is empty by creating a protected context via the {@link Context#createChildContext()} method. + * + * @param defaultTarget The Restlet to use as the default target. + * @return The created route. + */ + @Override + public TemplateRoute attachDefault(Restlet defaultTarget) { + checkContext(defaultTarget); + return super.attachDefault(defaultTarget); + } + + /** + * Checks the context and sets it if necessary. + * + * @param target The target Restlet. + */ + protected void checkContext(Restlet target) { + if ((target.getContext() == null) && (this.parentContext != null)) { + target.setContext(this.parentContext.createChildContext()); + } + } + + /** + * Creates a new finder instance based on the "targetClass" property. + * + *

In addition to super class behavior, this method will set the context of the finder by + * creating a protected context via the {@link Context#createChildContext()} method. + * + * @param targetClass The target Resource class to attach. + * @return The new finder instance. + */ + @Override + public Finder createFinder(Class targetClass) { + Finder result = super.createFinder(targetClass); + result.setContext(getContext().createChildContext()); + return result; + } + + @Override + protected TemplateRoute createRoute(String uriPattern, Restlet target, int matchingMode) { + TemplateRoute result = + new TemplateRoute(this, uriPattern, target) { + @Override + protected int beforeHandle(Request request, Response response) { + final int result = super.beforeHandle(request, response); + + // Set the request's root reference + request.setRootRef(request.getResourceRef().getBaseRef()); + + // Save the hash code of the current host + setCurrent(VirtualHost.this.hashCode()); + + return result; + } + }; + + result.getTemplate().setMatchingMode(matchingMode); + result.setMatchingQuery(getDefaultMatchingQuery()); + return result; + } + + /** + * Returns the hostRef host domain to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @return The hostRef host domain to match. + */ + public String getHostDomain() { + return this.hostDomain; + } + + /** + * Returns the hostRef host port to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @return The hostRef host port to match. + */ + public String getHostPort() { + return this.hostPort; + } + + /** + * Returns the hostRef scheme to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @return The hostRef scheme to match. + */ + public String getHostScheme() { + return this.hostScheme; + } + + /** + * Returns the resourceRef host domain to match. See the {@link java.util.regex.Pattern} class + * for details on the syntax. + * + * @return The resourceRef host domain to match. + */ + public String getResourceDomain() { + return this.resourceDomain; + } + + /** + * Returns the resourceRef host port to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @return The resourceRef host port to match. + */ + public String getResourcePort() { + return this.resourcePort; + } + + /** + * Returns the resourceRef scheme to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @return The resourceRef scheme to match. + */ + public String getResourceScheme() { + return this.resourceScheme; + } + + /** + * Returns the listening server address. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @return The listening server address. + */ + public String getServerAddress() { + return this.serverAddress; + } + + /** + * Returns the listening server port. See the {@link java.util.regex.Pattern} class for details + * on the syntax. + * + * @return The listening server port. + */ + public String getServerPort() { + return this.serverPort; + } + + @Override + public void setContext(Context parentContext) { + this.parentContext = parentContext; + super.setContext((parentContext == null) ? null : parentContext.createChildContext()); + } + + /** + * Sets the hostRef host domain to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @param hostDomain The hostRef host domain to match. + */ + public void setHostDomain(String hostDomain) { + this.hostDomain = hostDomain; + } + + /** + * Sets the hostRef host port to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @param hostPort The hostRef host port to match. + */ + public void setHostPort(String hostPort) { + this.hostPort = hostPort; + } + + /** + * Sets the hostRef scheme to match. See the {@link java.util.regex.Pattern} class for details + * on the syntax. + * + * @param hostScheme The hostRef scheme to match. + */ + public void setHostScheme(String hostScheme) { + this.hostScheme = hostScheme; + } + + /** + * Sets the resourceRef host domain to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @param resourceDomain The resourceRef host domain to match. + */ + public void setResourceDomain(String resourceDomain) { + this.resourceDomain = resourceDomain; + } + + /** + * Sets the resourceRef host port to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @param resourcePort The resourceRef host port to match. + */ + public void setResourcePort(String resourcePort) { + this.resourcePort = resourcePort; + } + + /** + * Sets the resourceRef scheme to match. See the {@link java.util.regex.Pattern} class for + * details on the syntax. + * + * @param resourceScheme The resourceRef scheme to match. + */ + public void setResourceScheme(String resourceScheme) { + this.resourceScheme = resourceScheme; + } + + /** + * Sets the listening server address. See the {@link java.util.regex.Pattern} class for details + * on the syntax. + * + * @param serverAddress The listening server address. + */ + public void setServerAddress(String serverAddress) { + this.serverAddress = serverAddress; + } + + /** + * Sets the listening server port. See the {@link java.util.regex.Pattern} class for details on + * the syntax. + * + * @param serverPort The listening server port. + */ + public void setServerPort(String serverPort) { + this.serverPort = serverPort; + } } diff --git a/org.restlet/src/main/java/org/restlet/security/Authenticator.java b/org.restlet/src/main/java/org/restlet/security/Authenticator.java index 1f47571a2c..8e2fd52358 100644 --- a/org.restlet/src/main/java/org/restlet/security/Authenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/Authenticator.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -16,252 +16,249 @@ import org.restlet.data.Status; import org.restlet.routing.Filter; -import java.util.logging.Level; - /** - * Filter authenticating the client sending the inbound request. Its main role - * is to inspect various credentials provided by the client and to add related - * application roles to the request's {@link ClientInfo} property. - * + * Filter authenticating the client sending the inbound request. Its main role is to inspect various + * credentials provided by the client and to add related application roles to the request's {@link + * ClientInfo} property. + * * @author Jerome Louvel */ public abstract class Authenticator extends Filter { - /** - * Invoked upon successful authentication to update the subject with new - * principals. - */ - private volatile Enroler enroler; - - /** - * Indicates if the authenticator should attempt to authenticate an already - * authenticated client. By default, it is set to true. - */ - private volatile boolean multiAuthenticating; + /** Invoked upon successful authentication to update the subject with new principals. */ + private volatile Enroler enroler; - /** - * Indicates if the authenticator is not required to succeed. In those cases, - * the attached Restlet is invoked. Note that authentication will be attempted - * independently of this property unless the client is already authenticated and - * the {@link #isMultiAuthenticating()} prevents multiple authentications. - */ - private volatile boolean optional; + /** + * Indicates if the authenticator should attempt to authenticate an already authenticated + * client. By default, it is set to true. + */ + private volatile boolean multiAuthenticating; - /** - * Constructor setting the mode to "required". - * - * @param context The context. - * @see #Authenticator(Context, boolean) - */ - public Authenticator(Context context) { - this(context, false); - } + /** + * Indicates if the authenticator is not required to succeed. In those cases, the attached + * Restlet is invoked. Note that authentication will be attempted independently of this property + * unless the client is already authenticated and the {@link #isMultiAuthenticating()} prevents + * multiple authentications. + */ + private volatile boolean optional; - /** - * Constructor using the context's default enroler. - * - * @param context The context. - * @param optional Indicates if the authenticator is not required to succeed. - * @see #Authenticator(Context, boolean, Enroler) - */ - public Authenticator(Context context, boolean optional) { - this(context, optional, (context != null) ? context.getDefaultEnroler() : null); - } + /** + * Constructor setting the mode to "required". + * + * @param context The context. + * @see #Authenticator(Context, boolean) + */ + public Authenticator(Context context) { + this(context, false); + } - /** - * Constructor. - * - * @param context The context. - * @param multiAuthenticating Indicates if the authenticator should attempt to - * authenticate an already authenticated client. - * @param optional Indicates if the authenticator is not required to - * succeed. - * @param enroler The enroler to invoke upon successful - * authentication. - */ - public Authenticator(Context context, boolean multiAuthenticating, boolean optional, Enroler enroler) { - super(context); - this.multiAuthenticating = multiAuthenticating; - this.optional = optional; - this.enroler = enroler; - } + /** + * Constructor using the context's default enroler. + * + * @param context The context. + * @param optional Indicates if the authenticator is not required to succeed. + * @see #Authenticator(Context, boolean, Enroler) + */ + public Authenticator(Context context, boolean optional) { + this(context, optional, (context != null) ? context.getDefaultEnroler() : null); + } - /** - * Constructor. - * - * @param context The context. - * @param optional Indicates if the authenticator is not required to succeed. - * @param enroler The enroler to invoke upon successful authentication. - */ - public Authenticator(Context context, boolean optional, Enroler enroler) { - this(context, true, optional, enroler); - } + /** + * Constructor. + * + * @param context The context. + * @param multiAuthenticating Indicates if the authenticator should attempt to authenticate an + * already authenticated client. + * @param optional Indicates if the authenticator is not required to succeed. + * @param enroler The enroler to invoke upon successful authentication. + */ + public Authenticator( + Context context, boolean multiAuthenticating, boolean optional, Enroler enroler) { + super(context); + this.multiAuthenticating = multiAuthenticating; + this.optional = optional; + this.enroler = enroler; + } - /** - * Attempts to authenticate the subject sending the request. - * - * @param request The request sent. - * @param response The response to update. - * @return True if the authentication succeeded. - */ - protected abstract boolean authenticate(Request request, Response response); + /** + * Constructor. + * + * @param context The context. + * @param optional Indicates if the authenticator is not required to succeed. + * @param enroler The enroler to invoke upon successful authentication. + */ + public Authenticator(Context context, boolean optional, Enroler enroler) { + this(context, true, optional, enroler); + } - /** - * Invoked upon successful authentication. By default, it updates the request's - * clientInfo and challengeResponse "authenticated" properties, clears the - * existing challenge requests on the response, calls the enroler and finally - * returns {@link Filter#CONTINUE}. - * - * @param request The request sent. - * @param response The response to update. - * @return The filter continuation code. - */ - protected int authenticated(Request request, Response response) { - boolean loggable = request.isLoggable() && getLogger().isLoggable(Level.FINE); + /** + * Attempts to authenticate the subject sending the request. + * + * @param request The request sent. + * @param response The response to update. + * @return True if the authentication succeeded. + */ + protected abstract boolean authenticate(Request request, Response response); - if (loggable && request.getChallengeResponse() != null) { - getLogger().log(Level.FINE, - "The authentication succeeded for the identifer \"" + request.getChallengeResponse().getIdentifier() - + "\" using the " + request.getChallengeResponse().getScheme() + " scheme."); - } + /** + * Invoked upon successful authentication. By default, it updates the request's clientInfo and + * challengeResponse "authenticated" properties, clears the existing challenge requests on the + * response, calls the enroler and finally returns {@link Filter#CONTINUE}. + * + * @param request The request sent. + * @param response The response to update. + * @return The filter continuation code. + */ + protected int authenticated(Request request, Response response) { + boolean loggable = request.isLoggable() && getLogger().isLoggable(Level.FINE); - // Update the client info accordingly - if (request.getClientInfo() != null) { - request.getClientInfo().setAuthenticated(true); - } + if (loggable && request.getChallengeResponse() != null) { + getLogger() + .log( + Level.FINE, + "The authentication succeeded for the identifer \"" + + request.getChallengeResponse().getIdentifier() + + "\" using the " + + request.getChallengeResponse().getScheme() + + " scheme."); + } - // Clear previous challenge requests - response.getChallengeRequests().clear(); + // Update the client info accordingly + if (request.getClientInfo() != null) { + request.getClientInfo().setAuthenticated(true); + } - // Add the roles for the authenticated subject - if (getEnroler() != null) { - getEnroler().enrole(request.getClientInfo()); - } + // Clear previous challenge requests + response.getChallengeRequests().clear(); - return CONTINUE; - } + // Add the roles for the authenticated subject + if (getEnroler() != null) { + getEnroler().enrole(request.getClientInfo()); + } - /** - * Handles the authentication by first invoking the - * {@link #authenticate(Request, Response)} method, only if - * {@link #isMultiAuthenticating()} returns true and if - * {@link ClientInfo#isAuthenticated()} returns false. If the method is invoked - * and returns true, the {@link #authenticated(Request, Response)} is called. - * Otherwise, if {@link #isOptional()} returns true it continues to the next - * Restlet or if it returns false it calls the - * {@link #unauthenticated(Request, Response)} method. - */ - @Override - protected int beforeHandle(Request request, Response response) { - if (isMultiAuthenticating() || !request.getClientInfo().isAuthenticated()) { - if (authenticate(request, response)) { - return authenticated(request, response); - } else if (isOptional()) { - response.setStatus(Status.SUCCESS_OK); - return CONTINUE; - } else { - return unauthenticated(request, response); - } - } else { - return CONTINUE; - } - } + return CONTINUE; + } - /** - * Returns the enroler invoked upon successful authentication to update the - * subject with new principals. Typically new {@link Role} are added based on - * the available {@link User} instances available. - * - * @return The enroler invoked upon successful authentication - */ - public Enroler getEnroler() { - return enroler; - } + /** + * Handles the authentication by first invoking the {@link #authenticate(Request, Response)} + * method, only if {@link #isMultiAuthenticating()} returns true and if {@link + * ClientInfo#isAuthenticated()} returns false. If the method is invoked and returns true, the + * {@link #authenticated(Request, Response)} is called. Otherwise, if {@link #isOptional()} + * returns true it continues to the next Restlet or if it returns false it calls the {@link + * #unauthenticated(Request, Response)} method. + */ + @Override + protected int beforeHandle(Request request, Response response) { + if (isMultiAuthenticating() || !request.getClientInfo().isAuthenticated()) { + if (authenticate(request, response)) { + return authenticated(request, response); + } else if (isOptional()) { + response.setStatus(Status.SUCCESS_OK); + return CONTINUE; + } else { + return unauthenticated(request, response); + } + } else { + return CONTINUE; + } + } - /** - * Indicates if the authenticator should attempt to authenticate an already - * authenticated client. The client is considered authenticated if - * {@link ClientInfo#isAuthenticated()} returns true. By default, it is set to - * true. - * - * @return True if the authenticator should attempt to authenticate an already - * authenticated client. - */ - public boolean isMultiAuthenticating() { - return multiAuthenticating; - } + /** + * Returns the enroler invoked upon successful authentication to update the subject with new + * principals. Typically, new {@link Role} are added based on the available {@link User} + * instances available. + * + * @return The enroler invoked upon successful authentication + */ + public Enroler getEnroler() { + return enroler; + } - /** - * Indicates if the authenticator is not required to succeed. In those cases, - * the attached Restlet is invoked. Note that authentication will be attempted - * independently of this property unless the client is already authenticated and - * the {@link #isMultiAuthenticating()} prevents multiple authentications. - * - * @return True if the authentication success is optional. - */ - public boolean isOptional() { - return optional; - } + /** + * Indicates if the authenticator should attempt to authenticate an already authenticated + * client. The client is considered authenticated if {@link ClientInfo#isAuthenticated()} + * returns true. By default, it is set to true. + * + * @return True if the authenticator should attempt to authenticate an already authenticated + * client. + */ + public boolean isMultiAuthenticating() { + return multiAuthenticating; + } - /** - * Sets the enroler invoked upon successful authentication. - * - * @param enroler The enroler invoked upon successful authentication. - */ - public void setEnroler(Enroler enroler) { - this.enroler = enroler; - } + /** + * Indicates if the authenticator is not required to succeed. In those cases, the attached + * Restlet is invoked. Note that authentication will be attempted independently of this property + * unless the client is already authenticated and the {@link #isMultiAuthenticating()} prevents + * multiple authentications. + * + * @return True if the authentication success is optional. + */ + public boolean isOptional() { + return optional; + } - /** - * Indicates if the authenticator should attempt to authenticate an already - * authenticated client. The client is considered authenticated if - * {@link ClientInfo#isAuthenticated()} returns true. By default, it is set to - * true. - * - * @param multiAuthenticating True if the authenticator should attempt to - * authenticate an already authenticated client. - */ - public void setMultiAuthenticating(boolean multiAuthenticating) { - this.multiAuthenticating = multiAuthenticating; - } + /** + * Sets the enroler invoked upon successful authentication. + * + * @param enroler The enroler invoked upon successful authentication. + */ + public void setEnroler(Enroler enroler) { + this.enroler = enroler; + } - /** - * Indicates if the authenticator is not required to succeed. In those cases, - * the attached Restlet is invoked. Note that authentication will be attempted - * independently of this property unless the client is already authenticated and - * the {@link #isMultiAuthenticating()} prevents multiple authentications. - * - * @param optional True if the authentication success is optional. - */ - public void setOptional(boolean optional) { - this.optional = optional; - } + /** + * Indicates if the authenticator should attempt to authenticate an already authenticated + * client. The client is considered authenticated if {@link ClientInfo#isAuthenticated()} + * returns true. By default, it is set to true. + * + * @param multiAuthenticating True if the authenticator should attempt to authenticate an + * already authenticated client. + */ + public void setMultiAuthenticating(boolean multiAuthenticating) { + this.multiAuthenticating = multiAuthenticating; + } - /** - * Invoked upon failed authentication. By default, it updates the request's - * clientInfo and challengeResponse "authenticated" properties, and returns - * {@link Filter#STOP}. - * - * @param request The request sent. - * @param response The response to update. - * @return The filter continuation code. - */ - protected int unauthenticated(Request request, Response response) { - boolean loggable = request.isLoggable() && getLogger().isLoggable(Level.FINE); + /** + * Indicates if the authenticator is not required to succeed. In those cases, the attached + * Restlet is invoked. Note that authentication will be attempted independently of this property + * unless the client is already authenticated and the {@link #isMultiAuthenticating()} prevents + * multiple authentications. + * + * @param optional True if the authentication success is optional. + */ + public void setOptional(boolean optional) { + this.optional = optional; + } - if (request.getChallengeResponse() != null && loggable) { - getLogger().log(Level.FINE, - "The authentication failed for the identifier \"" + request.getChallengeResponse().getIdentifier() - + "\" using the " + request.getChallengeResponse().getScheme() + " scheme."); - } + /** + * Invoked upon failed authentication. By default, it updates the request's clientInfo and + * challengeResponse "authenticated" properties, and returns {@link Filter#STOP}. + * + * @param request The request sent. + * @param response The response to update. + * @return The filter continuation code. + */ + protected int unauthenticated(Request request, Response response) { + boolean loggable = request.isLoggable() && getLogger().isLoggable(Level.FINE); - // Update the client info accordingly - if (request.getClientInfo() != null) { - request.getClientInfo().setAuthenticated(false); - } + if (request.getChallengeResponse() != null && loggable) { + getLogger() + .log( + Level.FINE, + "The authentication failed for the identifier \"" + + request.getChallengeResponse().getIdentifier() + + "\" using the " + + request.getChallengeResponse().getScheme() + + " scheme."); + } - // Stop the filtering chain - return STOP; - } + // Update the client info accordingly + if (request.getClientInfo() != null) { + request.getClientInfo().setAuthenticated(false); + } + // Stop the filtering chain + return STOP; + } } diff --git a/org.restlet/src/main/java/org/restlet/security/Authorizer.java b/org.restlet/src/main/java/org/restlet/security/Authorizer.java index df080bc53f..55535c2f9d 100644 --- a/org.restlet/src/main/java/org/restlet/security/Authorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/Authorizer.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import org.restlet.Request; @@ -18,128 +17,124 @@ import org.restlet.routing.Filter; /** - * Filter authorizing inbound request. It can be attached to protect a set of - * downstream {@link Restlet} and {@link ServerResource} objects. - * + * Filter authorizing inbound request. It can be attached to protect a set of downstream {@link + * Restlet} and {@link ServerResource} objects. + * * @author Jerome Louvel */ public abstract class Authorizer extends Filter { - /** Authorizer returning true all the time. */ - public static final Authorizer ALWAYS = new Authorizer() { - @Override - public boolean authorize(Request request, Response response) { - return true; - } - }; - - /** - * Authorizer returning true for all authenticated requests. For unauthenticated - * requests, it sets the response's status to - * {@link Status#CLIENT_ERROR_UNAUTHORIZED} instead of the default - * {@link Status#CLIENT_ERROR_FORBIDDEN}. - * - * @see ClientInfo#isAuthenticated() - */ - public static final Authorizer AUTHENTICATED = new Authorizer() { - @Override - public boolean authorize(Request request, Response response) { - return request.getClientInfo().isAuthenticated(); - } - - @Override - protected int unauthorized(Request request, Response response) { - response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED); - return STOP; - } - }; - - /** Authorizer returning false all the time. */ - public static final Authorizer NEVER = new Authorizer() { - @Override - public boolean authorize(Request request, Response response) { - return false; - } - }; - - /** The identifier unique within an application. */ - private volatile String identifier; - - /** - * Default constructor. - */ - public Authorizer() { - } - - /** - * Constructor. - * - * @param identifier The identifier unique within an application. - */ - public Authorizer(String identifier) { - this.identifier = identifier; - } - - /** - * Attempts to authorize the request. - * - * @param request The request sent. - * @param response The response to update. - * @return True if the authorization succeeded. - */ - protected abstract boolean authorize(Request request, Response response); - - /** - * Invoked upon successful authorization. Returns {@link Filter#CONTINUE} by - * default. - * - * @param request The request sent. - * @param response The response to update. - * @return The filter continuation code. - */ - protected int authorized(Request request, Response response) { - return CONTINUE; - } - - @Override - protected int beforeHandle(Request request, Response response) { - if (authorize(request, response)) { - return authorized(request, response); - } - - return unauthorized(request, response); - } - - /** - * Returns the identifier unique within an application. - * - * @return The identifier unique within an application. - */ - public String getIdentifier() { - return identifier; - } - - /** - * Sets the identifier unique within an application. - * - * @param identifier The identifier unique within an application. - */ - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - /** - * Invoked upon failed authorization. Sets the status to - * {@link Status#CLIENT_ERROR_FORBIDDEN} and returns {@link Filter#STOP} by - * default. - * - * @param request The request sent. - * @param response The response to update. - * @return The filter continuation code. - */ - protected int unauthorized(Request request, Response response) { - response.setStatus(Status.CLIENT_ERROR_FORBIDDEN); - return STOP; - } - + /** Authorizer returning true all the time. */ + public static final Authorizer ALWAYS = + new Authorizer() { + @Override + public boolean authorize(Request request, Response response) { + return true; + } + }; + + /** + * Authorizer returning true for all authenticated requests. For unauthenticated requests, it + * sets the response's status to {@link Status#CLIENT_ERROR_UNAUTHORIZED} instead of the default + * {@link Status#CLIENT_ERROR_FORBIDDEN}. + * + * @see ClientInfo#isAuthenticated() + */ + public static final Authorizer AUTHENTICATED = + new Authorizer() { + @Override + public boolean authorize(Request request, Response response) { + return request.getClientInfo().isAuthenticated(); + } + + @Override + protected int unauthorized(Request request, Response response) { + response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED); + return STOP; + } + }; + + /** Authorizer returning false all the time. */ + public static final Authorizer NEVER = + new Authorizer() { + @Override + public boolean authorize(Request request, Response response) { + return false; + } + }; + + /** The identifier unique within an application. */ + private volatile String identifier; + + /** Default constructor. */ + public Authorizer() {} + + /** + * Constructor. + * + * @param identifier The identifier unique within an application. + */ + public Authorizer(String identifier) { + this.identifier = identifier; + } + + /** + * Attempts to authorize the request. + * + * @param request The request sent. + * @param response The response to update. + * @return True if the authorization succeeded. + */ + protected abstract boolean authorize(Request request, Response response); + + /** + * Invoked upon successful authorization. Returns {@link Filter#CONTINUE} by default. + * + * @param request The request sent. + * @param response The response to update. + * @return The filter continuation code. + */ + protected int authorized(Request request, Response response) { + return CONTINUE; + } + + @Override + protected int beforeHandle(Request request, Response response) { + if (authorize(request, response)) { + return authorized(request, response); + } + + return unauthorized(request, response); + } + + /** + * Returns the identifier unique within an application. + * + * @return The identifier unique within an application. + */ + public String getIdentifier() { + return identifier; + } + + /** + * Sets the identifier unique within an application. + * + * @param identifier The identifier unique within an application. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Invoked upon failed authorization. Sets the status to {@link Status#CLIENT_ERROR_FORBIDDEN} + * and returns {@link Filter#STOP} by default. + * + * @param request The request sent. + * @param response The response to update. + * @return The filter continuation code. + */ + protected int unauthorized(Request request, Response response) { + response.setStatus(Status.CLIENT_ERROR_FORBIDDEN); + return STOP; + } } diff --git a/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java b/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java index d5863072f8..5dc6d366ad 100644 --- a/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java @@ -1,119 +1,113 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.Status; - -import javax.security.auth.x500.X500Principal; import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; +import javax.security.auth.x500.X500Principal; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Status; /** - * Authenticator based on the SSL client certificate. If a client certificate is - * presented, and accepted by your SSL certificate truststore, it adds the - * Principal of its subject to the list of principals in the request's - * ClientInfo. It also sets the user to be a new User based on this Principal. - * - * {@link #getPrincipals(List)} and {@link #getUser(Principal)} can be - * overridden to change the default behavior. - * + * Authenticator based on the SSL client certificate. If a client certificate is presented, and + * accepted by your SSL certificate truststore, it adds the Principal of its subject to the list of + * principals in the request's ClientInfo. It also sets the user to be a new User based on this + * Principal. + * + *

{@link #getPrincipals(List)} and {@link #getUser(Principal)} can be overridden to change the + * default behavior. + * * @author Bruno Harbulot (bruno/distributedmatter.net) */ public class CertificateAuthenticator extends Authenticator { - /** - * - * @param context - */ - public CertificateAuthenticator(Context context) { - super(context); - } - - /** - * Extracts the Principal of the subject to use from a chain of certificate. By - * default, this is the X500Principal of the subject of the first - * certificate in the chain. - * - * @see X509Certificate - * @see X500Principal - * @param certificateChain chain of client certificates. - * @return Principal of the client certificate or null if the chain is empty. - */ - protected List getPrincipals(List certificateChain) { - ArrayList principals = null; + /** + * @param context + */ + public CertificateAuthenticator(Context context) { + super(context); + } - if ((certificateChain != null) && (!certificateChain.isEmpty())) { - Certificate userCert = certificateChain.get(0); + /** + * Extracts the Principal of the subject to use from a chain of certificate. By default, this is + * the X500Principal of the subject of the first certificate in the chain. + * + * @see X509Certificate + * @see X500Principal + * @param certificateChain chain of client certificates. + * @return Principal of the client certificate or null if the chain is empty. + */ + protected List getPrincipals(List certificateChain) { + ArrayList principals = null; - if (userCert instanceof X509Certificate) { - principals = new ArrayList(); - principals.add(((X509Certificate) userCert).getSubjectX500Principal()); - } + if ((certificateChain != null) && (!certificateChain.isEmpty())) { + Certificate userCert = certificateChain.get(0); - return principals; - } else { - return null; - } - } + if (userCert instanceof X509Certificate) { + principals = new ArrayList<>(); + principals.add(((X509Certificate) userCert).getSubjectX500Principal()); + } - /** - * Creates a new User based on the subject's X500Principal. By default, the user - * name is the subject distinguished name, formatted accorded to RFC 2253. Some - * may choose to extract the Common Name only, for example. - * - * @param principal subject's Principal (most likely X500Principal). - * @return User instance corresponding to this principal or null. - */ - protected User getUser(Principal principal) { - if (principal != null) { - return new User(principal.getName()); - } else { - return null; - } - } + return principals; + } else { + return null; + } + } - /** - * Authenticates the call using the X.509 client certificate. The verification - * of the credentials is normally done by the SSL layer, via the TrustManagers. - * - * It uses the certificate chain in the request's - * "org.restlet.https.clientCertificates" attribute, adds the principal returned - * from this chain by {@link #getPrincipals(List)} to the request's ClientInfo - * and set the user to the result of {@link #getUser(Principal)} if that user is - * non-null. - * - * If no client certificate is available, then a 401 status is set. - */ - @Override - protected boolean authenticate(Request request, Response response) { - List certchain = request.getClientInfo().getCertificates(); - List principals = getPrincipals(certchain); + /** + * Creates a new User based on the subject's X500Principal. By default, the username is the + * subject distinguished name, formatted according to RFC 2253. Some may choose to extract the + * Common Name only, for example. + * + * @param principal subject's Principal (most likely X500Principal). + * @return User instance corresponding to this principal or null. + */ + protected User getUser(Principal principal) { + if (principal != null) { + return new User(principal.getName()); + } else { + return null; + } + } - if ((principals != null) && (!principals.isEmpty())) { - request.getClientInfo().getPrincipals().addAll(principals); - User user = getUser(principals.get(0)); + /** + * Authenticates the call using the X.509 client certificate. The SSL layer normally does the + * verification of the credentials, via the TrustManagers. + * + *

It uses the certificate chain in the request's "org.restlet.https.clientCertificates" + * attribute, adds the principal returned from this chain by {@link #getPrincipals(List)} to the + * request's ClientInfo, and set the user to the result of {@link #getUser(Principal)} if that + * user is non-null. + * + *

If no client certificate is available, then a 401 status is set. + */ + @Override + protected boolean authenticate(Request request, Response response) { + List certChain = request.getClientInfo().getCertificates(); + List principals = getPrincipals(certChain); - if (user != null) { - request.getClientInfo().setUser(user); - } - return true; - } else { - response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED); - return false; - } - } + if ((principals != null) && (!principals.isEmpty())) { + request.getClientInfo().getPrincipals().addAll(principals); + User user = getUser(principals.getFirst()); + if (user != null) { + request.getClientInfo().setUser(user); + } + return true; + } else { + response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED); + return false; + } + } } diff --git a/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java b/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java index 2fedc570e6..85670e71ba 100644 --- a/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java @@ -1,25 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; - -import java.util.logging.Level; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.ClientInfo; +import org.restlet.data.Status; /** - * Authenticator based on a challenge scheme. This is typically used to support - * the HTTP BASIC and DIGEST challenge schemes. - * + * Authenticator based on a challenge scheme. This is typically used to support the HTTP BASIC and + * DIGEST challenge schemes. + * * @see ChallengeScheme * @see ChallengeRequest * @see ChallengeResponse @@ -27,275 +29,286 @@ */ public class ChallengeAuthenticator extends Authenticator { - /** The authentication realm. */ - private volatile String realm; - - /** - * Indicates if a new challenge should be sent when invalid credentials are - * received (true by default to conform to HTTP recommendations). - */ - private volatile boolean rechallenging; - - /** The expected challenge scheme. */ - private final ChallengeScheme scheme; - - /** The credentials verifier. */ - private volatile Verifier verifier; - - /** - * Constructor using the context's default verifier. - * - * @param context The context. - * @param optional Indicates if the authentication success is optional. - * @param challengeScheme The authentication scheme to use. - * @param realm The authentication realm. - * - * @see #ChallengeAuthenticator(Context, boolean, ChallengeScheme, String, - * Verifier) - */ - public ChallengeAuthenticator(Context context, boolean optional, ChallengeScheme challengeScheme, String realm) { - this(context, optional, challengeScheme, realm, (context != null) ? context.getDefaultVerifier() : null); - } - - /** - * Constructor. - * - * @param context The context. - * @param optional Indicates if the authentication success is optional. - * @param challengeScheme The authentication scheme to use. - * @param realm The authentication realm. - * @param verifier The credentials verifier. - */ - public ChallengeAuthenticator(Context context, boolean optional, ChallengeScheme challengeScheme, String realm, - Verifier verifier) { - super(context, optional); - this.realm = realm; - this.rechallenging = true; - this.scheme = challengeScheme; - this.verifier = verifier; - } - - /** - * Constructor setting the optional property to false. - * - * @param context The context. - * @param challengeScheme The authentication scheme to use. - * @param realm The authentication realm. - * @see #ChallengeAuthenticator(Context, boolean, ChallengeScheme, String, - * Verifier) - */ - public ChallengeAuthenticator(Context context, ChallengeScheme challengeScheme, String realm) { - this(context, false, challengeScheme, realm); - } - - /** - * Authenticates the call, relying on the verifier to check the credentials - * provided (in general an identifier + secret couple). If the credentials are - * valid, the next Restlet attached is invoked.
- *
- * If the credentials are missing, then {@link #challenge(Response, boolean)} is - * invoked.
- *
- * If the credentials are invalid and if the "rechallenge" property is true then - * {@link #challenge(Response, boolean)} is invoked. Otherwise, - * {@link #forbid(Response)} is invoked.
- *
- * If the credentials are stale, then {@link #challenge(Response, boolean)} is - * invoked with the "stale" parameter to true.
- *
- * At the end of the process, the {@link ClientInfo#setAuthenticated(boolean)} - * method is invoked. - */ - @Override - protected boolean authenticate(Request request, Response response) { - boolean result = false; - boolean loggable = request.isLoggable() && getLogger().isLoggable(Level.FINE); - - if (getVerifier() != null) { - switch (getVerifier().verify(request, response)) { - case Verifier.RESULT_VALID: - // Valid credentials provided - result = true; - - if (loggable) { - ChallengeResponse challengeResponse = request.getChallengeResponse(); - - if (challengeResponse != null) { - getLogger().fine("Authentication succeeded. Valid credentials provided for identifier: " - + request.getChallengeResponse().getIdentifier() + "."); - } else { - getLogger().fine("Authentication succeeded. Valid credentials provided."); - } - } - break; - case Verifier.RESULT_MISSING: - // No credentials provided - if (loggable) { - getLogger().fine("Authentication failed. No credentials provided."); - } - - if (!isOptional()) { - challenge(response, false); - } - break; - case Verifier.RESULT_INVALID: - // Invalid credentials provided - if (loggable) { - getLogger().fine("Authentication failed. Invalid credentials provided."); - } - - if (!isOptional()) { - if (isRechallenging()) { - challenge(response, false); - } else { - forbid(response); - } - } - break; - case Verifier.RESULT_STALE: - if (loggable) { - getLogger().fine("Authentication failed. Stale credentials provided."); - } - - if (!isOptional()) { - challenge(response, true); - } - break; - case Verifier.RESULT_UNKNOWN: - if (loggable) { - getLogger().fine("Authentication failed. Identifier is unknown."); - } - - if (!isOptional()) { - if (isRechallenging()) { - challenge(response, false); - } else { - forbid(response); - } - } - break; - } - } else { - getLogger().warning("Authentication failed. No verifier provided."); - response.setStatus(Status.SERVER_ERROR_INTERNAL, "Authentication failed. No verifier provided."); - } - - return result; - } - - /** - * Challenges the client by adding a challenge request to the response and by - * setting the status to {@link Status#CLIENT_ERROR_UNAUTHORIZED}. - * - * @param response The response to update. - * @param stale Indicates if the new challenge is due to a stale response. - */ - public void challenge(Response response, boolean stale) { - boolean loggable = response.getRequest().isLoggable() && getLogger().isLoggable(Level.FINE); - - if (loggable) { - getLogger().log(Level.FINE, "An authentication challenge was requested."); - } - - response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED); - response.getChallengeRequests().add(createChallengeRequest(stale)); - } - - /** - * Creates a new challenge request. - * - * @param stale Indicates if the new challenge is due to a stale response. - * @return A new challenge request. - */ - protected ChallengeRequest createChallengeRequest(boolean stale) { - return new ChallengeRequest(getScheme(), getRealm()); - } - - /** - * Rejects the call due to a failed authentication or authorization. This can be - * overridden to change the default behavior, for example to display an error - * page. By default, if authentication is required, the challenge method is - * invoked, otherwise the call status is set to CLIENT_ERROR_FORBIDDEN. - * - * @param response The reject response. - */ - public void forbid(Response response) { - boolean loggable = response.getRequest().isLoggable() && getLogger().isLoggable(Level.FINE); - - if (loggable) { - getLogger().log(Level.FINE, - "Authentication or authorization failed for this URI: " + response.getRequest().getResourceRef()); - } - - response.setStatus(Status.CLIENT_ERROR_FORBIDDEN); - } - - /** - * Returns the authentication realm. - * - * @return The authentication realm. - */ - public String getRealm() { - return this.realm; - } - - /** - * Returns the authentication challenge scheme. - * - * @return The authentication challenge scheme. - */ - public ChallengeScheme getScheme() { - return scheme; - } - - /** - * Returns the credentials verifier. - * - * @return The credentials verifier. - */ - public Verifier getVerifier() { - return verifier; - } - - /** - * Indicates if a new challenge should be sent when invalid credentials are - * received (true by default to conform to HTTP recommendations). If set to - * false, upon reception of invalid credentials, the method - * {@link #forbid(Response)} will be called. - * - * @return True if invalid credentials result in a new challenge. - */ - public boolean isRechallenging() { - return this.rechallenging; - } - - /** - * Sets the authentication realm. - * - * @param realm The authentication realm. - */ - public void setRealm(String realm) { - this.realm = realm; - } - - /** - * Indicates if a new challenge should be sent when invalid credentials are - * received. - * - * @param rechallenging True if invalid credentials result in a new challenge. - * @see #isRechallenging() - */ - public void setRechallenging(boolean rechallenging) { - this.rechallenging = rechallenging; - } - - /** - * Sets the credentials verifier. - * - * @param verifier The credentials verifier. - */ - public void setVerifier(Verifier verifier) { - this.verifier = verifier; - } - + /** The authentication realm. */ + private volatile String realm; + + /** + * Indicates if a new challenge should be sent when invalid credentials are received (true by + * default to conform to HTTP recommendations). + */ + private volatile boolean rechallenging; + + /** The expected challenge scheme. */ + private final ChallengeScheme scheme; + + /** The credentials verifier. */ + private volatile Verifier verifier; + + /** + * Constructor using the context's default verifier. + * + * @param context The context. + * @param optional Indicates if the authentication success is optional. + * @param challengeScheme The authentication scheme to use. + * @param realm The authentication realm. + * @see #ChallengeAuthenticator(Context, boolean, ChallengeScheme, String, Verifier) + */ + public ChallengeAuthenticator( + Context context, boolean optional, ChallengeScheme challengeScheme, String realm) { + this( + context, + optional, + challengeScheme, + realm, + (context != null) ? context.getDefaultVerifier() : null); + } + + /** + * Constructor. + * + * @param context The context. + * @param optional Indicates if the authentication success is optional. + * @param challengeScheme The authentication scheme to use. + * @param realm The authentication realm. + * @param verifier The credentials' verifier. + */ + public ChallengeAuthenticator( + Context context, + boolean optional, + ChallengeScheme challengeScheme, + String realm, + Verifier verifier) { + super(context, optional); + this.realm = realm; + this.rechallenging = true; + this.scheme = challengeScheme; + this.verifier = verifier; + } + + /** + * Constructor setting the optional property to false. + * + * @param context The context. + * @param challengeScheme The authentication scheme to use. + * @param realm The authentication realm. + * @see #ChallengeAuthenticator(Context, boolean, ChallengeScheme, String, Verifier) + */ + public ChallengeAuthenticator(Context context, ChallengeScheme challengeScheme, String realm) { + this(context, false, challengeScheme, realm); + } + + /** + * Authenticates the call, relying on the verifier to check the credentials provided (in general + * an identifier and secret couple). If the credentials are valid, the next Restlet attached is + * invoked.
+ *
+ * If the credentials are missing, then {@link #challenge(Response, boolean)} is invoked.
+ *
+ * If the credentials are invalid and if the "rechallenge" property is true then {@link + * #challenge(Response, boolean)} is invoked. Otherwise, {@link #forbid(Response)} is invoked. + *
+ *
+ * If the credentials are stale, then {@link #challenge(Response, boolean)} is invoked with the + * "stale" parameter to true.
+ *
+ * At the end of the process, the {@link ClientInfo#setAuthenticated(boolean)} method is + * invoked. + */ + @Override + protected boolean authenticate(Request request, Response response) { + boolean result = false; + boolean loggable = request.isLoggable() && getLogger().isLoggable(Level.FINE); + + if (getVerifier() != null) { + switch (getVerifier().verify(request, response)) { + case Verifier.RESULT_VALID: + // Valid credentials provided + result = true; + + if (loggable) { + ChallengeResponse challengeResponse = request.getChallengeResponse(); + + if (challengeResponse != null) { + getLogger() + .fine( + "Authentication succeeded. Valid credentials provided for identifier: " + + request.getChallengeResponse().getIdentifier() + + "."); + } else { + getLogger() + .fine("Authentication succeeded. Valid credentials provided."); + } + } + break; + case Verifier.RESULT_MISSING: + // No credentials provided + if (loggable) { + getLogger().fine("Authentication failed. No credentials provided."); + } + + if (!isOptional()) { + challenge(response, false); + } + break; + case Verifier.RESULT_INVALID: + // Invalid credentials provided + if (loggable) { + getLogger().fine("Authentication failed. Invalid credentials provided."); + } + + if (!isOptional()) { + if (isRechallenging()) { + challenge(response, false); + } else { + forbid(response); + } + } + break; + case Verifier.RESULT_STALE: + if (loggable) { + getLogger().fine("Authentication failed. Stale credentials provided."); + } + + if (!isOptional()) { + challenge(response, true); + } + break; + case Verifier.RESULT_UNKNOWN: + if (loggable) { + getLogger().fine("Authentication failed. Identifier is unknown."); + } + + if (!isOptional()) { + if (isRechallenging()) { + challenge(response, false); + } else { + forbid(response); + } + } + break; + } + } else { + getLogger().warning("Authentication failed. No verifier provided."); + response.setStatus( + Status.SERVER_ERROR_INTERNAL, "Authentication failed. No verifier provided."); + } + + return result; + } + + /** + * Challenges the client by adding a challenge request to the response and by setting the status + * to {@link Status#CLIENT_ERROR_UNAUTHORIZED}. + * + * @param response The response to update. + * @param stale Indicates if the new challenge is due to a stale response. + */ + public void challenge(Response response, boolean stale) { + boolean loggable = response.getRequest().isLoggable() && getLogger().isLoggable(Level.FINE); + + if (loggable) { + getLogger().log(Level.FINE, "An authentication challenge was requested."); + } + + response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED); + response.getChallengeRequests().add(createChallengeRequest(stale)); + } + + /** + * Creates a new challenge request. + * + * @param stale Indicates if the new challenge is due to a stale response. + * @return A new challenge request. + */ + protected ChallengeRequest createChallengeRequest(boolean stale) { + return new ChallengeRequest(getScheme(), getRealm()); + } + + /** + * Rejects the call due to a failed authentication or authorization. This can be overridden to + * change the default behavior, for example, to display an error page. By default, if + * authentication is required, the challenge method is invoked, otherwise the call status is set + * to CLIENT_ERROR_FORBIDDEN. + * + * @param response The reject response. + */ + public void forbid(Response response) { + boolean loggable = response.getRequest().isLoggable() && getLogger().isLoggable(Level.FINE); + + if (loggable) { + getLogger() + .log( + Level.FINE, + "Authentication or authorization failed for this URI: " + + response.getRequest().getResourceRef()); + } + + response.setStatus(Status.CLIENT_ERROR_FORBIDDEN); + } + + /** + * Returns the authentication realm. + * + * @return The authentication realm. + */ + public String getRealm() { + return this.realm; + } + + /** + * Returns the authentication challenge scheme. + * + * @return The authentication challenge scheme. + */ + public ChallengeScheme getScheme() { + return scheme; + } + + /** + * Returns the credentials verifier. + * + * @return The credentials verifier. + */ + public Verifier getVerifier() { + return verifier; + } + + /** + * Indicates if a new challenge should be sent when invalid credentials are received (true by + * default to conform to HTTP recommendations). If set to false, upon reception of invalid + * credentials, the method {@link #forbid(Response)} will be called. + * + * @return True if invalid credentials result in a new challenge. + */ + public boolean isRechallenging() { + return this.rechallenging; + } + + /** + * Sets the authentication realm. + * + * @param realm The authentication realm. + */ + public void setRealm(String realm) { + this.realm = realm; + } + + /** + * Indicates if a new challenge should be sent when invalid credentials are received. + * + * @param rechallenging True if invalid credentials result in a new challenge. + * @see #isRechallenging() + */ + public void setRechallenging(boolean rechallenging) { + this.rechallenging = rechallenging; + } + + /** + * Sets the credentials' verifier. + * + * @param verifier The credentials' verifier. + */ + public void setVerifier(Verifier verifier) { + this.verifier = verifier; + } } diff --git a/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java b/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java index c24767f7b1..df295091ca 100644 --- a/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java @@ -1,35 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import org.restlet.Request; import org.restlet.Response; /** - * Authorizer allowing only confidential calls. Confidential calls typically - * come through HTTPS server connectors. - * + * Authorizer allowing only confidential calls. Confidential calls typically come through HTTPS + * server connectors. + * * @author Jerome Louvel */ public class ConfidentialAuthorizer extends Authorizer { - /** - * Authorizes the request only if its method is one of the authorized methods. - * - * @param request The request sent. - * @param response The response to update. - * @return True if the authorization succeeded. - */ - @Override - public boolean authorize(Request request, Response response) { - return request.isConfidential(); - } - + /** + * Authorizes the request only if its method is one of the authorized methods. + * + * @param request The request sent. + * @param response The response to update. + * @return True if the authorization succeeded. + */ + @Override + public boolean authorize(Request request, Response response) { + return request.isConfidential(); + } } diff --git a/org.restlet/src/main/java/org/restlet/security/Enroler.java b/org.restlet/src/main/java/org/restlet/security/Enroler.java index 43ad1d9a04..905a5a5352 100644 --- a/org.restlet/src/main/java/org/restlet/security/Enroler.java +++ b/org.restlet/src/main/java/org/restlet/security/Enroler.java @@ -1,23 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; -import org.restlet.data.ClientInfo; - import java.security.Principal; +import org.restlet.data.ClientInfo; /** - * Updates an authenticated client user with assigned roles. Typically, it is - * invoked by an {@link Authenticator} after successful authentication to add - * {@link Role} instances based on available {@link User}. - * + * Updates an authenticated client user with assigned roles. Typically, it is invoked by an {@link + * Authenticator} after successful authentication to add {@link Role} instances based on available + * {@link User}. + * * @see Authenticator#getEnroler() * @see Authenticator#setEnroler(Enroler) * @see ClientInfo#getUser() @@ -26,15 +24,13 @@ */ public interface Enroler { - /** - * Attempts to update an authenticated client, with a {@link User} properly - * defined, by adding the {@link Role} that are assigned to this user. Note that - * principals could also be added to the {@link ClientInfo} if necessary. The - * addition could also potentially be based on the presence of - * {@link Principal}. - * - * @param clientInfo The clientInfo to update. - */ - void enrole(ClientInfo clientInfo); - + /** + * Attempts to update an authenticated client, with a {@link User} properly defined, by adding + * the {@link Role} that are assigned to this user. Note that principals could also be added to + * the {@link ClientInfo} if necessary. The addition could also potentially be based on the + * presence of {@link Principal}. + * + * @param clientInfo The clientInfo to update. + */ + void enrole(ClientInfo clientInfo); } diff --git a/org.restlet/src/main/java/org/restlet/security/Group.java b/org.restlet/src/main/java/org/restlet/security/Group.java index 68c15676b5..9eae99c5cb 100644 --- a/org.restlet/src/main/java/org/restlet/security/Group.java +++ b/org.restlet/src/main/java/org/restlet/security/Group.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import java.util.List; @@ -14,171 +13,166 @@ /** * Group that contains member groups and users. - * + * * @author Jerome Louvel */ public class Group { - /** The description. */ - private volatile String description; - - /** - * Indicates if the roles of the parent group should be inherited. Those roles - * indirectly cover the granted or denied permissions. - */ - private volatile boolean inheritingRoles; - - /** The modifiable list of child groups. */ - private final List memberGroups; - - /** The modifiable list of members user references. */ - private final List memberUsers; - - /** The display name. */ - private volatile String name; - - /** - * Default constructor. Note that roles are inherited by default. - */ - public Group() { - this(null, null); - } - - /** - * Constructor. Note that roles are inherited by default. - * - * @param name The display name. - * @param description The description. - */ - public Group(String name, String description) { - this(name, description, true); - } - - /** - * Constructor. - * - * @param name The display name. - * @param description The description. - * @param inheritingRoles Indicates if the roles of the parent group should be - * inherited. - */ - public Group(String name, String description, boolean inheritingRoles) { - this.name = name; - this.description = description; - this.inheritingRoles = inheritingRoles; - this.memberGroups = new CopyOnWriteArrayList(); - this.memberUsers = new CopyOnWriteArrayList(); - } - - /** - * Returns the description. - * - * @return The description - */ - public String getDescription() { - return this.description; - } - - /** - * Returns the modifiable list of member groups. - * - * @return The modifiable list of member groups. - */ - public List getMemberGroups() { - return memberGroups; - } - - public List getMemberUsers() { - return memberUsers; - } - - /** - * Returns the display name. - * - * @return The display name. - */ - public String getName() { - return this.name; - } - - /** - * Indicates if the roles of the parent group should be inherited. Those roles - * indirectly cover the granted or denied permissions. - * - * @return True if the roles of the parent group should be inherited. - */ - public boolean isInheritingRoles() { - return inheritingRoles; - } - - /** - * Sets the description. - * - * @param description The description. - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Indicates if the roles of the parent group should be inherited. Those roles - * indirectly cover the granted or denied permissions. - * - * @param inheritingRoles True if the roles of the parent group should be - * inherited. - */ - public void setInheritingRoles(boolean inheritingRoles) { - this.inheritingRoles = inheritingRoles; - } - - /** - * Sets the modifiable list of member groups. This method clears the current - * list and adds all entries in the parameter list. - * - * @param memberGroups A list of member groups. - */ - public void setMemberGroups(List memberGroups) { - synchronized (getMemberGroups()) { - if (memberGroups != getMemberGroups()) { - getMemberGroups().clear(); - - if (memberGroups != null) { - getMemberGroups().addAll(memberGroups); - } - } - } - } - - /** - * Sets the modifiable list of member user references. This method clears the - * current list and adds all entries in the parameter list. - * - * @param memberUsers A list of member user references. - */ - public void setMemberUsers(List memberUsers) { - synchronized (getMemberUsers()) { - if (memberUsers != getMemberUsers()) { - getMemberUsers().clear(); - - if (memberUsers != null) { - getMemberUsers().addAll(memberUsers); - } - } - } - } - - /** - * Sets the display name. - * - * @param name The display name. - */ - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return getName(); - } - + /** The description. */ + private volatile String description; + + /** + * Indicates if the roles of the parent group should be inherited. Those roles indirectly cover + * the granted or denied permissions. + */ + private volatile boolean inheritingRoles; + + /** The modifiable list of child groups. */ + private final List memberGroups; + + /** The modifiable list of members user references. */ + private final List memberUsers; + + /** The display name. */ + private volatile String name; + + /** Default constructor. Note that roles are inherited by default. */ + public Group() { + this(null, null); + } + + /** + * Constructor. Note that roles are inherited by default. + * + * @param name The display name. + * @param description The description. + */ + public Group(String name, String description) { + this(name, description, true); + } + + /** + * Constructor. + * + * @param name The display name. + * @param description The description. + * @param inheritingRoles Indicates if the roles of the parent group should be inherited. + */ + public Group(String name, String description, boolean inheritingRoles) { + this.name = name; + this.description = description; + this.inheritingRoles = inheritingRoles; + this.memberGroups = new CopyOnWriteArrayList(); + this.memberUsers = new CopyOnWriteArrayList(); + } + + /** + * Returns the description. + * + * @return The description + */ + public String getDescription() { + return this.description; + } + + /** + * Returns the modifiable list of member groups. + * + * @return The modifiable list of member groups. + */ + public List getMemberGroups() { + return memberGroups; + } + + public List getMemberUsers() { + return memberUsers; + } + + /** + * Returns the display name. + * + * @return The display name. + */ + public String getName() { + return this.name; + } + + /** + * Indicates if the roles of the parent group should be inherited. Those roles indirectly cover + * the granted or denied permissions. + * + * @return True if the roles of the parent group should be inherited. + */ + public boolean isInheritingRoles() { + return inheritingRoles; + } + + /** + * Sets the description. + * + * @param description The description. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Indicates if the roles of the parent group should be inherited. Those roles indirectly cover + * the granted or denied permissions. + * + * @param inheritingRoles True if the roles of the parent group should be inherited. + */ + public void setInheritingRoles(boolean inheritingRoles) { + this.inheritingRoles = inheritingRoles; + } + + /** + * Sets the modifiable list of member groups. This method clears the current list and adds all + * entries in the parameter list. + * + * @param memberGroups A list of member groups. + */ + public void setMemberGroups(List memberGroups) { + synchronized (getMemberGroups()) { + if (memberGroups != getMemberGroups()) { + getMemberGroups().clear(); + + if (memberGroups != null) { + getMemberGroups().addAll(memberGroups); + } + } + } + } + + /** + * Sets the modifiable list of member user references. This method clears the current list and + * adds all entries in the parameter list. + * + * @param memberUsers A list of member user references. + */ + public void setMemberUsers(List memberUsers) { + synchronized (getMemberUsers()) { + if (memberUsers != getMemberUsers()) { + getMemberUsers().clear(); + + if (memberUsers != null) { + getMemberUsers().addAll(memberUsers); + } + } + } + } + + /** + * Sets the display name. + * + * @param name The display name. + */ + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return getName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java b/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java index ec161b6de2..fc1134f713 100644 --- a/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java +++ b/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java @@ -1,34 +1,31 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; /** - * Verifier that can locally retrieve the secrets. This verifier assumes that - * the secret associated to an identifier can be retrieved, which isn't always - * possible or even desirable. - * + * Verifier that can locally retrieve the secrets. This verifier assumes that the secret associated + * to an identifier can be retrieved, which isn't always possible or even desirable. + * * @author Jerome Louvel */ public abstract class LocalVerifier extends SecretVerifier { - /** - * Returns the local secret associated to a given identifier. - * - * @param identifier The identifier to lookup. - * @return The secret associated to the identifier or null. - */ - public abstract char[] getLocalSecret(String identifier); - - @Override - public int verify(String identifier, char[] secret) { - return compare(secret, getLocalSecret(identifier)) ? RESULT_VALID : RESULT_INVALID; - } + /** + * Returns the local secret associated with a given identifier. + * + * @param identifier The identifier to lookup. + * @return The secret associated with the identifier or null. + */ + public abstract char[] getLocalSecret(String identifier); + @Override + public int verify(String identifier, char[] secret) { + return compare(secret, getLocalSecret(identifier)) ? RESULT_VALID : RESULT_INVALID; + } } diff --git a/org.restlet/src/main/java/org/restlet/security/MapVerifier.java b/org.restlet/src/main/java/org/restlet/security/MapVerifier.java index 121bb2eaaa..2a1332f1ae 100644 --- a/org.restlet/src/main/java/org/restlet/security/MapVerifier.java +++ b/org.restlet/src/main/java/org/restlet/security/MapVerifier.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import java.util.Map; @@ -14,62 +13,59 @@ import java.util.concurrent.ConcurrentMap; /** - * Verifier that stores its local secrets in a map indexed by the identifier. - * Note that this verifier isn't very secure by itself. - * + * Verifier that stores its local secrets in a map indexed by the identifier. Note that this + * verifier isn't very secure by itself. + * * @author Jerome Louvel */ public class MapVerifier extends LocalVerifier { - /** The map of local secrets. */ - private final ConcurrentMap localSecrets; - - /** - * Constructor. - */ - public MapVerifier() { - this(new ConcurrentHashMap()); - } + /** The map of local secrets. */ + private final ConcurrentMap localSecrets; - /** - * Constructor. - * - * @param localSecrets The map of local secrets. - */ - public MapVerifier(ConcurrentMap localSecrets) { - this.localSecrets = localSecrets; - } + /** Constructor. */ + public MapVerifier() { + this(new ConcurrentHashMap<>()); + } - @Override - public char[] getLocalSecret(String identifier) { - return (identifier == null) ? null : getLocalSecrets().get(identifier); - } + /** + * Constructor. + * + * @param localSecrets The map of local secrets. + */ + public MapVerifier(ConcurrentMap localSecrets) { + this.localSecrets = localSecrets; + } - /** - * Returns the map of local secrets. - * - * @return The map of local secrets. - */ - public ConcurrentMap getLocalSecrets() { - return localSecrets; - } + @Override + public char[] getLocalSecret(String identifier) { + return (identifier == null) ? null : getLocalSecrets().get(identifier); + } - /** - * Sets the modifiable map of local secrets. This method clears the current map - * and puts all entries in the parameter map. - * - * @param localSecrets A map of local secrets. - */ - public void setLocalSecrets(Map localSecrets) { - synchronized (getLocalSecrets()) { - if (localSecrets != getLocalSecrets()) { - getLocalSecrets().clear(); + /** + * Returns the map of local secrets. + * + * @return The map of local secrets. + */ + public ConcurrentMap getLocalSecrets() { + return localSecrets; + } - if (localSecrets != null) { - getLocalSecrets().putAll(localSecrets); - } - } - } - } + /** + * Sets the modifiable map of local secrets. This method clears the current map and puts all + * entries in the parameter map. + * + * @param localSecrets A map of local secrets. + */ + public void setLocalSecrets(Map localSecrets) { + synchronized (getLocalSecrets()) { + if (localSecrets != getLocalSecrets()) { + getLocalSecrets().clear(); + if (localSecrets != null) { + getLocalSecrets().putAll(localSecrets); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java b/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java index 10e3ef6b57..0b952d162c 100644 --- a/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java +++ b/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java @@ -1,513 +1,509 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; -import org.restlet.Application; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.ClientInfo; -import org.restlet.engine.security.RoleMapping; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.Application; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.ClientInfo; +import org.restlet.engine.security.RoleMapping; /** - * Security realm based on a memory model. The model is composed of root groups, - * users and mapping to associated roles. - * + * Security realm based on a memory model. The model is composed of root groups, users and mapping + * to associated roles. + * * @author Jerome Louvel */ public class MemoryRealm extends Realm { - /** - * Enroler based on the default security model. - */ - private class DefaultEnroler implements Enroler { - - public void enrole(ClientInfo clientInfo) { - User user = findUser(clientInfo.getUser().getIdentifier()); - - if (user != null) { - // Find all the inherited groups of this user - Set userGroups = findGroups(user); - - // Add roles specific to this user - Set userRoles = findRoles(user); - - for (Role role : userRoles) { - clientInfo.getRoles().add(role); - } - - // Add roles common to group members - Set groupRoles = findRoles(userGroups); - - for (Role role : groupRoles) { - clientInfo.getRoles().add(role); - } - } - } - } - - /** - * Verifier based on the default security model. It looks up users in the mapped - * organizations. - */ - private class DefaultVerifier extends SecretVerifier { - - @Override - protected User createUser(String identifier, Request request, Response response) { - User result = new User(identifier); - - // Find the reference user - User user = findUser(identifier); - - if (user != null) { - // Copy the properties of the reference user - result.setEmail(user.getEmail()); - result.setFirstName(user.getFirstName()); - result.setLastName(user.getLastName()); - } - - return result; - } - - @Override - public int verify(String identifier, char[] secret) { - char[] actualSecret = null; - User user = findUser(identifier); - - if (user != null) { - actualSecret = user.getSecret(); - } - - return compare(secret, actualSecret) ? RESULT_VALID : RESULT_INVALID; - } - } - - /** The modifiable list of role mappings. */ - private final List roleMappings; - - /** The modifiable list of root groups. */ - private final List rootGroups; - - /** The modifiable list of users. */ - private final List users; - - /** - * Constructor. - */ - public MemoryRealm() { - setVerifier(new DefaultVerifier()); - setEnroler(new DefaultEnroler()); - this.rootGroups = new CopyOnWriteArrayList(); - this.roleMappings = new CopyOnWriteArrayList(); - this.users = new CopyOnWriteArrayList(); - } - - /** - * Recursively adds groups where a given user is a member. - * - * @param user The member user. - * @param userGroups The set of user groups to update. - * @param currentGroup The current group to inspect. - * @param stack The stack of ancestor groups. - * @param inheritOnly Indicates if only the ancestors groups that have their - * "inheritRoles" property enabled should be added. - */ - private void addGroups(User user, Set userGroups, Group currentGroup, List stack, - boolean inheritOnly) { - if ((currentGroup != null) && !stack.contains(currentGroup)) { - stack.add(currentGroup); - - if (currentGroup.getMemberUsers().contains(user)) { - userGroups.add(currentGroup); - - // Add the ancestor groups as well - boolean inherit = !inheritOnly || currentGroup.isInheritingRoles(); - Group group; - - for (int i = stack.size() - 2; inherit && (i >= 0); i--) { - group = stack.get(i); - userGroups.add(group); - inherit = !inheritOnly || group.isInheritingRoles(); - } - } - - for (Group group : currentGroup.getMemberGroups()) { - addGroups(user, userGroups, group, stack, inheritOnly); - } - } - } - - /** - * Finds the set of groups where a given user is a member. Note that inheritable - * ancestors groups are also returned. - * - * @param user The member user. - * @return The set of groups. - */ - public Set findGroups(User user) { - return findGroups(user, true); - } - - /** - * Finds the set of groups where a given user is a member. - * - * @param user The member user. - * @param inheritOnly Indicates if only the ancestors groups that have their - * "inheritRoles" property enabled should be added. - * @return The set of groups. - */ - public Set findGroups(User user, boolean inheritOnly) { - Set result = new HashSet(); - List stack; - - // Recursively find user groups - for (Group group : getRootGroups()) { - stack = new ArrayList(); - addGroups(user, result, group, stack, inheritOnly); - } - - return result; - } - - /** - * Finds the roles mapped to a given user group. - * - * @param application The parent application. Can't be null. - * @param userGroup The user group. - * @return The roles found. - * @throws IllegalArgumentException If application is null. - */ - public Set findRoles(Application application, Group userGroup) { - if (application == null) { - throw new IllegalArgumentException("The application argument can't be null"); - } - - Set result = new HashSet(); - Object source; - - for (RoleMapping mapping : getRoleMappings()) { - source = mapping.getSource(); - - if ((userGroup != null) && userGroup.equals(source)) { - if (mapping.getTarget().getApplication() == application) { - result.add(mapping.getTarget()); - } - } - } - - return result; - } - - /** - * Finds the roles mapped to given user groups. - * - * @param application The parent application. Can't be null. - * @param userGroups The user groups. - * @return The roles found. - * @throws IllegalArgumentException If application is null. - */ - public Set findRoles(Application application, Set userGroups) { - if (application == null) { - throw new IllegalArgumentException("The application argument can't be null"); - } - - Set result = new HashSet(); - Object source; - - for (RoleMapping mapping : getRoleMappings()) { - source = mapping.getSource(); - - if ((userGroups != null) && userGroups.contains(source)) { - if (mapping.getTarget().getApplication() == application) { - result.add(mapping.getTarget()); - } - } - } - - return result; - } - - /** - * Finds the roles mapped to a given user, for a specific application. - * - * @param application The parent application. Can't be null. - * @param user The user. - * @return The roles found. - * @throws IllegalArgumentException If application is null. - */ - public Set findRoles(Application application, User user) { - if (application == null) { - throw new IllegalArgumentException("The application argument can't be null"); - } - - Set result = new HashSet(); - Object source; - - for (RoleMapping mapping : getRoleMappings()) { - source = mapping.getSource(); - - if ((user != null) && user.equals(source)) { - if (mapping.getTarget().getApplication() == application) { - result.add(mapping.getTarget()); - } - } - } - - return result; - } - - /** - * Finds the roles mapped to given user group. - * - * @param userGroup The user group. - * @return The roles found. - */ - public Set findRoles(Group userGroup) { - Set result = new HashSet(); - Object source; - - for (RoleMapping mapping : getRoleMappings()) { - source = mapping.getSource(); - - if ((userGroup != null) && userGroup.equals(source)) { - result.add(mapping.getTarget()); - } - } - - return result; - } - - /** - * Finds the roles mapped to given user groups. - * - * @param userGroups The user groups. - * @return The roles found. - */ - public Set findRoles(Set userGroups) { - Set result = new HashSet(); - Object source; - - for (RoleMapping mapping : getRoleMappings()) { - source = mapping.getSource(); - - if ((userGroups != null) && userGroups.contains(source)) { - result.add(mapping.getTarget()); - } - } - - return result; - } - - /** - * Finds the roles mapped to a given user. - * - * @param user The user. - * @return The roles found. - */ - public Set findRoles(User user) { - Set result = new HashSet(); - Object source; - - for (RoleMapping mapping : getRoleMappings()) { - source = mapping.getSource(); - - if ((user != null) && user.equals(source)) { - result.add(mapping.getTarget()); - } - } - - return result; - } - - /** - * Finds a user in the organization based on its identifier. - * - * @param userIdentifier The identifier to match. - * @return The matched user or null. - */ - public User findUser(String userIdentifier) { - User result = null; - User user; - - for (int i = 0; (result == null) && (i < getUsers().size()); i++) { - user = getUsers().get(i); - - if (user.getIdentifier().equals(userIdentifier)) { - result = user; - } - } - - return result; - } - - /** - * Returns the modifiable list of role mappings. - * - * @return The modifiable list of role mappings. - */ - private List getRoleMappings() { - return roleMappings; - } - - /** - * Returns the modifiable list of root groups. - * - * @return The modifiable list of root groups. - */ - public List getRootGroups() { - return rootGroups; - } - - /** - * Returns the modifiable list of users. - * - * @return The modifiable list of users. - */ - public List getUsers() { - return users; - } - - /** - * Maps a group defined in a component to a role defined in the application. - * - * @param group The source group. - * @param role The target role. - */ - public void map(Group group, Role role) { - getRoleMappings().add(new RoleMapping(group, role)); - } - - /** - * Maps a user defined in a component to a role defined in the application. - * - * @param user The source user. - * @param application The parent application. Can't be null. - * @param roleName The target role name. - * @throws IllegalArgumentException If application is null. - */ - public void map(User user, Application application, String roleName) { - map(user, Role.get(application, roleName, null)); - } - - /** - * Maps a user defined in a component to a role defined in the application. - * - * @param user The source user. - * @param role The target role. - */ - public void map(User user, Role role) { - getRoleMappings().add(new RoleMapping(user, role)); - } - - /** - * Sets the modifiable list of root groups. This method clears the current list - * and adds all entries in the parameter list. - * - * @param rootGroups A list of root groups. - */ - public void setRootGroups(List rootGroups) { - synchronized (getRootGroups()) { - if (rootGroups != getRootGroups()) { - getRootGroups().clear(); - - if (rootGroups != null) { - getRootGroups().addAll(rootGroups); - } - } - } - } - - /** - * Sets the modifiable list of users. This method clears the current list and - * adds all entries in the parameter list. - * - * @param users A list of users. - */ - public void setUsers(List users) { - synchronized (getUsers()) { - if (users != getUsers()) { - getUsers().clear(); - - if (users != null) { - getUsers().addAll(users); - } - } - } - } - - /** - * Unmaps a group defined in a component from a role defined in the application. - * - * @param group The source group. - * @param application The parent application. Can't be null. - * @param roleName The target role name. - * @throws IllegalArgumentException If application is null. - */ - public void unmap(Group group, Application application, String roleName) { - unmap(group, Role.get(application, roleName, null)); - } - - /** - * Unmaps a group defined in a component from a role defined in the application. - * - * @param group The source group. - * @param role The target role. - */ - public void unmap(Group group, Role role) { - unmap((Object) group, role); - } - - /** - * Unmaps an element (user, group or organization) defined in a component from a - * role defined in the application. - * - * @param source The source group. - * @param role The target role. - */ - private void unmap(Object source, Role role) { - RoleMapping mapping; - - for (int i = getRoleMappings().size() - 1; i >= 0; i--) { - mapping = getRoleMappings().get(i); - - if (mapping.getSource().equals(source) && mapping.getTarget().equals(role)) { - getRoleMappings().remove(i); - } - } - } - - /** - * Unmaps a user defined in a component from a role defined in the application. - * - * @param user The source user. - * @param application The parent application. Can't be null. - * @param roleName The target role name. - * @throws IllegalArgumentException If application is null. - */ - public void unmap(User user, Application application, String roleName) { - unmap(user, Role.get(application, roleName, null)); - } - - /** - * Unmaps a user defined in a component from a role defined in the application. - * - * @param user The source user. - * @param role The target role. - */ - public void unmap(User user, Role role) { - unmap((Object) user, role); - } - + /** Enroler based on the default security model. */ + private class DefaultEnroler implements Enroler { + + public void enrole(ClientInfo clientInfo) { + User user = findUser(clientInfo.getUser().getIdentifier()); + + if (user != null) { + // Find all the inherited groups of this user + Set userGroups = findGroups(user); + + // Add roles specific to this user + Set userRoles = findRoles(user); + + for (Role role : userRoles) { + clientInfo.getRoles().add(role); + } + + // Add roles common to group members + Set groupRoles = findRoles(userGroups); + + for (Role role : groupRoles) { + clientInfo.getRoles().add(role); + } + } + } + } + + /** + * Verifier based on the default security model. It looks up users in the mapped organizations. + */ + private class DefaultVerifier extends SecretVerifier { + + @Override + protected User createUser(String identifier, Request request, Response response) { + User result = new User(identifier); + + // Find the reference user + User user = findUser(identifier); + + if (user != null) { + // Copy the properties of the reference user + result.setEmail(user.getEmail()); + result.setFirstName(user.getFirstName()); + result.setLastName(user.getLastName()); + } + + return result; + } + + @Override + public int verify(String identifier, char[] secret) { + char[] actualSecret = null; + User user = findUser(identifier); + + if (user != null) { + actualSecret = user.getSecret(); + } + + return compare(secret, actualSecret) ? RESULT_VALID : RESULT_INVALID; + } + } + + /** The modifiable list of role mappings. */ + private final List roleMappings; + + /** The modifiable list of root groups. */ + private final List rootGroups; + + /** The modifiable list of users. */ + private final List users; + + /** Constructor. */ + public MemoryRealm() { + setVerifier(new DefaultVerifier()); + setEnroler(new DefaultEnroler()); + this.rootGroups = new CopyOnWriteArrayList<>(); + this.roleMappings = new CopyOnWriteArrayList<>(); + this.users = new CopyOnWriteArrayList<>(); + } + + /** + * Recursively adds groups where a given user is a member. + * + * @param user The member user. + * @param userGroups The set of user groups to update. + * @param currentGroup The current group to inspect. + * @param stack The stack of ancestor groups. + * @param inheritOnly Indicates if only the ancestors groups that have their "inheritRoles" + * property enabled should be added. + */ + private void addGroups( + User user, + Set userGroups, + Group currentGroup, + List stack, + boolean inheritOnly) { + if ((currentGroup != null) && !stack.contains(currentGroup)) { + stack.add(currentGroup); + + if (currentGroup.getMemberUsers().contains(user)) { + userGroups.add(currentGroup); + + // Add the ancestor groups as well + boolean inherit = !inheritOnly || currentGroup.isInheritingRoles(); + Group group; + + for (int i = stack.size() - 2; inherit && (i >= 0); i--) { + group = stack.get(i); + userGroups.add(group); + inherit = !inheritOnly || group.isInheritingRoles(); + } + } + + for (Group group : currentGroup.getMemberGroups()) { + addGroups(user, userGroups, group, stack, inheritOnly); + } + } + } + + /** + * Finds the set of groups where a given user is a member. Note that inheritable ancestors + * groups are also returned. + * + * @param user The member user. + * @return The set of groups. + */ + public Set findGroups(User user) { + return findGroups(user, true); + } + + /** + * Finds the set of groups where a given user is a member. + * + * @param user The member user. + * @param inheritOnly Indicates if only the ancestors groups that have their "inheritRoles" + * property enabled should be added. + * @return The set of groups. + */ + public Set findGroups(User user, boolean inheritOnly) { + Set result = new HashSet(); + List stack; + + // Recursively find user groups + for (Group group : getRootGroups()) { + stack = new ArrayList<>(); + addGroups(user, result, group, stack, inheritOnly); + } + + return result; + } + + /** + * Finds the roles mapped to a given user group. + * + * @param application The parent application. Can't be null. + * @param userGroup The user group. + * @return The roles found. + * @throws IllegalArgumentException If the application is null. + */ + public Set findRoles(Application application, Group userGroup) { + if (application == null) { + throw new IllegalArgumentException("The application argument can't be null"); + } + + Set result = new HashSet<>(); + Object source; + + for (RoleMapping mapping : getRoleMappings()) { + source = mapping.getSource(); + + if ((userGroup != null) && userGroup.equals(source)) { + if (mapping.getTarget().getApplication() == application) { + result.add(mapping.getTarget()); + } + } + } + + return result; + } + + /** + * Finds the roles mapped to given user groups. + * + * @param application The parent application. Can't be null. + * @param userGroups The user groups. + * @return The roles found. + * @throws IllegalArgumentException If the application is null. + */ + public Set findRoles(Application application, Set userGroups) { + if (application == null) { + throw new IllegalArgumentException("The application argument can't be null"); + } + + Set result = new HashSet(); + Object source; + + for (RoleMapping mapping : getRoleMappings()) { + source = mapping.getSource(); + + if ((userGroups != null) && userGroups.contains(source)) { + if (mapping.getTarget().getApplication() == application) { + result.add(mapping.getTarget()); + } + } + } + + return result; + } + + /** + * Finds the roles mapped to a given user, for a specific application. + * + * @param application The parent application. Can't be null. + * @param user The user. + * @return The roles found. + * @throws IllegalArgumentException If application is null. + */ + public Set findRoles(Application application, User user) { + if (application == null) { + throw new IllegalArgumentException("The application argument can't be null"); + } + + Set result = new HashSet(); + Object source; + + for (RoleMapping mapping : getRoleMappings()) { + source = mapping.getSource(); + + if ((user != null) && user.equals(source)) { + if (mapping.getTarget().getApplication() == application) { + result.add(mapping.getTarget()); + } + } + } + + return result; + } + + /** + * Finds the roles mapped to a given user group. + * + * @param userGroup The user group. + * @return The roles found. + */ + public Set findRoles(Group userGroup) { + Set result = new HashSet<>(); + Object source; + + for (RoleMapping mapping : getRoleMappings()) { + source = mapping.getSource(); + + if ((userGroup != null) && userGroup.equals(source)) { + result.add(mapping.getTarget()); + } + } + + return result; + } + + /** + * Finds the roles mapped to given user groups. + * + * @param userGroups The user groups. + * @return The roles found. + */ + public Set findRoles(Set userGroups) { + Set result = new HashSet<>(); + Object source; + + for (RoleMapping mapping : getRoleMappings()) { + source = mapping.getSource(); + + if ((userGroups != null) && userGroups.contains(source)) { + result.add(mapping.getTarget()); + } + } + + return result; + } + + /** + * Finds the roles mapped to a given user. + * + * @param user The user. + * @return The roles found. + */ + public Set findRoles(User user) { + Set result = new HashSet<>(); + Object source; + + for (RoleMapping mapping : getRoleMappings()) { + source = mapping.getSource(); + + if ((user != null) && user.equals(source)) { + result.add(mapping.getTarget()); + } + } + + return result; + } + + /** + * Finds a user in the organization based on its identifier. + * + * @param userIdentifier The identifier to match. + * @return The matched user or null. + */ + public User findUser(String userIdentifier) { + User result = null; + User user; + + for (int i = 0; (result == null) && (i < getUsers().size()); i++) { + user = getUsers().get(i); + + if (user.getIdentifier().equals(userIdentifier)) { + result = user; + } + } + + return result; + } + + /** + * Returns the modifiable list of role mappings. + * + * @return The modifiable list of role mappings. + */ + private List getRoleMappings() { + return roleMappings; + } + + /** + * Returns the modifiable list of root groups. + * + * @return The modifiable list of root groups. + */ + public List getRootGroups() { + return rootGroups; + } + + /** + * Returns the modifiable list of users. + * + * @return The modifiable list of users. + */ + public List getUsers() { + return users; + } + + /** + * Maps a group defined in a component to a role defined in the application. + * + * @param group The source group. + * @param role The target role. + */ + public void map(Group group, Role role) { + getRoleMappings().add(new RoleMapping(group, role)); + } + + /** + * Maps a user defined in a component to a role defined in the application. + * + * @param user The source user. + * @param application The parent application. Can't be null. + * @param roleName The target role name. + * @throws IllegalArgumentException If the application is null. + */ + public void map(User user, Application application, String roleName) { + map(user, Role.get(application, roleName, null)); + } + + /** + * Maps a user defined in a component to a role defined in the application. + * + * @param user The source user. + * @param role The target role. + */ + public void map(User user, Role role) { + getRoleMappings().add(new RoleMapping(user, role)); + } + + /** + * Sets the modifiable list of root groups. This method clears the current list and adds all + * entries in the parameter list. + * + * @param rootGroups A list of root groups. + */ + public void setRootGroups(List rootGroups) { + synchronized (getRootGroups()) { + if (rootGroups != getRootGroups()) { + getRootGroups().clear(); + + if (rootGroups != null) { + getRootGroups().addAll(rootGroups); + } + } + } + } + + /** + * Sets the modifiable list of users. This method clears the current list and adds all entries + * in the parameter list. + * + * @param users A list of users. + */ + public void setUsers(List users) { + synchronized (getUsers()) { + if (users != getUsers()) { + getUsers().clear(); + + if (users != null) { + getUsers().addAll(users); + } + } + } + } + + /** + * Unmaps a group defined in a component from a role defined in the application. + * + * @param group The source group. + * @param application The parent application. Can't be null. + * @param roleName The target role name. + * @throws IllegalArgumentException If the application is null. + */ + public void unmap(Group group, Application application, String roleName) { + unmap(group, Role.get(application, roleName, null)); + } + + /** + * Unmaps a group defined in a component from a role defined in the application. + * + * @param group The source group. + * @param role The target role. + */ + public void unmap(Group group, Role role) { + unmap((Object) group, role); + } + + /** + * Unmaps an element (user, group or organization) defined in a component from a role defined in + * the application. + * + * @param source The source group. + * @param role The target role. + */ + private void unmap(Object source, Role role) { + RoleMapping mapping; + + for (int i = getRoleMappings().size() - 1; i >= 0; i--) { + mapping = getRoleMappings().get(i); + + if (mapping.getSource().equals(source) && mapping.getTarget().equals(role)) { + getRoleMappings().remove(i); + } + } + } + + /** + * Unmaps a user defined in a component from a role defined in the application. + * + * @param user The source user. + * @param application The parent application. Can't be null. + * @param roleName The target role name. + * @throws IllegalArgumentException If the application is null. + */ + public void unmap(User user, Application application, String roleName) { + unmap(user, Role.get(application, roleName, null)); + } + + /** + * Unmaps a user defined in a component from a role defined in the application. + * + * @param user The source user. + * @param role The target role. + */ + public void unmap(User user, Role role) { + unmap((Object) user, role); + } } diff --git a/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java b/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java index b11028c77e..f8fbf3a4ef 100644 --- a/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java @@ -1,133 +1,127 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Request; import org.restlet.Response; import org.restlet.data.Method; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - /** - * Authorizer based on authorized methods. Note that this authorizer makes the - * difference between authenticated and anonymous users. - * + * Authorizer based on authorized methods. Note that this authorizer makes the difference between + * authenticated and anonymous users. + * * @author Jerome Louvel */ public class MethodAuthorizer extends Authorizer { - /** The modifiable list of methods authorized for anonymous users. */ - private List anonymousMethods; - - /** The modifiable list of methods authorized for authenticated users. */ - private List authenticatedMethods; - - /** - * Default constructor. - */ - public MethodAuthorizer() { - this(null); - } - - /** - * Constructor. - * - * @param identifier The identifier unique within an application. - */ - public MethodAuthorizer(String identifier) { - super(identifier); - - this.anonymousMethods = new CopyOnWriteArrayList(); - this.authenticatedMethods = new CopyOnWriteArrayList(); - } - - /** - * Authorizes the request only if its method is one of the authorized methods. - * - * @param request The request sent. - * @param response The response to update. - * @return True if the authorization succeeded. - */ - @Override - public boolean authorize(Request request, Response response) { - boolean authorized = false; - - if (request.getClientInfo().isAuthenticated()) { - // Verify if the request method is one of the forbidden methods - for (Method authenticatedMethod : getAuthenticatedMethods()) { - authorized = authorized || request.getMethod().equals(authenticatedMethod); - } - } else { - // Verify if the request method is one of the authorized methods - for (Method authorizedMethod : getAnonymousMethods()) { - authorized = authorized || request.getMethod().equals(authorizedMethod); - } - } - - return authorized; - } - - /** - * Returns the modifiable list of methods authorized for anonymous users. - * - * @return The modifiable list of methods authorized for anonymous users. - */ - public List getAnonymousMethods() { - return anonymousMethods; - } - - /** - * Returns the modifiable list of methods authorized for authenticated users. - * - * @return The modifiable list of methods authorized for authenticated users. - */ - public List getAuthenticatedMethods() { - return authenticatedMethods; - } - - /** - * Sets the modifiable list of methods authorized for anonymous users. This - * method clears the current list and adds all entries in the parameter list. - * - * @param anonymousMethods A list of methods authorized for anonymous users. - */ - public void setAnonymousMethods(List anonymousMethods) { - synchronized (getAnonymousMethods()) { - if (anonymousMethods != getAnonymousMethods()) { - getAnonymousMethods().clear(); - - if (anonymousMethods != null) { - getAnonymousMethods().addAll(anonymousMethods); - } - } - } - } - - /** - * Sets the modifiable list of methods authorized for authenticated users. This - * method clears the current list and adds all entries in the parameter list. - * - * @param authenticatedMethods A list of methods authorized for authenticated - * users. - */ - public void setAuthenticatedMethods(List authenticatedMethods) { - synchronized (getAuthenticatedMethods()) { - if (authenticatedMethods != getAuthenticatedMethods()) { - getAuthenticatedMethods().clear(); - - if (authenticatedMethods != null) { - getAuthenticatedMethods().addAll(authenticatedMethods); - } - } - } - } - + /** The modifiable list of methods authorized for anonymous users. */ + private List anonymousMethods; + + /** The modifiable list of methods authorized for authenticated users. */ + private List authenticatedMethods; + + /** Default constructor. */ + public MethodAuthorizer() { + this(null); + } + + /** + * Constructor. + * + * @param identifier The identifier unique within an application. + */ + public MethodAuthorizer(String identifier) { + super(identifier); + + this.anonymousMethods = new CopyOnWriteArrayList<>(); + this.authenticatedMethods = new CopyOnWriteArrayList<>(); + } + + /** + * Authorizes the request only if its method is one of the authorized methods. + * + * @param request The request sent. + * @param response The response to update. + * @return True if the authorization succeeded. + */ + @Override + public boolean authorize(Request request, Response response) { + boolean authorized = false; + + if (request.getClientInfo().isAuthenticated()) { + // Verify if the request method is one of the forbidden methods + for (Method authenticatedMethod : getAuthenticatedMethods()) { + authorized = authorized || request.getMethod().equals(authenticatedMethod); + } + } else { + // Verify if the request method is one of the authorized methods + for (Method authorizedMethod : getAnonymousMethods()) { + authorized = authorized || request.getMethod().equals(authorizedMethod); + } + } + + return authorized; + } + + /** + * Returns the modifiable list of methods authorized for anonymous users. + * + * @return The modifiable list of methods authorized for anonymous users. + */ + public List getAnonymousMethods() { + return anonymousMethods; + } + + /** + * Returns the modifiable list of methods authorized for authenticated users. + * + * @return The modifiable list of methods authorized for authenticated users. + */ + public List getAuthenticatedMethods() { + return authenticatedMethods; + } + + /** + * Sets the modifiable list of methods authorized for anonymous users. This method clears the + * current list and adds all entries in the parameter list. + * + * @param anonymousMethods A list of methods authorized for anonymous users. + */ + public void setAnonymousMethods(List anonymousMethods) { + synchronized (getAnonymousMethods()) { + if (anonymousMethods != getAnonymousMethods()) { + getAnonymousMethods().clear(); + + if (anonymousMethods != null) { + getAnonymousMethods().addAll(anonymousMethods); + } + } + } + } + + /** + * Sets the modifiable list of methods authorized for authenticated users. This method clears + * the current list and adds all entries in the parameter list. + * + * @param authenticatedMethods A list of methods authorized for authenticated users. + */ + public void setAuthenticatedMethods(List authenticatedMethods) { + synchronized (getAuthenticatedMethods()) { + if (authenticatedMethods != getAuthenticatedMethods()) { + getAuthenticatedMethods().clear(); + + if (authenticatedMethods != null) { + getAuthenticatedMethods().addAll(authenticatedMethods); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/security/Realm.java b/org.restlet/src/main/java/org/restlet/security/Realm.java index d51a71ed3d..12631a4755 100644 --- a/org.restlet/src/main/java/org/restlet/security/Realm.java +++ b/org.restlet/src/main/java/org/restlet/security/Realm.java @@ -1,186 +1,174 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.data.Parameter; import org.restlet.util.Series; -import java.util.concurrent.CopyOnWriteArrayList; - /** * Security realm capable of providing an enroler and a verifier. - * + * * @author Jerome Louvel */ public abstract class Realm { - /** The name. */ - private volatile String name; - - /** The modifiable series of parameters. */ - private final Series parameters; - - /** - * The enroler that can add the user roles based on user principals. - */ - private volatile Enroler enroler; - - /** - * The verifier that can check the validity of the user credentials associated - * to a request. - */ - private volatile Verifier verifier; - - /** Indicates if the realm was started. */ - private volatile boolean started; - - /** - * Constructor. - */ - public Realm() { - this(null, null); - } - - /** - * Constructor. - * - * @param verifier The verifier that can check the validity of the credentials - * associated to a request. - * - * @param enroler The enroler that can add the user roles based on user - * principals. - */ - public Realm(Verifier verifier, Enroler enroler) { - this.enroler = enroler; - this.verifier = verifier; - this.parameters = new Series(Parameter.class, new CopyOnWriteArrayList()); - this.started = false; - } - - /** - * Returns an enroler that can add the user roles based on user principals. - * - * @return An enroler. - */ - public Enroler getEnroler() { - return enroler; - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Returns the modifiable series of parameters. A parameter is a pair composed - * of a name and a value and is typically used for configuration purpose, like - * Java properties. Note that multiple parameters with the same name can be - * declared and accessed. - * - * @return The modifiable series of parameters. - */ - public Series getParameters() { - return this.parameters; - } - - /** - * Returns a verifier that can check the validity of the credentials associated - * to a request. - * - * @return A verifier. - */ - public Verifier getVerifier() { - return this.verifier; - } - - /** - * Indicates if the realm is started. - * - * @return True if the realm is started. - */ - public boolean isStarted() { - return this.started; - } - - /** - * Indicates if the realm is stopped. - * - * @return True if the realm is stopped. - */ - public boolean isStopped() { - return !this.started; - } - - /** - * Sets an enroler that can add the user roles based on user principals. - * - * @param enroler An enroler. - */ - public void setEnroler(Enroler enroler) { - this.enroler = enroler; - } - - /** - * Sets the name. - * - * @param name The name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Sets the modifiable series of parameters. This method clears the current - * series and adds all entries in the parameter series. - * - * @param parameters A series of parameters. - */ - public void setParameters(Series parameters) { - synchronized (getParameters()) { - if (parameters != getParameters()) { - getParameters().clear(); - - if (parameters != null) { - getParameters().addAll(parameters); - } - } - } - } - - /** - * Sets a verifier that can check the validity of the credentials associated to - * a request. - * - * @param verifier A local verifier. - */ - public void setVerifier(Verifier verifier) { - this.verifier = verifier; - } - - /** Starts the realm. */ - public synchronized void start() throws Exception { - this.started = true; - } - - /** Stops the realm. */ - public synchronized void stop() throws Exception { - this.started = false; - } - - @Override - public String toString() { - return getName(); - } - + /** The name. */ + private volatile String name; + + /** The modifiable series of parameters. */ + private final Series parameters; + + /** The enroler that can add the user roles based on user principals. */ + private volatile Enroler enroler; + + /** + * The verifier that can check the validity of the user credentials associated with a request. + */ + private volatile Verifier verifier; + + /** Indicates if the realm was started. */ + private volatile boolean started; + + /** Constructor. */ + public Realm() { + this(null, null); + } + + /** + * Constructor. + * + * @param verifier The verifier that can check the validity of the credentials associated with a + * request. + * @param enroler The enroler that can add the user roles based on user principals. + */ + public Realm(Verifier verifier, Enroler enroler) { + this.enroler = enroler; + this.verifier = verifier; + this.parameters = + new Series(Parameter.class, new CopyOnWriteArrayList()); + this.started = false; + } + + /** + * Returns an enroler that can add the user roles based on user principals. + * + * @return An enroler. + */ + public Enroler getEnroler() { + return enroler; + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Returns the modifiable series of parameters. A parameter is a pair composed of a name and a + * value and is typically used for configuration purposes, like Java properties. Note that + * multiple parameters with the same name can be declared and accessed. + * + * @return The modifiable series of parameters. + */ + public Series getParameters() { + return this.parameters; + } + + /** + * Returns a verifier that can check the validity of the credentials associated with a request. + * + * @return A verifier. + */ + public Verifier getVerifier() { + return this.verifier; + } + + /** + * Indicates if the realm is started. + * + * @return True if the realm is started. + */ + public boolean isStarted() { + return this.started; + } + + /** + * Indicates if the realm is stopped. + * + * @return True if the realm is stopped. + */ + public boolean isStopped() { + return !this.started; + } + + /** + * Sets an enroler that can add the user roles based on user principals. + * + * @param enroler An enroler. + */ + public void setEnroler(Enroler enroler) { + this.enroler = enroler; + } + + /** + * Sets the name. + * + * @param name The name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the modifiable series of parameters. This method clears the current series and adds all + * entries in the parameter series. + * + * @param parameters A series of parameters. + */ + public void setParameters(Series parameters) { + synchronized (getParameters()) { + if (parameters != getParameters()) { + getParameters().clear(); + + if (parameters != null) { + getParameters().addAll(parameters); + } + } + } + } + + /** + * Sets a verifier that can check the validity of the credentials associated with a request. + * + * @param verifier A local verifier. + */ + public void setVerifier(Verifier verifier) { + this.verifier = verifier; + } + + /** Starts the realm. */ + public synchronized void start() throws Exception { + this.started = true; + } + + /** Stops the realm. */ + public synchronized void stop() throws Exception { + this.started = false; + } + + @Override + public String toString() { + return getName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/security/Role.java b/org.restlet/src/main/java/org/restlet/security/Role.java index f4b71fa2aa..df63883017 100644 --- a/org.restlet/src/main/java/org/restlet/security/Role.java +++ b/org.restlet/src/main/java/org/restlet/security/Role.java @@ -1,209 +1,203 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; -import org.restlet.Application; -import org.restlet.engine.util.SystemUtils; - import java.security.Principal; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.Application; +import org.restlet.engine.util.SystemUtils; /** - * Application specific role. Common examples are "administrator", "user", - * "anonymous", "supervisor". Note that for reusability purpose, it is - * recommended that those role don't reflect an actual organization, but more - * the functional requirements of your application. - * - * Two roles are considered equals if they belong to the same parent application - * and have the same name and child roles. The description isn't used for - * equality assessment. - * - * Since version 2.2, they don't need to be the same Java objects anymore. In - * order to prevent the multiplication of equivalent {@link Role} instances, you - * should try to call {@link Application#getRole(String)} method. - * + * Application-specific role. Common examples are "administrator", "user", "anonymous", + * "supervisor". Note that for reusability purposes, it is recommended that those roles don't reflect + * an actual organization, but more the functional requirements of your application. + * + *

Two roles are considered equals if they belong to the same parent application and have the + * same name and child roles. The description isn't used for equality assessment. + * + *

Since version 2.2, they don't need to be the same Java objects anymore. To prevent the + * multiplication of equivalent {@link Role} instances, you should try to call {@link + * Application#getRole(String)} method. + * * @author Jerome Louvel * @author Tim Peierls */ public class Role implements Principal { - /** - * Finds an existing role or creates a new one if needed. Note that a null - * description will be set if the role has to be created. - * - * @param application The parent application. - * @param name The role name to find or create. - * @return The role found or created. - */ - public static Role get(Application application, String name) { - return get(application, name, null); - } - - /** - * Finds an existing role or creates a new one if needed. - * - * @param application The parent application. - * @param name The role name to find or create. - * @param description The role description if one needs to be created. - * @return The role found or created. - */ - public static Role get(Application application, String name, String description) { - Role role = (application == null) ? null : application.getRole(name); - return (role == null) ? new Role(application, name, description) : role; - } - - /** The parent application. */ - private volatile Application application; - - /** The modifiable list of child roles. */ - private final List childRoles; - - /** The description. */ - private volatile String description; - - /** The name. */ - private volatile String name; - - /** - * Default constructor. Note that the parent application is retrieved using the - * {@link Application#getCurrent()} method if available or is null. - */ - public Role() { - this(Application.getCurrent(), null, null); - } - - /** - * Constructor. - * - * @param application The parent application or null. - * @param name The name. - */ - public Role(Application application, String name) { - this(application, name, null); - } - - /** - * Constructor. - * - * @param application The parent application or null. - * @param name The name. - * @param description The description. - */ - public Role(Application application, String name, String description) { - this.application = application; - this.name = name; - this.description = description; - this.childRoles = new CopyOnWriteArrayList(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Role)) - return false; - - Role that = (Role) o; - return Objects.equals(that.getApplication(), getApplication()) && Objects.equals(that.getName(), getName()) - && Objects.equals(that.getChildRoles(), getChildRoles()); - } - - /** - * Returns the parent application. - * - * @return The parent application. - */ - public Application getApplication() { - return application; - } - - /** - * Returns the modifiable list of child roles. - * - * @return The modifiable list of child roles. - */ - public List getChildRoles() { - return childRoles; - } - - /** - * Returns the description. - * - * @return The description. - */ - public String getDescription() { - return description; - } - - /** - * Returns the name. - * - * @return The name. - */ - public String getName() { - return name; - } - - @Override - public int hashCode() { - return SystemUtils.hashCode(getApplication(), getName(), getChildRoles()); - } - - /** - * Sets the parent application. - * - * @param application The parent application. - */ - public void setApplication(Application application) { - this.application = application; - } - - /** - * Sets the modifiable list of child roles. This method clears the current list - * and adds all entries in the parameter list. - * - * @param childRoles A list of child roles. - */ - public void setChildRoles(List childRoles) { - synchronized (getChildRoles()) { - if (childRoles != getChildRoles()) { - getChildRoles().clear(); - - if (childRoles != null) { - getChildRoles().addAll(childRoles); - } - } - } - } - - /** - * Sets the description. - * - * @param description The description. - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Sets the name. - * - * @param name The name. - */ - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return getName(); - } - + /** + * Finds an existing role or creates a new one if needed. Note that a null description will be + * set if the role has to be created. + * + * @param application The parent application. + * @param name The role name to find or create. + * @return The role found or created. + */ + public static Role get(Application application, String name) { + return get(application, name, null); + } + + /** + * Finds an existing role or creates a new one if needed. + * + * @param application The parent application. + * @param name The role name to find or create. + * @param description The role description if one needs to be created. + * @return The role found or created. + */ + public static Role get(Application application, String name, String description) { + Role role = (application == null) ? null : application.getRole(name); + return (role == null) ? new Role(application, name, description) : role; + } + + /** The parent application. */ + private volatile Application application; + + /** The modifiable list of child roles. */ + private final List childRoles; + + /** The description. */ + private volatile String description; + + /** The name. */ + private volatile String name; + + /** + * Default constructor. Note that the parent application is retrieved using the {@link + * Application#getCurrent()} method if available or is null. + */ + public Role() { + this(Application.getCurrent(), null, null); + } + + /** + * Constructor. + * + * @param application The parent application or null. + * @param name The name. + */ + public Role(Application application, String name) { + this(application, name, null); + } + + /** + * Constructor. + * + * @param application The parent application or null. + * @param name The name. + * @param description The description. + */ + public Role(Application application, String name, String description) { + this.application = application; + this.name = name; + this.description = description; + this.childRoles = new CopyOnWriteArrayList<>(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof final Role that)) return false; + + return Objects.equals(that.getApplication(), getApplication()) + && Objects.equals(that.getName(), getName()) + && Objects.equals(that.getChildRoles(), getChildRoles()); + } + + /** + * Returns the parent application. + * + * @return The parent application. + */ + public Application getApplication() { + return application; + } + + /** + * Returns the modifiable list of child roles. + * + * @return The modifiable list of child roles. + */ + public List getChildRoles() { + return childRoles; + } + + /** + * Returns the description. + * + * @return The description. + */ + public String getDescription() { + return description; + } + + /** + * Returns the name. + * + * @return The name. + */ + public String getName() { + return name; + } + + @Override + public int hashCode() { + return SystemUtils.hashCode(getApplication(), getName(), getChildRoles()); + } + + /** + * Sets the parent application. + * + * @param application The parent application. + */ + public void setApplication(Application application) { + this.application = application; + } + + /** + * Sets the modifiable list of child roles. This method clears the current list and adds all + * entries in the parameter list. + * + * @param childRoles A list of child roles. + */ + public void setChildRoles(List childRoles) { + synchronized (getChildRoles()) { + if (childRoles != getChildRoles()) { + getChildRoles().clear(); + + if (childRoles != null) { + getChildRoles().addAll(childRoles); + } + } + } + } + + /** + * Sets the description. + * + * @param description The description. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Sets the name. + * + * @param name The name. + */ + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return getName(); + } } diff --git a/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java b/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java index 3f929e7908..3aecacee11 100644 --- a/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java @@ -1,136 +1,131 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; -import org.restlet.Request; -import org.restlet.Response; - import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.Request; +import org.restlet.Response; /** - * Authorizer based on authorized and forbidden roles. Note that if no role is - * added to the "authorizedRoles" list, then only the "forbiddenRoles" list is - * considered. - * + * Authorizer based on authorized and forbidden roles. Note that if no role is added to the + * "authorizedRoles" list, then only the "forbiddenRoles" list is considered. + * * @author Jerome Louvel */ public class RoleAuthorizer extends Authorizer { - /** The modifiable list of authorized roles. */ - private List authorizedRoles; - - /** The modifiable list of forbidden roles. */ - private List forbiddenRoles; - - /** - * Default constructor. - */ - public RoleAuthorizer() { - this(null); - } - - /** - * Constructor. - * - * @param identifier The identifier unique within an application. - */ - public RoleAuthorizer(String identifier) { - super(identifier); - - this.authorizedRoles = new CopyOnWriteArrayList(); - this.forbiddenRoles = new CopyOnWriteArrayList(); - } - - /** - * Authorizes the request only if its subject is in one of the authorized roles - * and in none of the forbidden ones. - * - * @param request The request sent. - * @param response The response to update. - * @return True if the authorization succeeded. - */ - @Override - public boolean authorize(Request request, Response response) { - boolean authorized = false; - boolean forbidden = false; - - // Verify if the subject is in one of the authorized roles - if (getAuthorizedRoles().isEmpty()) { - authorized = true; - } else { - for (Role authorizedRole : getAuthorizedRoles()) { - authorized = authorized || request.getClientInfo().getRoles().contains(authorizedRole); - } - } - - // Verify if the subject is in one of the forbidden roles - for (Role forbiddenRole : getForbiddenRoles()) { - forbidden = forbidden || request.getClientInfo().getRoles().contains(forbiddenRole); - } - - return authorized && !forbidden; - } - - /** - * Returns the modifiable list of authorized roles. - * - * @return The modifiable list of authorized roles. - */ - public List getAuthorizedRoles() { - return authorizedRoles; - } - - /** - * Returns the modifiable list of forbidden roles. - * - * @return The modifiable list of forbidden roles. - */ - public List getForbiddenRoles() { - return forbiddenRoles; - } - - /** - * Sets the modifiable list of authorized roles. This method clears the current - * list and adds all entries in the parameter list. - * - * @param authorizedRoles A list of authorized roles. - */ - public void setAuthorizedRoles(List authorizedRoles) { - synchronized (getAuthorizedRoles()) { - if (authorizedRoles != getAuthorizedRoles()) { - getAuthorizedRoles().clear(); - - if (authorizedRoles != null) { - getAuthorizedRoles().addAll(authorizedRoles); - } - } - } - } - - /** - * Sets the modifiable list of forbidden roles. This method clears the current - * list and adds all entries in the parameter list. - * - * @param forbiddenRoles A list of forbidden roles. - */ - public void setForbiddenRoles(List forbiddenRoles) { - synchronized (getForbiddenRoles()) { - if (forbiddenRoles != getForbiddenRoles()) { - getForbiddenRoles().clear(); - - if (forbiddenRoles != null) { - getForbiddenRoles().addAll(forbiddenRoles); - } - } - } - } - + /** The modifiable list of authorized roles. */ + private List authorizedRoles; + + /** The modifiable list of forbidden roles. */ + private List forbiddenRoles; + + /** Default constructor. */ + public RoleAuthorizer() { + this(null); + } + + /** + * Constructor. + * + * @param identifier The identifier unique within an application. + */ + public RoleAuthorizer(String identifier) { + super(identifier); + + this.authorizedRoles = new CopyOnWriteArrayList<>(); + this.forbiddenRoles = new CopyOnWriteArrayList<>(); + } + + /** + * Authorizes the request only if its subject is in one of the authorized roles and in none of + * the forbidden ones. + * + * @param request The request sent. + * @param response The response to update. + * @return True if the authorization succeeded. + */ + @Override + public boolean authorize(Request request, Response response) { + boolean authorized = false; + boolean forbidden = false; + + // Verify if the subject is in one of the authorized roles + if (getAuthorizedRoles().isEmpty()) { + authorized = true; + } else { + for (Role authorizedRole : getAuthorizedRoles()) { + authorized = + authorized || request.getClientInfo().getRoles().contains(authorizedRole); + } + } + + // Verify if the subject is in one of the forbidden roles + for (Role forbiddenRole : getForbiddenRoles()) { + forbidden = forbidden || request.getClientInfo().getRoles().contains(forbiddenRole); + } + + return authorized && !forbidden; + } + + /** + * Returns the modifiable list of authorized roles. + * + * @return The modifiable list of authorized roles. + */ + public List getAuthorizedRoles() { + return authorizedRoles; + } + + /** + * Returns the modifiable list of forbidden roles. + * + * @return The modifiable list of forbidden roles. + */ + public List getForbiddenRoles() { + return forbiddenRoles; + } + + /** + * Sets the modifiable list of authorized roles. This method clears the current list and adds + * all entries in the parameter list. + * + * @param authorizedRoles A list of authorized roles. + */ + public void setAuthorizedRoles(List authorizedRoles) { + synchronized (getAuthorizedRoles()) { + if (authorizedRoles != getAuthorizedRoles()) { + getAuthorizedRoles().clear(); + + if (authorizedRoles != null) { + getAuthorizedRoles().addAll(authorizedRoles); + } + } + } + } + + /** + * Sets the modifiable list of forbidden roles. This method clears the current list and adds all + * entries in the parameter list. + * + * @param forbiddenRoles A list of forbidden roles. + */ + public void setForbiddenRoles(List forbiddenRoles) { + synchronized (getForbiddenRoles()) { + if (forbiddenRoles != getForbiddenRoles()) { + getForbiddenRoles().clear(); + + if (forbiddenRoles != null) { + getForbiddenRoles().addAll(forbiddenRoles); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java b/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java index 969f155fc7..b49cd06271 100644 --- a/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java +++ b/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import org.restlet.Request; @@ -15,112 +14,108 @@ import org.restlet.data.ClientInfo; /** - * Verifier of identifier/secret couples. By default, it extracts the identifier - * and the secret from the {@link ChallengeResponse}. If the verification is - * successful, it automatically adds a new {@link User} for the given - * identifier. - * + * Verifier of identifier/secret couples. By default, it extracts the identifier and the secret from + * the {@link ChallengeResponse}. If the verification is successful, it automatically adds a new + * {@link User} for the given identifier. + * * @author Jerome Louvel */ public abstract class SecretVerifier implements Verifier { - /** - * Compares that two secrets are equal and not null. - * - * @param secret1 The input secret. - * @param secret2 The output secret. - * @return True if both are equal. - */ - public static boolean compare(char[] secret1, char[] secret2) { - boolean result = false; - - if ((secret1 != null) && (secret2 != null)) { - // None is null - if (secret1.length == secret2.length) { - boolean equals = true; - - for (int i = 0; (i < secret1.length) && equals; i++) { - equals = (secret1[i] == secret2[i]); - } - - result = equals; - } - } - - return result; - } - - /** - * Called back to create a new user when valid credentials are provided. - * - * @param identifier The user identifier. - * @param request The request handled. - * @param response The response handled. - * @return The {@link User} instance created. - */ - protected User createUser(String identifier, Request request, Response response) { - return new User(identifier); - } - - /** - * Returns the user identifier. - * - * @param request The request to inspect. - * @param response The response to inspect. - * @return The user identifier. - */ - protected String getIdentifier(Request request, Response response) { - return request.getChallengeResponse().getIdentifier(); - } - - /** - * Returns the secret provided by the user. - * - * @param request The request to inspect. - * @param response The response to inspect. - * @return The secret provided by the user. - */ - protected char[] getSecret(Request request, Response response) { - return request.getChallengeResponse().getSecret(); - } - - /** - * Verifies that the proposed secret is correct for the specified request. By - * default, it compares the inputSecret of the request's authentication response - * with the one obtain by the {@link ChallengeResponse#getSecret()} method and - * sets the {@link org.restlet.security.User} instance of the request's - * {@link ClientInfo} if successful. - * - * @param request The request to inspect. - * @param response The response to inspect. - * @return Result of the verification based on the RESULT_* constants. - */ - public int verify(Request request, Response response) { - int result = RESULT_VALID; - - if (request.getChallengeResponse() == null) { - result = RESULT_MISSING; - } else { - String identifier = getIdentifier(request, response); - char[] secret = getSecret(request, response); - result = verify(identifier, secret); - - if (result == RESULT_VALID) { - request.getClientInfo().setUser(createUser(identifier, request, response)); - } - } - - return result; - } - - /** - * Verifies that the identifier/secret couple is valid. It throws an - * IllegalArgumentException in case the identifier is either null or does not - * identify a user. - * - * @param identifier The user identifier to match. - * @param secret The provided secret to verify. - * @return Result of the verification based on the RESULT_* constants. - */ - public abstract int verify(String identifier, char[] secret); - + /** + * Compares that two secrets are equal and not null. + * + * @param secret1 The input secret. + * @param secret2 The output secret. + * @return True if both are equal. + */ + public static boolean compare(char[] secret1, char[] secret2) { + boolean result = false; + + if ((secret1 != null) && (secret2 != null)) { + // None is null + if (secret1.length == secret2.length) { + boolean equals = true; + + for (int i = 0; (i < secret1.length) && equals; i++) { + equals = (secret1[i] == secret2[i]); + } + + result = equals; + } + } + + return result; + } + + /** + * Called back to create a new user when valid credentials are provided. + * + * @param identifier The user identifier. + * @param request The request handled. + * @param response The response handled. + * @return The {@link User} instance created. + */ + protected User createUser(String identifier, Request request, Response response) { + return new User(identifier); + } + + /** + * Returns the user identifier. + * + * @param request The request to inspect. + * @param response The response to inspect. + * @return The user identifier. + */ + protected String getIdentifier(Request request, Response response) { + return request.getChallengeResponse().getIdentifier(); + } + + /** + * Returns the secret provided by the user. + * + * @param request The request to inspect. + * @param response The response to inspect. + * @return The secret provided by the user. + */ + protected char[] getSecret(Request request, Response response) { + return request.getChallengeResponse().getSecret(); + } + + /** + * Verifies that the proposed secret is correct for the specified request. By default, it + * compares the inputSecret of the request's authentication response with the one obtain by the + * {@link ChallengeResponse#getSecret()} method and sets the {@link org.restlet.security.User} + * instance of the request's {@link ClientInfo} if successful. + * + * @param request The request to inspect. + * @param response The response to inspect. + * @return Result of the verification based on the RESULT_* constants. + */ + public int verify(Request request, Response response) { + int result = RESULT_VALID; + + if (request.getChallengeResponse() == null) { + result = RESULT_MISSING; + } else { + String identifier = getIdentifier(request, response); + char[] secret = getSecret(request, response); + result = verify(identifier, secret); + + if (result == RESULT_VALID) { + request.getClientInfo().setUser(createUser(identifier, request, response)); + } + } + + return result; + } + + /** + * Verifies that the identifier/secret couple is valid. It throws an IllegalArgumentException in + * case the identifier is either null or does not identify a user. + * + * @param identifier The user identifier to match. + * @param secret The provided secret to verify. + * @return Result of the verification based on the RESULT_* constants. + */ + public abstract int verify(String identifier, char[] secret); } diff --git a/org.restlet/src/main/java/org/restlet/security/User.java b/org.restlet/src/main/java/org/restlet/security/User.java index c6b6f9b0fb..f63d572af1 100644 --- a/org.restlet/src/main/java/org/restlet/security/User.java +++ b/org.restlet/src/main/java/org/restlet/security/User.java @@ -1,208 +1,204 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import java.security.Principal; /** - * User part of a security realm. Note the same user can be member of several - * groups. - * + * User part of a security realm. Note the same user can be a member of several groups. + * * @see Realm * @see Group * @author Jerome Louvel */ public class User implements Principal { - /** The email. */ - private volatile String email; - - /** The first name. */ - private volatile String firstName; - - /** The identifier. */ - private volatile String identifier; - - /** The last name. */ - private volatile String lastName; - - /** The secret. */ - private volatile char[] secret; - - /** - * Default constructor. - */ - public User() { - this(null, (char[]) null, null, null, null); - } - - /** - * Constructor. - * - * @param identifier The identifier (login). - */ - public User(String identifier) { - this(identifier, (char[]) null, null, null, null); - } - - /** - * Constructor. - * - * @param identifier The identifier (login). - * @param secret The identification secret. - */ - public User(String identifier, char[] secret) { - this(identifier, secret, null, null, null); - } - - /** - * Constructor. - * - * @param identifier The identifier (login). - * @param secret The identification secret. - * @param firstName The first name. - * @param lastName The last name. - * @param email The email. - */ - public User(String identifier, char[] secret, String firstName, String lastName, String email) { - this.identifier = identifier; - this.secret = secret; - this.firstName = firstName; - this.lastName = lastName; - this.email = email; - } - - /** - * Constructor. - * - * @param identifier The identifier (login). - * @param secret The identification secret. - */ - public User(String identifier, String secret) { - this(identifier, secret.toCharArray()); - } - - /** - * Constructor. - * - * @param identifier The identifier (login). - * @param secret The identification secret. - * @param firstName The first name. - * @param lastName The last name. - * @param email The email. - */ - public User(String identifier, String secret, String firstName, String lastName, String email) { - this(identifier, secret.toCharArray(), firstName, lastName, email); - } - - /** - * Returns the email. - * - * @return The email. - */ - public String getEmail() { - return email; - } - - /** - * Returns the first name. - * - * @return The first name. - */ - public String getFirstName() { - return firstName; - } - - /** - * Returns the identifier. - * - * @return The identifier. - */ - public String getIdentifier() { - return identifier; - } - - /** - * Returns the last name. - * - * @return The last name. - */ - public String getLastName() { - return lastName; - } - - /** - * Returns the user identifier. - * - * @see #getIdentifier() - */ - public String getName() { - return getIdentifier(); - } - - /** - * Returns the secret. - * - * @return The secret. - */ - public char[] getSecret() { - return secret; - } - - /** - * Sets the email. - * - * @param email The email. - */ - public void setEmail(String email) { - this.email = email; - } - - /** - * Sets the first name. - * - * @param firstName The first name. - */ - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - /** - * Sets the identifier. - * - * @param identifier The identifier. - */ - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - /** - * Sets the last name. - * - * @param lastName The last name. - */ - public void setLastName(String lastName) { - this.lastName = lastName; - } - - /** - * Sets the secret. - * - * @param secret The secret. - */ - public void setSecret(char[] secret) { - this.secret = secret; - } - - @Override - public String toString() { - return getIdentifier(); - } + /** The email. */ + private volatile String email; + + /** The first name. */ + private volatile String firstName; + + /** The identifier. */ + private volatile String identifier; + + /** The last name. */ + private volatile String lastName; + + /** The secret. */ + private volatile char[] secret; + + /** Default constructor. */ + public User() { + this(null, (char[]) null, null, null, null); + } + + /** + * Constructor. + * + * @param identifier The identifier (login). + */ + public User(String identifier) { + this(identifier, (char[]) null, null, null, null); + } + + /** + * Constructor. + * + * @param identifier The identifier (login). + * @param secret The identification secret. + */ + public User(String identifier, char[] secret) { + this(identifier, secret, null, null, null); + } + + /** + * Constructor. + * + * @param identifier The identifier (login). + * @param secret The identification secret. + * @param firstName The first name. + * @param lastName The last name. + * @param email The email. + */ + public User(String identifier, char[] secret, String firstName, String lastName, String email) { + this.identifier = identifier; + this.secret = secret; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + } + + /** + * Constructor. + * + * @param identifier The identifier (login). + * @param secret The identification secret. + */ + public User(String identifier, String secret) { + this(identifier, secret.toCharArray()); + } + + /** + * Constructor. + * + * @param identifier The identifier (login). + * @param secret The identification secret. + * @param firstName The first name. + * @param lastName The last name. + * @param email The email. + */ + public User(String identifier, String secret, String firstName, String lastName, String email) { + this(identifier, secret.toCharArray(), firstName, lastName, email); + } + + /** + * Returns the email. + * + * @return The email. + */ + public String getEmail() { + return email; + } + + /** + * Returns the first name. + * + * @return The first name. + */ + public String getFirstName() { + return firstName; + } + + /** + * Returns the identifier. + * + * @return The identifier. + */ + public String getIdentifier() { + return identifier; + } + + /** + * Returns the last name. + * + * @return The last name. + */ + public String getLastName() { + return lastName; + } + + /** + * Returns the user identifier. + * + * @see #getIdentifier() + */ + public String getName() { + return getIdentifier(); + } + + /** + * Returns the secret. + * + * @return The secret. + */ + public char[] getSecret() { + return secret; + } + + /** + * Sets the email. + * + * @param email The email. + */ + public void setEmail(String email) { + this.email = email; + } + + /** + * Sets the first name. + * + * @param firstName The first name. + */ + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + /** + * Sets the identifier. + * + * @param identifier The identifier. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Sets the last name. + * + * @param lastName The last name. + */ + public void setLastName(String lastName) { + this.lastName = lastName; + } + + /** + * Sets the secret. + * + * @param secret The secret. + */ + public void setSecret(char[] secret) { + this.secret = secret; + } + + @Override + public String toString() { + return getIdentifier(); + } } diff --git a/org.restlet/src/main/java/org/restlet/security/Verifier.java b/org.restlet/src/main/java/org/restlet/security/Verifier.java index c503b50b20..4ae3f13488 100644 --- a/org.restlet/src/main/java/org/restlet/security/Verifier.java +++ b/org.restlet/src/main/java/org/restlet/security/Verifier.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import org.restlet.Request; @@ -14,37 +13,35 @@ /** * Verifies the credentials provided by a client user sending a request. - * + * * @author Jerome Louvel */ public interface Verifier { - /** Invalid credentials provided. */ - public final static int RESULT_INVALID = -1; - - /** No credentials provided. */ - public final static int RESULT_MISSING = 0; + /** Invalid credentials provided. */ + int RESULT_INVALID = -1; - /** Stale credentials provided. */ - public final static int RESULT_STALE = 1; + /** No credentials provided. */ + int RESULT_MISSING = 0; - /** Unsupported credentials. */ - public final static int RESULT_UNSUPPORTED = 3; + /** Stale credentials provided. */ + int RESULT_STALE = 1; - /** Unknown user. */ - public final static int RESULT_UNKNOWN = 5; + /** Unsupported credentials. */ + int RESULT_UNSUPPORTED = 3; - /** Valid credentials provided. */ - public final static int RESULT_VALID = 4; + /** Unknown user. */ + int RESULT_UNKNOWN = 5; - /** - * Attempts to verify the credentials provided by the client user sending the - * request. - * - * @param request The request sent. - * @param response The response to update. - * @return Result of the verification based on the RESULT_* constants. - */ - int verify(Request request, Response response); + /** Valid credentials provided. */ + int RESULT_VALID = 4; + /** + * Attempts to verify the credentials provided by the client user sending the request. + * + * @param request The request sent. + * @param response The response to update. + * @return Result of the verification based on the RESULT_* constants. + */ + int verify(Request request, Response response); } diff --git a/org.restlet/src/main/java/org/restlet/service/ConnectorService.java b/org.restlet/src/main/java/org/restlet/service/ConnectorService.java index 58c6652e95..0ecfea6302 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConnectorService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConnectorService.java @@ -1,130 +1,119 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Application; import org.restlet.data.Protocol; import org.restlet.representation.Representation; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - /** - * Application service declaring client and server connectors. This is useful at - * deployment time to know which connectors an application expects to be able to - * use.
+ * Application service declaring client and server connectors. This is useful at deployment time to + * know which connectors an application expects to be able to use.
*
- * If you need to override the {@link #afterSend(Representation)} method for - * example, just create a subclass and set it on your application with the - * {@link Application#setConnectorService(ConnectorService)} method.
+ * If you need to override the {@link #afterSend(Representation)} method for example, just create a + * subclass and set it on your application with the {@link + * Application#setConnectorService(ConnectorService)} method.
*
- * Implementation note: the parent component will ensure that client connectors - * won't automatically follow redirections. This will ensure a consistent - * behavior and portability of applications. - * + * Implementation note: the parent component will ensure that client connectors won't automatically + * follow redirections. This will ensure a consistent behavior and portability of applications. + * * @author Jerome Louvel */ public class ConnectorService extends Service { - /** The list of required client protocols. */ - private final List clientProtocols; - - /** The list of required server protocols. */ - private final List serverProtocols; + /** The list of required client protocols. */ + private final List clientProtocols; - /** - * Constructor. - */ - public ConnectorService() { - this.clientProtocols = new CopyOnWriteArrayList<>(); - this.serverProtocols = new CopyOnWriteArrayList<>(); - } + /** The list of required server protocols. */ + private final List serverProtocols; - /** - * Call-back method invoked by the client or server connectors just after - * sending the response to the target component. The default implementation does - * nothing. - * - * @param entity The optional entity about to be committed. - */ - public void afterSend(Representation entity) { - // Do nothing by default. - } + /** Constructor. */ + public ConnectorService() { + this.clientProtocols = new CopyOnWriteArrayList<>(); + this.serverProtocols = new CopyOnWriteArrayList<>(); + } - /** - * Call-back method invoked by the client or server connectors just before - * sending the response to the target component. The default implementation does - * nothing. - * - * @param entity The optional entity about to be committed. - */ - public void beforeSend(Representation entity) { - // Do nothing by default. - } + /** + * Call-back method invoked by the client or server connectors just after sending the response + * to the target component. The default implementation does nothing. + * + * @param entity The optional entity about to be committed. + */ + public void afterSend(Representation entity) { + // Do nothing by default. + } - /** - * Returns the modifiable list of required client protocols. You need to update - * this list if you need the parent component to provide additional client - * connectors. - * - * @return The list of required client protocols. - */ - public List getClientProtocols() { - return this.clientProtocols; - } + /** + * Call-back method invoked by the client or server connectors just before sending the response + * to the target component. The default implementation does nothing. + * + * @param entity The optional entity about to be committed. + */ + public void beforeSend(Representation entity) { + // Do nothing by default. + } - /** - * Returns the modifiable list of required server protocols. An empty list means - * that all protocols are potentially supported (default case). You should - * update this list to restrict the actual protocols supported by your - * application. - * - * @return The list of required server protocols. - */ - public List getServerProtocols() { - return this.serverProtocols; - } + /** + * Returns the modifiable list of required client protocols. You need to update this list if you + * need the parent component to provide additional client connectors. + * + * @return The list of required client protocols. + */ + public List getClientProtocols() { + return this.clientProtocols; + } - /** - * Sets the modifiable list of required client protocols. This method clears the - * current list and adds all entries in the parameter list. - * - * @param clientProtocols A list of required client protocols. - */ - public void setClientProtocols(List clientProtocols) { - synchronized (getClientProtocols()) { - if (clientProtocols != getClientProtocols()) { - getClientProtocols().clear(); + /** + * Returns the modifiable list of required server protocols. An empty list means that all + * protocols are potentially supported (default case). You should update this list to restrict + * the actual protocols supported by your application. + * + * @return The list of required server protocols. + */ + public List getServerProtocols() { + return this.serverProtocols; + } - if (clientProtocols != null) { - getClientProtocols().addAll(clientProtocols); - } - } - } - } + /** + * Sets the modifiable list of required client protocols. This method clears the current list + * and adds all entries in the parameter list. + * + * @param clientProtocols A list of required client protocols. + */ + public void setClientProtocols(List clientProtocols) { + synchronized (getClientProtocols()) { + if (clientProtocols != getClientProtocols()) { + getClientProtocols().clear(); - /** - * Sets the modifiable list of required server protocols. This method clears the - * current list and adds all entries in the parameter list. - * - * @param serverProtocols A list of required server protocols. - */ - public void setServerProtocols(List serverProtocols) { - synchronized (getServerProtocols()) { - if (serverProtocols != getServerProtocols()) { - getServerProtocols().clear(); + if (clientProtocols != null) { + getClientProtocols().addAll(clientProtocols); + } + } + } + } - if (serverProtocols != null) { - getServerProtocols().addAll(serverProtocols); - } - } - } - } + /** + * Sets the modifiable list of required server protocols. This method clears the current list + * and adds all entries in the parameter list. + * + * @param serverProtocols A list of required server protocols. + */ + public void setServerProtocols(List serverProtocols) { + synchronized (getServerProtocols()) { + if (serverProtocols != getServerProtocols()) { + getServerProtocols().clear(); + if (serverProtocols != null) { + getServerProtocols().addAll(serverProtocols); + } + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/service/ConnegService.java b/org.restlet/src/main/java/org/restlet/service/ConnegService.java index 0918778082..96eb86b25d 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConnegService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConnegService.java @@ -1,96 +1,87 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.util.List; import org.restlet.Request; import org.restlet.engine.application.Conneg; import org.restlet.engine.application.FlexibleConneg; import org.restlet.engine.application.StrictConneg; import org.restlet.representation.Variant; -import java.util.List; - /** - * Application service negotiating the preferred resource variants. This service - * is leveraged by server-side and client-side content negotiation, annotated - * method dispatching, and so on. - * + * Application service negotiating the preferred resource variants. This service is leveraged by + * server-side and client-side content negotiation, annotated method dispatching, and so on. + * * @author Jerome Louvel */ public class ConnegService extends Service { - /** - * Indicates if the conneg algorithm should strictly respect client preferences - * or be more flexible. - */ - private volatile boolean strict; - - /** - * Constructor. - */ - public ConnegService() { - this(true); - } + /** + * Indicates if the conneg algorithm should strictly respect client preferences or be more + * flexible. + */ + private volatile boolean strict; - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - */ - public ConnegService(boolean enabled) { - super(enabled); - this.strict = false; - } + /** Constructor. */ + public ConnegService() { + this(true); + } - /** - * Returns the best variant representation for a given resource according the - * the client preferences.
- * A default language is provided in case the variants don't match the client - * preferences. - * - * @param variants The list of variants to compare. - * @param request The request including client preferences. - * @param metadataService The metadata service used to get default metadata - * values. - * @return The preferred variant. - * @see Apache - * content negotiation algorithm - */ - public Variant getPreferredVariant(List variants, Request request, - MetadataService metadataService) { - Conneg conneg = isStrict() ? new StrictConneg(request, metadataService) - : new FlexibleConneg(request, metadataService); - return conneg.getPreferredVariant(variants); - } + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + */ + public ConnegService(boolean enabled) { + super(enabled); + this.strict = false; + } - /** - * Indicates if the conneg algorithm should strictly respect client preferences - * or be more flexible. Value is false by default. - * - * @return True if the conneg algorithm should strictly respect client - * preferences. - */ - public boolean isStrict() { - return strict; - } + /** + * Returns the best variant representation for a given resource according the the client + * preferences.
+ * A default language is provided in case the variants don't match the client preferences. + * + * @param variants The list of variants to compare. + * @param request The request including client preferences. + * @param metadataService The metadata service used to get default metadata values. + * @return The preferred variant. + * @see Apache + * content negotiation algorithm + */ + public Variant getPreferredVariant( + List variants, Request request, MetadataService metadataService) { + Conneg conneg = + isStrict() + ? new StrictConneg(request, metadataService) + : new FlexibleConneg(request, metadataService); + return conneg.getPreferredVariant(variants); + } - /** - * Indicates if the conneg algorithm should strictly respect client preferences - * or be more flexible. - * - * @param strict True if the conneg algorithm should strictly respect client - * preferences. - */ - public void setStrict(boolean strict) { - this.strict = strict; - } + /** + * Indicates if the conneg algorithm should strictly respect client preferences or be more + * flexible. Value is false by default. + * + * @return True if the conneg algorithm should strictly respect client preferences. + */ + public boolean isStrict() { + return strict; + } + /** + * Indicates if the conneg algorithm should strictly respect client preferences or be more + * flexible. + * + * @param strict True if the conneg algorithm should strictly respect client preferences. + */ + public void setStrict(boolean strict) { + this.strict = strict; + } } diff --git a/org.restlet/src/main/java/org/restlet/service/ConverterService.java b/org.restlet/src/main/java/org/restlet/service/ConverterService.java index 4490a30a83..cc3fec5700 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConverterService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConverterService.java @@ -1,14 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.data.MediaType; import org.restlet.data.Preference; @@ -20,309 +23,318 @@ import org.restlet.representation.Variant; import org.restlet.resource.Resource; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - /** - * Application service converting between representation and regular Java - * objects. The conversion can work in both directions. Actual converters can be - * plugged into the engine to support this service.
+ * Application service converting between representation and regular Java objects. The conversion + * can work in both directions. Actual converters can be plugged into the engine to support this + * service.
*
- * Root object classes used for conversion shouldn't be generic classes - * otherwise important contextual type information will be missing at runtime - * due to Java type erasure mechanism. If needed, create a fully resolved - * subclasses and/or a container classes. - * + * Root object classes used for conversion shouldn't be generic classes otherwise important + * contextual type information will be missing at runtime due to Java type erasure mechanism. If + * needed, create a fully resolved subclasses and/or a container classes. + * * @author Jerome Louvel */ public class ConverterService extends Service { - /** - * Constructor. - */ - public ConverterService() { - super(); - } - - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - */ - public ConverterService(boolean enabled) { - super(enabled); - } - - /** - * Applies a patch representation to an initial representation in order to - * obtain a modified one. The patch must have a recognized media type in order - * for the {@link ConverterService} to be able to process it. - * - * @param initial The initial representation on which the patch must be applied. - * @param patch The patch representation to apply. - * @return The modified representation. - * @throws IOException - */ - public Representation applyPatch(Representation initial, Representation patch) throws IOException { - - return null; - } - - /** - * Creates a patch representation by calculating a diff between initial and - * modified representations. - * - * @param initial The initial representation. - * @param modified The modified representation. - * @return The patch representation able to convert the initial representation - * into the modified representation. - * @throws IOException - */ - public Representation createPatch(Representation initial, Representation modified) throws IOException { - - return null; - } - - /** - * Returns the list of object classes that can be converted from a given - * variant. - * - * @param source The source variant. - * @return The list of object class that can be converted. - */ - public List> getObjectClasses(Variant source) { - List> result = null; - List> helperObjectClasses = null; - - for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { - helperObjectClasses = ch.getObjectClasses(source); - - if (helperObjectClasses != null) { - if (result == null) { - result = new ArrayList>(); - } - - result.addAll(helperObjectClasses); - } - } - - return result; - } - - /** - * Returns the list of patch media types available for the given representation - * types. - * - * @param representationType The representation media type or null for all - * supported patch types. - * @return The list of patch media types available. - */ - public List getPatchTypes(MediaType representationType) { - return null; - } - - /** - * Returns the list of variants that can be converted from a given object class. - * - * @param source The source class. - * @param target The expected representation metadata. - * @return The list of variants that can be converted. - * @throws IOException - */ - public List getVariants(Class source, Variant target) throws IOException { - return ConverterUtils.getVariants(source, target); - } - - /** - * Reverts a patch representation from a modified representation in order to - * obtain the initial one. The patch must have a recognized media type in order - * for the {@link ConverterService} to be able to process it. - * - * @param modified The modified representation from which the patch must be - * reverted. - * @param patch The patch representation to revert. - * @return The initial representation. - * @throws IOException - */ - public Representation revertPatch(Representation modified, Representation patch) throws IOException { - - return null; - } - - /** - * Converts a Representation into a regular Java object. - * - * @param source The source representation to convert. - * @return The converted Java object. - * @throws IOException - */ - public Object toObject(Representation source) throws IOException { - return toObject(source, null, null); - } - - /** - * Converts a Representation into a regular Java object. - * - * @param The expected class of the Java object. - * @param source The source representation to convert. - * @param target The target class of the Java object. - * @param resource The parent resource. - * @return The converted Java object. - * @throws IOException - */ - public T toObject(Representation source, Class target, Resource resource) throws IOException { - T result = null; - boolean loggable = resource == null || resource.isLoggable(); - - if ((source != null) && source.isAvailable() && (source.getSize() != 0)) { - ConverterHelper ch = ConverterUtils.getBestHelper(source, target, resource); - - if (ch != null) { - if (loggable && Context.getCurrentLogger().isLoggable(Level.FINE)) { - Context.getCurrentLogger() - .fine("The following converter was selected for the " + source + " representation: " + ch); - } - - result = ch.toObject(source, target, resource); - - if (result instanceof Representation resultRepresentation) { - - // Copy the variant metadata - resultRepresentation.setCharacterSet(source.getCharacterSet()); - resultRepresentation.setMediaType(source.getMediaType()); - resultRepresentation.getEncodings().addAll(source.getEncodings()); - resultRepresentation.getLanguages().addAll(source.getLanguages()); - } - } else { - if (loggable) { - Context.getCurrentLogger() - .warning("Unable to find a converter for this representation : " + source); - } - } - } - - return result; - } - - /** - * Converts a regular Java object into a Representation. The converter will use - * the preferred variant of the selected converter. - * - * @param source The source object to convert. - * @return The converted representation. - * @throws IOException - */ - public Representation toRepresentation(Object source) throws IOException { - return toRepresentation(source, null, null); - } - - /** - * Converts a regular Java object into a Representation. - * - * @param source The source object to convert. - * @param target The target representation media type. - * @return The converted representation. - * @throws IOException - */ - public Representation toRepresentation(Object source, MediaType target) throws IOException { - return toRepresentation(source, new Variant(target)); - } - - /** - * Converts a regular Java object into a Representation. - * - * @param source The source object to convert. - * @param target The target representation variant. - * @return The converted representation. - * @throws IOException - */ - public Representation toRepresentation(Object source, Variant target) throws IOException { - return toRepresentation(source, target, null); - } - - /** - * Converts a regular Java object into a Representation. - * - * @param source The source object to convert. - * @param target The target representation variant. - * @param resource The parent resource. - * @return The converted representation. - * @throws IOException - */ - public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { - Representation result = null; - boolean loggable = (resource == null) || resource.isLoggable(); - ConverterHelper ch = ConverterUtils.getBestHelper(source, target, resource); - - if (ch != null) { - if (loggable && Context.getCurrentLogger().isLoggable(Level.FINE)) { - Context.getCurrentLogger().fine("Converter selected for " + source.getClass().getSimpleName() + ": " - + ch.getClass().getSimpleName()); - } - - if (target == null) { - List variants = ch.getVariants(source.getClass()); - - if ((variants != null) && !variants.isEmpty()) { - if (resource != null) { - target = resource.getConnegService().getPreferredVariant(variants, resource.getRequest(), - resource.getMetadataService()); - } else { - target = variants.get(0); - } - } - } - if (target == null) { - target = new Variant(); - } - - result = ch.toRepresentation(source, target, resource); - - if (result != null) { - // Copy the variant metadata if necessary - if (result.getCharacterSet() == null) { - result.setCharacterSet(target.getCharacterSet()); - } - - if ((result.getMediaType() == null) || !result.getMediaType().isConcrete()) { - if ((target.getMediaType() != null) && target.getMediaType().isConcrete()) { - result.setMediaType(target.getMediaType()); - } else if (resource != null) { - result.setMediaType(resource.getMetadataService().getDefaultMediaType()); - } else { - result.setMediaType(MediaType.APPLICATION_OCTET_STREAM); - } - } - - if (result.getEncodings().isEmpty()) { - result.getEncodings().addAll(target.getEncodings()); - } - - if (result.getLanguages().isEmpty()) { - result.getLanguages().addAll(target.getLanguages()); - } - } - } else { - if (loggable) { - Context.getCurrentLogger().warning("Unable to find a converter for this object : " + source); - } - } - - return result; - } - - /** - * Updates the media type preferences with available conversion capabilities for - * the given entity class. - * - * @param preferences The media type preferences. - * @param entity The entity class to convert. - */ - public void updatePreferences(List> preferences, Class entity) { - for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { - ch.updatePreferences(preferences, entity); - } - } + /** Constructor. */ + public ConverterService() { + super(); + } + + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + */ + public ConverterService(boolean enabled) { + super(enabled); + } + + /** + * Applies a patch representation to an initial representation to obtain a modified one. The + * patch must have a recognized media type in order for the {@link ConverterService} to be able + * to process it. + * + * @param initial The initial representation on which the patch must be applied. + * @param patch The patch representation to apply. + * @return The modified representation. + * @throws IOException + */ + public Representation applyPatch(Representation initial, Representation patch) + throws IOException { + + return null; + } + + /** + * Creates a patch representation by calculating a diff between initial and modified + * representations. + * + * @param initial The initial representation. + * @param modified The modified representation. + * @return The patch representation able to convert the initial representation into the modified + * representation. + * @throws IOException + */ + public Representation createPatch(Representation initial, Representation modified) + throws IOException { + + return null; + } + + /** + * Returns the list of object classes that can be converted from a given variant. + * + * @param source The source variant. + * @return The list of object class that can be converted. + */ + public List> getObjectClasses(Variant source) { + List> result = null; + List> helperObjectClasses = null; + + for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { + helperObjectClasses = ch.getObjectClasses(source); + + if (helperObjectClasses != null) { + if (result == null) { + result = new ArrayList<>(); + } + + result.addAll(helperObjectClasses); + } + } + + return result; + } + + /** + * Returns the list of patch media types available for the given representation types. + * + * @param representationType The representation media type or null for all supported patch + * types. + * @return The list of patch media types available. + */ + public List getPatchTypes(MediaType representationType) { + return null; + } + + /** + * Returns the list of variants that can be converted from a given object class. + * + * @param source The source class. + * @param target The expected representation metadata. + * @return The list of variants that can be converted. + * @throws IOException + */ + public List getVariants(Class source, Variant target) throws IOException { + return ConverterUtils.getVariants(source, target); + } + + /** + * Reverts a patch representation from a modified representation to get the initial one. The + * patch must have a recognized media type in order for the {@link ConverterService} to be able + * to process it. + * + * @param modified The modified representation from which the patch must be reverted. + * @param patch The patch representation to revert. + * @return The initial representation. + * @throws IOException + */ + public Representation revertPatch(Representation modified, Representation patch) + throws IOException { + + return null; + } + + /** + * Converts a Representation into a regular Java object. + * + * @param source The source representation to convert. + * @return The converted Java object. + * @throws IOException + */ + public Object toObject(Representation source) throws IOException { + return toObject(source, null, null); + } + + /** + * Converts a Representation into a regular Java object. + * + * @param The expected class of the Java object. + * @param source The source representation to convert. + * @param target The target class of the Java object. + * @param resource The parent resource. + * @return The converted Java object. + * @throws IOException + */ + public T toObject(Representation source, Class target, Resource resource) + throws IOException { + T result = null; + boolean loggable = resource == null || resource.isLoggable(); + + if ((source != null) && source.isAvailable() && (source.getSize() != 0)) { + ConverterHelper ch = ConverterUtils.getBestHelper(source, target, resource); + + if (ch != null) { + if (loggable && Context.getCurrentLogger().isLoggable(Level.FINE)) { + Context.getCurrentLogger() + .fine( + "The following converter was selected for the " + + source + + " representation: " + + ch); + } + + result = ch.toObject(source, target, resource); + + if (result instanceof Representation resultRepresentation) { + + // Copy the variant metadata + resultRepresentation.setCharacterSet(source.getCharacterSet()); + resultRepresentation.setMediaType(source.getMediaType()); + resultRepresentation.getEncodings().addAll(source.getEncodings()); + resultRepresentation.getLanguages().addAll(source.getLanguages()); + } + } else { + if (loggable) { + Context.getCurrentLogger() + .warning( + "Unable to find a converter for this representation : " + + source); + } + } + } + + return result; + } + + /** + * Converts a regular Java object into a Representation. The converter will use the preferred + * variant of the selected converter. + * + * @param source The source object to convert. + * @return The converted representation. + * @throws IOException + */ + public Representation toRepresentation(Object source) throws IOException { + return toRepresentation(source, null, null); + } + + /** + * Converts a regular Java object into a Representation. + * + * @param source The source object to convert. + * @param target The target representation media type. + * @return The converted representation. + * @throws IOException + */ + public Representation toRepresentation(Object source, MediaType target) throws IOException { + return toRepresentation(source, new Variant(target)); + } + + /** + * Converts a regular Java object into a Representation. + * + * @param source The source object to convert. + * @param target The target representation variant. + * @return The converted representation. + * @throws IOException + */ + public Representation toRepresentation(Object source, Variant target) throws IOException { + return toRepresentation(source, target, null); + } + + /** + * Converts a regular Java object into a Representation. + * + * @param source The source object to convert. + * @param target The target representation variant. + * @param resource The parent resource. + * @return The converted representation. + * @throws IOException + */ + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { + Representation result = null; + boolean loggable = (resource == null) || resource.isLoggable(); + ConverterHelper ch = ConverterUtils.getBestHelper(source, target, resource); + + if (ch != null) { + if (loggable && Context.getCurrentLogger().isLoggable(Level.FINE)) { + Context.getCurrentLogger() + .fine( + "Converter selected for " + + source.getClass().getSimpleName() + + ": " + + ch.getClass().getSimpleName()); + } + + if (target == null) { + List variants = ch.getVariants(source.getClass()); + + if ((variants != null) && !variants.isEmpty()) { + if (resource != null) { + target = + resource.getConnegService() + .getPreferredVariant( + variants, + resource.getRequest(), + resource.getMetadataService()); + } else { + target = variants.get(0); + } + } + } + if (target == null) { + target = new Variant(); + } + + result = ch.toRepresentation(source, target, resource); + + if (result != null) { + // Copy the variant metadata if necessary + if (result.getCharacterSet() == null) { + result.setCharacterSet(target.getCharacterSet()); + } + + if ((result.getMediaType() == null) || !result.getMediaType().isConcrete()) { + if ((target.getMediaType() != null) && target.getMediaType().isConcrete()) { + result.setMediaType(target.getMediaType()); + } else if (resource != null) { + result.setMediaType(resource.getMetadataService().getDefaultMediaType()); + } else { + result.setMediaType(MediaType.APPLICATION_OCTET_STREAM); + } + } + + if (result.getEncodings().isEmpty()) { + result.getEncodings().addAll(target.getEncodings()); + } + + if (result.getLanguages().isEmpty()) { + result.getLanguages().addAll(target.getLanguages()); + } + } + } else { + if (loggable) { + Context.getCurrentLogger() + .warning("Unable to find a converter for this object : " + source); + } + } + + return result; + } + + /** + * Updates the media type preferences with available conversion capabilities for the given + * entity class. + * + * @param preferences The media type preferences. + * @param entity The entity class to convert. + */ + public void updatePreferences(List> preferences, Class entity) { + for (ConverterHelper ch : Engine.getInstance().getRegisteredConverters()) { + ch.updatePreferences(preferences, entity); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/service/CorsService.java b/org.restlet/src/main/java/org/restlet/service/CorsService.java index 04bad797e9..831b0ffb8a 100644 --- a/org.restlet/src/main/java/org/restlet/service/CorsService.java +++ b/org.restlet/src/main/java/org/restlet/service/CorsService.java @@ -1,291 +1,278 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.restlet.Context; import org.restlet.data.Method; import org.restlet.engine.application.CorsFilter; import org.restlet.engine.util.SetUtils; import org.restlet.routing.Filter; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - /** - * Application service that adds support of CORS. This service lets the target - * resource specifies the allowed methods. - * - * Example: - * + * Application service that adds support of CORS. This service lets the target resource specify + * the allowed methods. + * + *

Example: + * *

  * CorsService corsService = new CorsService();
  * corsService.setAllowedOrigins(new HashSet(Arrays.asList("http://server.com")));
  * corsService.setAllowedCredentials(true);
  * 
- * + * * @author Manuel Boillod */ public class CorsService extends Service { - /** - * If true, add 'Access-Control-Allow-Credentials' header. Default is false. - */ - private boolean allowedCredentials = false; + /** If true, add an 'Access-Control-Allow-Credentials' header. Default is false. */ + private boolean allowedCredentials = false; - /** - * The value of 'Access-Control-Allow-Headers' response header. Used only if - * {@link #allowingAllRequestedHeaders} is false. - */ - private Set allowedHeaders = null; + /** + * The value of 'Access-Control-Allow-Headers' response header. Used only if {@link + * #allowingAllRequestedHeaders} is false. + */ + private Set allowedHeaders = null; - /** The value of 'Access-Control-Allow-Origin' header. Default is '*'. */ - private Set allowedOrigins = SetUtils.newHashSet("*"); + /** The value of the 'Access-Control-Allow-Origin' header. Default is '*'. */ + private Set allowedOrigins = SetUtils.newHashSet("*"); - /** - * If true, copies the value of 'Access-Control-Request-Headers' request header - * into the 'Access-Control-Allow-Headers' response header. If false, use - * {@link #allowedHeaders}. Default is true. - */ - private boolean allowingAllRequestedHeaders = true; + /** + * If true, copies the value of 'Access-Control-Request-Headers' request header into the + * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. + * Default is true. + */ + private boolean allowingAllRequestedHeaders = true; - /** - * The set of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. By default: GET, PUT, - * POST, DELETE, PATCH. - */ - private Set defaultAllowedMethods = new HashSet<>( - Arrays.asList(Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); + /** + * The set of methods allowed by default, used when {@link #skippingResourceForCorsOptions} is + * turned on. The default list is: GET, PUT, POST, DELETE, PATCH. + */ + private Set defaultAllowedMethods = + new HashSet<>( + Arrays.asList( + Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); - /** The value of 'Access-Control-Expose-Headers' response header. */ - private Set exposedHeaders = null; + /** The value of 'Access-Control-Expose-Headers' response header. */ + private Set exposedHeaders = null; - /** - * The value of 'Access-Control-Max-Age' response header. Default is that the - * header is not set. - */ - private int maxAge = -1; + /** + * The value of the 'Access-Control-Max-Age' response header. Default is that the header is not set. + */ + private int maxAge = -1; - /** - * If true, the filter does not call the server resource for OPTIONS method of - * CORS request and set Access-Control-Allow-Methods header with the default - * methods cf {@link #getDefaultAllowedMethods()}. Default is false. - */ - private boolean skippingResourceForCorsOptions = false; + /** + * If true, the filter does not call the server resource for OPTIONS method of CORS request and + * set Access-Control-Allow-Methods header with the default methods cf {@link + * #getDefaultAllowedMethods()}. Default is false. + */ + private boolean skippingResourceForCorsOptions = false; - /** - * Constructor. - */ - public CorsService() { - this(true); - } + /** Constructor. */ + public CorsService() { + this(true); + } - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - */ - public CorsService(boolean enabled) { - super(enabled); - } + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + */ + public CorsService(boolean enabled) { + super(enabled); + } - @Override - public Filter createInboundFilter(Context context) { - return new CorsFilter().setAllowedCredentials(allowedCredentials).setAllowedOrigins(allowedOrigins) - .setAllowingAllRequestedHeaders(allowingAllRequestedHeaders).setAllowedHeaders(allowedHeaders) - .setExposedHeaders(exposedHeaders).setSkippingResourceForCorsOptions(skippingResourceForCorsOptions) - .setDefaultAllowedMethods(getDefaultAllowedMethods()).setMaxAge(maxAge); - } + @Override + public Filter createInboundFilter(Context context) { + return new CorsFilter() + .setAllowedCredentials(allowedCredentials) + .setAllowedOrigins(allowedOrigins) + .setAllowingAllRequestedHeaders(allowingAllRequestedHeaders) + .setAllowedHeaders(allowedHeaders) + .setExposedHeaders(exposedHeaders) + .setSkippingResourceForCorsOptions(skippingResourceForCorsOptions) + .setDefaultAllowedMethods(getDefaultAllowedMethods()) + .setMaxAge(maxAge); + } - /** - * Returns the modifiable set of headers allowed by the actual request on the - * current resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Headers" header. - * - * @return The set of headers allowed by the actual request on the current - * resource. - */ - public Set getAllowedHeaders() { - return allowedHeaders; - } + /** + * Returns the modifiable set of headers allowed by the actual request on the current resource. + *
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Headers" header. + * + * @return The set of headers allowed by the actual request on the current resource. + */ + public Set getAllowedHeaders() { + return allowedHeaders; + } - /** - * Returns the URI an origin server allows for the requested resource. Use "*" - * as a wildcard character.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Allow-Origin" header. - * - * @return The origin allowed by the requested resource. - */ - public Set getAllowedOrigins() { - return allowedOrigins; - } + /** + * Returns the URI an origin server allows for the requested resource. Use "*" as a wildcard + * character.
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Allow-Origin" header. + * + * @return The origin allowed by the requested resource. + */ + public Set getAllowedOrigins() { + return allowedOrigins; + } - /** - * Returns the list of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. - * - * @return The list of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. - */ - public Set getDefaultAllowedMethods() { - return defaultAllowedMethods; - } + /** + * Returns the list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + * + * @return The list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + */ + public Set getDefaultAllowedMethods() { + return defaultAllowedMethods; + } - /** - * Returns a modifiable whitelist of headers an origin server allows for the - * requested resource.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Expose-Headers" header. - * - * @return The set of headers an origin server allows for the requested - * resource. - */ - public Set getExposedHeaders() { - return exposedHeaders; - } + /** + * Returns a modifiable whitelist of headers an origin server allows for the requested resource. + *
+ * Note that when used with HTTP connectors, this property maps to the + * "Access-Control-Expose-Headers" header. + * + * @return The set of headers an origin server allows for the requested resource. + */ + public Set getExposedHeaders() { + return exposedHeaders; + } - /** - * Indicates how long (in seconds) the results of a preflight request can be - * cached in a preflight result cache.
- * In case of a negative value, the results of a preflight request is not meant - * to be cached.
- * Note that when used with HTTP connectors, this property maps to the - * "Access-Control-Max-Age" header. - * - * @return Indicates how long the results of a preflight request can be cached - * in a preflight result cache. - */ - public int getMaxAge() { - return maxAge; - } + /** + * Indicates how long (in seconds) the results of a preflight request can be cached in a + * preflight result cache.
+ * In case of a negative value, the results of a preflight request is not meant to be cached. + *
+ * Note that when used with HTTP connectors, this property maps to the "Access-Control-Max-Age" + * header. + * + * @return Indicates how long the results of a preflight request can be cached in a preflight + * result cache. + */ + public int getMaxAge() { + return maxAge; + } - /** - * If true, adds 'Access-Control-Allow-Credentials' header. - * - * @return True, if the 'Access-Control-Allow-Credentials' header will be added. - */ - public boolean isAllowedCredentials() { - return allowedCredentials; - } + /** + * If true, adds 'Access-Control-Allow-Credentials' header. + * + * @return True, if the 'Access-Control-Allow-Credentials' header will be added. + */ + public boolean isAllowedCredentials() { + return allowedCredentials; + } - /** - * If true, indicates that the value of 'Access-Control-Request-Headers' request - * header will be copied into the 'Access-Control-Allow-Headers' response - * header. If false, use {@link #allowedHeaders}. - * - * @return True to copy the value - */ - public boolean isAllowingAllRequestedHeaders() { - return allowingAllRequestedHeaders; - } + /** + * If true, indicates that the value of 'Access-Control-Request-Headers' request header will be + * copied into the 'Access-Control-Allow-Headers' response header. If false, use {@link + * #allowedHeaders}. + * + * @return True to copy the value + */ + public boolean isAllowingAllRequestedHeaders() { + return allowingAllRequestedHeaders; + } - /** - * If true, the filter does not call the server resource for OPTIONS method of - * CORS request and set Access-Control-Allow-Methods header with the default - * methods. Default is false. - * - * @return True if the filter does not call the server resource for OPTIONS - * method of CORS request. - */ - public boolean isSkippingResourceForCorsOptions() { - return skippingResourceForCorsOptions; - } + /** + * If true, the filter does not call the server resource for OPTIONS method of CORS request and + * set the Access-Control-Allow-Methods header with the default methods. Default is false. + * + * @return True if the filter does not call the server resource for OPTIONS method of CORS + * request. + */ + public boolean isSkippingResourceForCorsOptions() { + return skippingResourceForCorsOptions; + } - /** - * If true, adds 'Access-Control-Allow-Credentials' header. - * - * @param allowedCredentials True to add the 'Access-Control-Allow-Credentials' - * header. - */ - public void setAllowedCredentials(boolean allowedCredentials) { - this.allowedCredentials = allowedCredentials; - } + /** + * If true, adds 'Access-Control-Allow-Credentials' header. + * + * @param allowedCredentials True to add the 'Access-Control-Allow-Credentials' header. + */ + public void setAllowedCredentials(boolean allowedCredentials) { + this.allowedCredentials = allowedCredentials; + } - /** - * Sets the value of the 'Access-Control-Allow-Headers' response header. Used - * only if {@link #allowingAllRequestedHeaders} is false. - * - * @param allowedHeaders The value of 'Access-Control-Allow-Headers' response - * header. - */ - public void setAllowedHeaders(Set allowedHeaders) { - this.allowedHeaders = allowedHeaders; - } + /** + * Sets the value of the 'Access-Control-Allow-Headers' response header. Used only if {@link + * #allowingAllRequestedHeaders} is false. + * + * @param allowedHeaders The value of 'Access-Control-Allow-Headers' response header. + */ + public void setAllowedHeaders(Set allowedHeaders) { + this.allowedHeaders = allowedHeaders; + } - /** - * Sets the value of 'Access-Control-Allow-Origin' header. - * - * @param allowedOrigins The value of 'Access-Control-Allow-Origin' header. - */ - public void setAllowedOrigins(Set allowedOrigins) { - this.allowedOrigins = allowedOrigins; - } + /** + * Sets the value of the 'Access-Control-Allow-Origin' header. + * + * @param allowedOrigins The value of the 'Access-Control-Allow-Origin' header. + */ + public void setAllowedOrigins(Set allowedOrigins) { + this.allowedOrigins = allowedOrigins; + } - /** - * If true, copies the value of 'Access-Control-Request-Headers' request header - * into the 'Access-Control-Allow-Headers' response header. If false, use - * {@link #allowedHeaders}. - * - * @param allowingAllRequestedHeaders True to copy the value of - * 'Access-Control-Request-Headers' request - * header into the - * 'Access-Control-Allow-Headers' response - * header. If false, use - * {@link #allowedHeaders}. - */ - public void setAllowingAllRequestedHeaders(boolean allowingAllRequestedHeaders) { - this.allowingAllRequestedHeaders = allowingAllRequestedHeaders; - } + /** + * If true, copies the value of 'Access-Control-Request-Headers' request header into the + * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. + * + * @param allowingAllRequestedHeaders True to copy the value of 'Access-Control-Request-Headers' + * request header into the 'Access-Control-Allow-Headers' response header. If false, use + * {@link #allowedHeaders}. + */ + public void setAllowingAllRequestedHeaders(boolean allowingAllRequestedHeaders) { + this.allowingAllRequestedHeaders = allowingAllRequestedHeaders; + } - /** - * Sets the list of methods allowed by default, used when - * {@link #skippingResourceForCorsOptions} is turned on. - * - * @param defaultAllowedMethods The list of methods allowed by default, used - * when {@link #skippingResourceForCorsOptions} is - * turned on. - */ - public void setDefaultAllowedMethods(Set defaultAllowedMethods) { - this.defaultAllowedMethods = defaultAllowedMethods; - } + /** + * Sets the list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + * + * @param defaultAllowedMethods The list of methods allowed by default, used when {@link + * #skippingResourceForCorsOptions} is turned on. + */ + public void setDefaultAllowedMethods(Set defaultAllowedMethods) { + this.defaultAllowedMethods = defaultAllowedMethods; + } - /** - * Sets the value of 'Access-Control-Expose-Headers' response header. - * - * @param exposedHeaders The value of 'Access-Control-Expose-Headers' response - * header. - */ - public void setExposedHeaders(Set exposedHeaders) { - this.exposedHeaders = exposedHeaders; - } + /** + * Sets the value of 'Access-Control-Expose-Headers' response header. + * + * @param exposedHeaders The value of 'Access-Control-Expose-Headers' response header. + */ + public void setExposedHeaders(Set exposedHeaders) { + this.exposedHeaders = exposedHeaders; + } - /** - * Sets the value of 'Access-Control-Max-Age' response header.
- * In case of negative value, the header is not set. - * - * @param maxAge The value of 'Access-Control-Max-Age' response header. - */ - public void setMaxAge(int maxAge) { - this.maxAge = maxAge; - } + /** + * Sets the value of the 'Access-Control-Max-Age' response header.
+ * In the case of negative value, the header is not set. + * + * @param maxAge The value of the 'Access-Control-Max-Age' response header. + */ + public void setMaxAge(int maxAge) { + this.maxAge = maxAge; + } - /** - * Sets the value of skipResourceForCorsOptions field. - * - * @param skipResourceForCorsOptions True if the filter does not call the server - * resource for OPTIONS method of CORS - * request. - */ - public void setSkippingResourceForCorsOptions(boolean skipResourceForCorsOptions) { - this.skippingResourceForCorsOptions = skipResourceForCorsOptions; - } + /** + * Sets the value of the skipResourceForCorsOptions field. + * + * @param skipResourceForCorsOptions True if the filter does not call the server resource for + * OPTIONS method of CORS request. + */ + public void setSkippingResourceForCorsOptions(boolean skipResourceForCorsOptions) { + this.skippingResourceForCorsOptions = skipResourceForCorsOptions; + } } diff --git a/org.restlet/src/main/java/org/restlet/service/DecoderService.java b/org.restlet/src/main/java/org/restlet/service/DecoderService.java index 3dff67b15a..721f74a2b5 100644 --- a/org.restlet/src/main/java/org/restlet/service/DecoderService.java +++ b/org.restlet/src/main/java/org/restlet/service/DecoderService.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; import org.restlet.Context; @@ -14,38 +13,35 @@ import org.restlet.routing.Filter; /** - * Application service automatically decoding or uncompressing received - * entities. This service works both for received requests entities on the - * server-side and received response entities on the client-side. - * + * Application service automatically decoding or uncompressing received entities. This service works + * both for received requests entities on the server-side and received response entities on the + * client-side. + * * @author Jerome Louvel */ public class DecoderService extends Service { - /** - * Constructor. - */ - public DecoderService() { - super(); - } - - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - */ - public DecoderService(boolean enabled) { - super(enabled); - } - - @Override - public Filter createInboundFilter(Context context) { - return new Decoder(context, true, false); - } - - @Override - public Filter createOutboundFilter(Context context) { - return new Decoder(context, false, true); - } - + /** Constructor. */ + public DecoderService() { + super(); + } + + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + */ + public DecoderService(boolean enabled) { + super(enabled); + } + + @Override + public Filter createInboundFilter(Context context) { + return new Decoder(context, true, false); + } + + @Override + public Filter createOutboundFilter(Context context) { + return new Decoder(context, false, true); + } } diff --git a/org.restlet/src/main/java/org/restlet/service/EncoderService.java b/org.restlet/src/main/java/org/restlet/service/EncoderService.java index cf70082f40..c3a413de40 100644 --- a/org.restlet/src/main/java/org/restlet/service/EncoderService.java +++ b/org.restlet/src/main/java/org/restlet/service/EncoderService.java @@ -1,14 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Context; import org.restlet.data.Encoding; import org.restlet.data.MediaType; @@ -16,185 +19,177 @@ import org.restlet.representation.Representation; import org.restlet.routing.Filter; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - /** * Application service automatically encoding or compressing request entities. - * + * * @author Jerome Louvel */ public class EncoderService extends Service { - /** Indicates if the encoding should always occur, regardless of the size. */ - public static final int ANY_SIZE = -1; + /** Indicates if the encoding should always occur, regardless of the size. */ + public static final int ANY_SIZE = -1; - /** Indicates if the default minimum size for encoding to occur. */ - public static final int DEFAULT_MINIMUM_SIZE = 1000; + /** Indicates if the default minimum size for encoding to occur. */ + public static final int DEFAULT_MINIMUM_SIZE = 1000; - /** - * Returns the list of default encoded media types. This can be overridden by - * subclasses. By default, all media types are encoded (except those explicitly - * ignored). - * - * @return The list of default encoded media types. - */ - public static List getDefaultAcceptedMediaTypes() { + /** + * Returns the list of default encoded media types. Subclasses can override this. By + * default, all media types are encoded (except those explicitly ignored). + * + * @return The list of default encoded media types. + */ + public static List getDefaultAcceptedMediaTypes() { return Arrays.asList(MediaType.ALL); - } - - /** - * Returns the list of default ignored media types. This can be overridden by - * subclasses. By default, all archive, audio, image and video media types are - * ignored. - * - * @return The list of default ignored media types. - */ - public static List getDefaultIgnoredMediaTypes() { - return Arrays.asList(MediaType.APPLICATION_CAB, - MediaType.APPLICATION_GNU_ZIP, MediaType.APPLICATION_ZIP, MediaType.APPLICATION_GNU_TAR, - MediaType.APPLICATION_JAVA_ARCHIVE, MediaType.APPLICATION_STUFFIT, MediaType.APPLICATION_TAR, - MediaType.AUDIO_ALL, MediaType.IMAGE_ALL, MediaType.VIDEO_ALL); - } - - /** - * The media types that should be encoded. - */ - private final List acceptedMediaTypes; - - /** - * The media types that should be ignored. - */ - private final List ignoredMediaTypes; - - /** - * The minimal size necessary for encoding. - */ - private volatile long mininumSize; - - /** - * Constructor. - */ - public EncoderService() { - this(true); - } - - /** - * Constructor. The default minimum size - * - * @param enabled True if the service has been enabled. - */ - public EncoderService(boolean enabled) { - super(enabled); - this.mininumSize = DEFAULT_MINIMUM_SIZE; - this.acceptedMediaTypes = new CopyOnWriteArrayList(getDefaultAcceptedMediaTypes()); - this.ignoredMediaTypes = new CopyOnWriteArrayList(getDefaultIgnoredMediaTypes()); - } - - /** - * Indicates if a representation can be encoded. - * - * @param representation The representation to test. - * @return True if the call can be encoded. - */ - public boolean canEncode(Representation representation) { - // Test the existence of the representation and that no existing - // encoding applies - boolean result = false; - - if (representation != null) { - boolean identity = true; - - for (Iterator iter = representation.getEncodings().iterator(); identity && iter.hasNext();) { - identity = (iter.next().equals(Encoding.IDENTITY)); - } - - result = identity; - } - - if (result) { - // Test the size of the representation - result = (getMinimumSize() == EncoderService.ANY_SIZE) - || (representation.getSize() == Representation.UNKNOWN_SIZE) - || (representation.getSize() >= getMinimumSize()); - } - - if (result) { - // Test the acceptance of the media type - MediaType mediaType = representation.getMediaType(); - boolean accepted = false; - - for (Iterator iter = getAcceptedMediaTypes().iterator(); !accepted && iter.hasNext();) { - accepted = iter.next().includes(mediaType); - } - - result = accepted; - } - - if (result) { - // Test the rejection of the media type - MediaType mediaType = representation.getMediaType(); - boolean rejected = false; - - for (Iterator iter = getIgnoredMediaTypes().iterator(); !rejected && iter.hasNext();) { - rejected = iter.next().includes(mediaType); - } - - result = !rejected; - } - - return result; - } - - @Override - public Filter createInboundFilter(Context context) { - return new Encoder(context, false, true, this); - } - - @Override - public Filter createOutboundFilter(Context context) { - return new Encoder(context, true, false, this); - } - - /** - * Returns the media types that should be encoded. - * - * @return The media types that should be encoded. - */ - public List getAcceptedMediaTypes() { - return this.acceptedMediaTypes; - } - - /** - * Returns the media types that should be ignored. - * - * @return The media types that should be ignored. - */ - public List getIgnoredMediaTypes() { - return this.ignoredMediaTypes; - } - - /** - * Returns the minimum size a representation must have before compression is - * done. - * - * @return The minimum size a representation must have before compression is - * done. - */ - public long getMinimumSize() { - return this.mininumSize; - } - - /** - * Sets the minimum size a representation must have before compression is done. - * - * @param mininumSize The minimum size a representation must have before - * compression is done. - */ - public void setMinimumSize(long mininumSize) { - this.mininumSize = mininumSize; - } - + } + + /** + * Returns the list of default ignored media types. Subclasses can override this. By + * default, all archive, audio, image, and video media types are ignored. + * + * @return The list of default ignored media types. + */ + public static List getDefaultIgnoredMediaTypes() { + return Arrays.asList( + MediaType.APPLICATION_CAB, + MediaType.APPLICATION_GNU_ZIP, + MediaType.APPLICATION_ZIP, + MediaType.APPLICATION_GNU_TAR, + MediaType.APPLICATION_JAVA_ARCHIVE, + MediaType.APPLICATION_STUFFIT, + MediaType.APPLICATION_TAR, + MediaType.AUDIO_ALL, + MediaType.IMAGE_ALL, + MediaType.VIDEO_ALL); + } + + /** The media types that should be encoded. */ + private final List acceptedMediaTypes; + + /** The media types that should be ignored. */ + private final List ignoredMediaTypes; + + /** The minimal size necessary for encoding. */ + private volatile long mininumSize; + + /** Constructor. */ + public EncoderService() { + this(true); + } + + /** + * Constructor. The default minimum size + * + * @param enabled True if the service has been enabled. + */ + public EncoderService(boolean enabled) { + super(enabled); + this.mininumSize = DEFAULT_MINIMUM_SIZE; + this.acceptedMediaTypes = new CopyOnWriteArrayList<>(getDefaultAcceptedMediaTypes()); + this.ignoredMediaTypes = new CopyOnWriteArrayList<>(getDefaultIgnoredMediaTypes()); + } + + /** + * Indicates if a representation can be encoded. + * + * @param representation The representation to test. + * @return True if the call can be encoded. + */ + public boolean canEncode(Representation representation) { + // Test the existence of the representation and that no existing + // encoding applies + boolean result = false; + + if (representation != null) { + boolean identity = true; + + for (Iterator iter = representation.getEncodings().iterator(); + identity && iter.hasNext(); ) { + identity = (iter.next().equals(Encoding.IDENTITY)); + } + + result = identity; + } + + if (result) { + // Test the size of the representation + result = + (getMinimumSize() == EncoderService.ANY_SIZE) + || (representation.getSize() == Representation.UNKNOWN_SIZE) + || (representation.getSize() >= getMinimumSize()); + } + + if (result) { + // Test the acceptance of the media type + MediaType mediaType = representation.getMediaType(); + boolean accepted = false; + + for (Iterator iter = getAcceptedMediaTypes().iterator(); + !accepted && iter.hasNext(); ) { + accepted = iter.next().includes(mediaType); + } + + result = accepted; + } + + if (result) { + // Test the rejection of the media type + MediaType mediaType = representation.getMediaType(); + boolean rejected = false; + + for (Iterator iter = getIgnoredMediaTypes().iterator(); + !rejected && iter.hasNext(); ) { + rejected = iter.next().includes(mediaType); + } + + result = !rejected; + } + + return result; + } + + @Override + public Filter createInboundFilter(Context context) { + return new Encoder(context, false, true, this); + } + + @Override + public Filter createOutboundFilter(Context context) { + return new Encoder(context, true, false, this); + } + + /** + * Returns the media types that should be encoded. + * + * @return The media types that should be encoded. + */ + public List getAcceptedMediaTypes() { + return this.acceptedMediaTypes; + } + + /** + * Returns the media types that should be ignored. + * + * @return The media types that should be ignored. + */ + public List getIgnoredMediaTypes() { + return this.ignoredMediaTypes; + } + + /** + * Returns the minimum size a representation must have before compression is done. + * + * @return The minimum size a representation must have before compression is done. + */ + public long getMinimumSize() { + return this.mininumSize; + } + + /** + * Sets the minimum size a representation must have before compression is done. + * + * @param mininumSize The minimum size a representation must have before compression is done. + */ + public void setMinimumSize(long mininumSize) { + this.mininumSize = mininumSize; + } } diff --git a/org.restlet/src/main/java/org/restlet/service/LogService.java b/org.restlet/src/main/java/org/restlet/service/LogService.java index 179ba44088..fbad40c7e4 100644 --- a/org.restlet/src/main/java/org/restlet/service/LogService.java +++ b/org.restlet/src/main/java/org/restlet/service/LogService.java @@ -1,14 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; @@ -22,378 +22,395 @@ import org.restlet.routing.Filter; import org.restlet.routing.Template; -import java.util.logging.Level; - /** - * Service providing access logging service. The implementation is fully based - * on the standard logging mechanism introduced in JDK 1.4.
+ * Service providing access logging service. The implementation is fully based on the standard + * logging mechanism introduced in JDK 1.4.
*
- * The default access log format follows the - * W3C Extended Log File - * Format with the following fields used:
+ * The default access log format follows the W3C + * Extended Log File Format with the following fields used:
+ * *

    - *
  1. Date (YYYY-MM-DD)
  2. - *
  3. Time (HH:MM:SS)
  4. - *
  5. Client address (IP)
  6. - *
  7. Remote user identifier (see RFC 1413)
  8. - *
  9. Server address (IP)
  10. - *
  11. Server port
  12. - *
  13. Method (GET|POST|...)
  14. - *
  15. Resource reference path (including the leading slash)
  16. - *
  17. Resource reference query (excluding the leading question mark)
  18. - *
  19. Response status code
  20. - *
  21. Number of bytes sent
  22. - *
  23. Number of bytes received
  24. - *
  25. Time to serve the request (in milliseconds)
  26. - *
  27. Host reference
  28. - *
  29. Client agent name
  30. - *
  31. Referrer reference
  32. + *
  33. Date (YYYY-MM-DD) + *
  34. Time (HH:MM:SS) + *
  35. Client address (IP) + *
  36. Remote user identifier (see RFC 1413) + *
  37. Server address (IP) + *
  38. Server port + *
  39. Method (GET|POST|...) + *
  40. Resource reference path (including the leading slash) + *
  41. Resource reference query (excluding the leading question mark) + *
  42. Response status code + *
  43. Number of bytes sent + *
  44. Number of bytes received + *
  45. Time to serve the request (in milliseconds) + *
  46. Host reference + *
  47. Client agent name + *
  48. Referrer reference *
+ * *
- * If you use Analog to - * generate your log reports, and if you use the default log format, then you - * can simply specify this string as a value of the LOGFORMAT command: + * If you use Analog to generate your log + * reports, and if you use the default log format, then you can simply specify this string as a + * value of the LOGFORMAT command: * (%Y-%m-%d\t%h:%n:%j\t%S\t%u\t%j\t%j\t%j\t%r\t%q\t%c\t%b\t%j\t%T\t%v\t%B\t%f)
*
- * For custom access log format, see the syntax to use and the list of available - * variable names in {@link org.restlet.routing.Template}.
- * + * For custom access log format, see the syntax to use and the list of available variable names in + * {@link org.restlet.routing.Template}.
+ * * @see java.util.logging + * "https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html">java.util.logging * @author Jerome Louvel */ public class LogService extends Service { - /** Indicates if the identity check (as specified by RFC1413) is enabled. */ - private volatile boolean identityCheck; - - /** The URI template of loggable resource references. */ - private volatile Template loggableTemplate; - - /** The access logger name. */ - private volatile String loggerName; - - /** The URI reference of the log properties. */ - private volatile Reference logPropertiesRef; - - /** The response log entry format. */ - private volatile String responseLogFormat; - - /** The response log template to use. */ - protected volatile Template responseLogTemplate; - - /** - * Constructor. - */ - public LogService() { - this(true); - } - - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - */ - public LogService(boolean enabled) { - super(enabled); - this.loggableTemplate = null; - this.loggerName = null; - this.responseLogFormat = null; - this.logPropertiesRef = null; - this.identityCheck = false; - } - - @Override - public Filter createInboundFilter(Context context) { - return new LogFilter(context, this); - } - - /** - * Format a log entry using the default IIS log format. - * - * @param response The response to log. - * @param duration The call duration (in milliseconds). - * @return The formatted log entry. - */ - protected String getDefaultResponseLogMessage(Response response, int duration) { - StringBuilder sb = new StringBuilder(); - Request request = response.getRequest(); - long currentTime = System.currentTimeMillis(); - - // Append the date of the request - sb.append(String.format("%tF", currentTime)); - sb.append('\t'); - - // Append the time of the request - sb.append(String.format("%tT", currentTime)); - sb.append('\t'); - - // Append the client IP address - String clientAddress = request.getClientInfo().getUpstreamAddress(); - sb.append((clientAddress == null) ? "-" : clientAddress); - sb.append('\t'); - - // Append the user name (via IDENT protocol) - if (isIdentityCheck()) { - org.restlet.engine.log.IdentClient ic = new org.restlet.engine.log.IdentClient( - request.getClientInfo().getUpstreamAddress(), request.getClientInfo().getPort(), - response.getServerInfo().getPort()); - sb.append((ic.getUserIdentifier() == null) ? "-" : ic.getUserIdentifier()); - } else if ((request.getChallengeResponse() != null) - && (request.getChallengeResponse().getIdentifier() != null)) { - sb.append(request.getChallengeResponse().getIdentifier()); - } else { - sb.append('-'); - } - - sb.append('\t'); - - // Append the server IP address - String serverAddress = response.getServerInfo().getAddress(); - sb.append((serverAddress == null) ? "-" : serverAddress); - sb.append('\t'); - - // Append the server port - Integer serverport = response.getServerInfo().getPort(); - sb.append((serverport == null) ? "-" : serverport.toString()); - sb.append('\t'); - - // Append the method name - String methodName = (request.getMethod() == null) ? "-" : request.getMethod().getName(); - sb.append((methodName == null) ? "-" : methodName); - - // Append the resource path - sb.append('\t'); - String resourcePath = (request.getResourceRef() == null) ? "-" : request.getResourceRef().getPath(); - sb.append((resourcePath == null) ? "-" : resourcePath); - - // Append the resource query - sb.append('\t'); - String resourceQuery = (request.getResourceRef() == null) ? "-" : request.getResourceRef().getQuery(); - sb.append((resourceQuery == null) ? "-" : resourceQuery); - - // Append the status code - sb.append('\t'); - sb.append((response.getStatus() == null) ? "-" : Integer.toString(response.getStatus().getCode())); - - // Append the returned size - sb.append('\t'); - - if (!response.isEntityAvailable() || Status.REDIRECTION_NOT_MODIFIED.equals(response.getStatus()) - || Status.SUCCESS_NO_CONTENT.equals(response.getStatus()) || Method.HEAD.equals(request.getMethod())) { - sb.append('0'); - } else { - sb.append((response.getEntity().getSize() == -1) ? "-" : Long.toString(response.getEntity().getSize())); - } - - // Append the received size - sb.append('\t'); - - try { - if (request.getEntity() == null) { - sb.append('0'); - } else { - sb.append((request.getEntity().getSize() == -1) ? "-" : Long.toString(request.getEntity().getSize())); - } - } catch (Throwable t) { - // Error while getting the request's entity, cf issue #931 - Engine.getLogger(LogService.class).log(Level.SEVERE, "Cannot retrieve size of request's entity", t); - sb.append("-"); - } - - // Append the duration - sb.append('\t'); - sb.append(duration); - - // Append the host reference - sb.append('\t'); - sb.append((request.getHostRef() == null) ? "-" : request.getHostRef().toString()); - - // Append the agent name - sb.append('\t'); - String agentName = request.getClientInfo().getAgent(); - sb.append((agentName == null) ? "-" : agentName); - - // Append the referrer - sb.append('\t'); - sb.append((request.getReferrerRef() == null) ? "-" : request.getReferrerRef().getIdentifier()); - - return sb.toString(); - } - - /** - * Returns the URI template of loggable resource references. Returns null by - * default, meaning the all requests are loggable, independant of their target - * resource URI reference. - * - * @return The URI template of loggable resource references. - * @see Request#getResourceRef() - */ - public Template getLoggableTemplate() { - return loggableTemplate; - } - - /** - * Returns the name of the JDK's logger to use when logging access calls. The - * default name will follow this pattern: "org.restlet.MyComponent.LogService", - * where "MyComponent" will correspond to the simple class name of your - * component subclass or to the base "Component" class. - * - * @return The name of the JDK's logger to use when logging access calls. - */ - public String getLoggerName() { - return this.loggerName; - } - - /** - * Returns the URI reference of the log properties. - * - * @return The URI reference of the log properties. - */ - public Reference getLogPropertiesRef() { - return logPropertiesRef; - } - - /** - * Returns the format used when logging responses. - * - * @return The format used, or null if the default one is used. - * @see org.restlet.routing.Template for format syntax and variables. - */ - public String getResponseLogFormat() { - return this.responseLogFormat; - } - - /** - * Format an access log entry. If the log template property isn't provided, then - * a default IIS like format is used. - * - * @param response The response to log. - * @param duration The call duration. - * @return The formatted log entry. - */ - public String getResponseLogMessage(Response response, int duration) { - String result = null; - - // Format the call into a log entry - if (this.responseLogTemplate != null) { - result = this.responseLogTemplate.format(response.getRequest(), response); - } else { - result = getDefaultResponseLogMessage(response, duration); - } - - return result; - } - - /** - * Indicates if the identity check (as specified by RFC1413) is enabled. Default - * value is false. - * - * @return True if the identity check is enabled. - */ - public boolean isIdentityCheck() { - return this.identityCheck; - } - - /** - * Indicates if the call should be logged during the processing chain. By - * default, it tries to match the request URI with the - * {@link #getLoggableTemplate()} URI template otherwise is returns true. - * - * @param request The request to log. - * @return True if the call should be logged during the processing chain. - */ - public boolean isLoggable(Request request) { - return getLoggableTemplate() == null - || getLoggableTemplate().match(request.getResourceRef().getTargetRef().toString()) > 0; - } - - /** - * Indicates if the identity check (as specified by RFC1413) is enabled. - * - * @param identityCheck True if the identity check is enabled. - */ - public void setIdentityCheck(boolean identityCheck) { - this.identityCheck = identityCheck; - } - - /** - * Sets the URI template of loggable resource references. - * - * @param loggableTemplateRef The URI template of loggable resource references. - * @see #setLoggableTemplate(Template) - */ - public void setLoggableTemplate(String loggableTemplateRef) { - if (loggableTemplateRef != null) { - this.loggableTemplate = new Template(loggableTemplateRef); - } else { - this.loggableTemplate = null; - } - } - - /** - * Sets the URI template of loggable resource references. - * - * @param loggableTemplate The URI template of loggable resource references. - */ - public void setLoggableTemplate(Template loggableTemplate) { - this.loggableTemplate = loggableTemplate; - } - - /** - * Sets the name of the JDK's logger to use when logging access calls. - * - * @param name The name of the JDK's logger to use when logging access calls. - */ - public void setLoggerName(String name) { - this.loggerName = name; - } - - /** - * Sets the URI reference of the log properties. - * - * @param logPropertiesRef The URI reference of the log properties. - */ - public void setLogPropertiesRef(Reference logPropertiesRef) { - this.logPropertiesRef = logPropertiesRef; - } - - /** - * Sets the URI reference of the log properties. - * - * @param logPropertiesUri The URI reference of the log properties. - */ - public void setLogPropertiesRef(String logPropertiesUri) { - setLogPropertiesRef(new Reference(logPropertiesUri)); - } - - /** - * Sets the format to use when logging responses. The default format matches the - * one of IIS 6. - * - * @param responseLogFormat The format to use when logging responses. - * @see org.restlet.routing.Template for format syntax and variables. - */ - public void setResponseLogFormat(String responseLogFormat) { - this.responseLogFormat = responseLogFormat; - } - - /** - * Starts the log service by attempting to read the log properties if the - * {@link #getLogPropertiesRef()} returns a non null URI reference. - */ - @Override - public synchronized void start() throws Exception { - super.start(); - - this.responseLogTemplate = (getResponseLogFormat() == null) ? null : new Template(getResponseLogFormat()); - - if (getLogPropertiesRef() != null) { - Representation logProperties = new ClientResource(getContext(), getLogPropertiesRef()).get(); - - if (logProperties != null) { - java.util.logging.LogManager.getLogManager().readConfiguration(logProperties.getStream()); - } - } - } + /** Indicates if the identity check (as specified by RFC1413) is enabled. */ + private volatile boolean identityCheck; + + /** The URI template of loggable resource references. */ + private volatile Template loggableTemplate; + + /** The access logger name. */ + private volatile String loggerName; + + /** The URI reference of the log properties. */ + private volatile Reference logPropertiesRef; + + /** The response log entry format. */ + private volatile String responseLogFormat; + + /** The response log template to use. */ + protected volatile Template responseLogTemplate; + + /** Constructor. */ + public LogService() { + this(true); + } + + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + */ + public LogService(boolean enabled) { + super(enabled); + this.loggableTemplate = null; + this.loggerName = null; + this.responseLogFormat = null; + this.logPropertiesRef = null; + this.identityCheck = false; + } + + @Override + public Filter createInboundFilter(Context context) { + return new LogFilter(context, this); + } + + /** + * Format a log entry using the default IIS log format. + * + * @param response The response to log. + * @param duration The call duration (in milliseconds). + * @return The formatted log entry. + */ + protected String getDefaultResponseLogMessage(Response response, int duration) { + StringBuilder sb = new StringBuilder(); + Request request = response.getRequest(); + long currentTime = System.currentTimeMillis(); + + // Append the date of the request + sb.append(String.format("%tF", currentTime)); + sb.append('\t'); + + // Append the time of the request + sb.append(String.format("%tT", currentTime)); + sb.append('\t'); + + // Append the client IP address + String clientAddress = request.getClientInfo().getUpstreamAddress(); + sb.append((clientAddress == null) ? "-" : clientAddress); + sb.append('\t'); + + // Append the user name (via IDENT protocol) + if (isIdentityCheck()) { + org.restlet.engine.log.IdentClient ic = + new org.restlet.engine.log.IdentClient( + request.getClientInfo().getUpstreamAddress(), + request.getClientInfo().getPort(), + response.getServerInfo().getPort()); + sb.append((ic.getUserIdentifier() == null) ? "-" : ic.getUserIdentifier()); + } else if ((request.getChallengeResponse() != null) + && (request.getChallengeResponse().getIdentifier() != null)) { + sb.append(request.getChallengeResponse().getIdentifier()); + } else { + sb.append('-'); + } + + sb.append('\t'); + + // Append the server IP address + String serverAddress = response.getServerInfo().getAddress(); + sb.append((serverAddress == null) ? "-" : serverAddress); + sb.append('\t'); + + // Append the server port + Integer serverPort = response.getServerInfo().getPort(); + sb.append((serverPort == null) ? "-" : serverPort.toString()); + sb.append('\t'); + + // Append the method name + String methodName = (request.getMethod() == null) ? "-" : request.getMethod().getName(); + sb.append((methodName == null) ? "-" : methodName); + + // Append the resource path + sb.append('\t'); + String resourcePath = + (request.getResourceRef() == null) ? "-" : request.getResourceRef().getPath(); + sb.append((resourcePath == null) ? "-" : resourcePath); + + // Append the resource query + sb.append('\t'); + String resourceQuery = + (request.getResourceRef() == null) ? "-" : request.getResourceRef().getQuery(); + sb.append((resourceQuery == null) ? "-" : resourceQuery); + + // Append the status code + sb.append('\t'); + sb.append( + (response.getStatus() == null) + ? "-" + : Integer.toString(response.getStatus().getCode())); + + // Append the returned size + sb.append('\t'); + + if (!response.isEntityAvailable() + || Status.REDIRECTION_NOT_MODIFIED.equals(response.getStatus()) + || Status.SUCCESS_NO_CONTENT.equals(response.getStatus()) + || Method.HEAD.equals(request.getMethod())) { + sb.append('0'); + } else { + sb.append( + (response.getEntity().getSize() == -1) + ? "-" + : Long.toString(response.getEntity().getSize())); + } + + // Append the received size + sb.append('\t'); + + try { + if (request.getEntity() == null) { + sb.append('0'); + } else { + sb.append( + (request.getEntity().getSize() == -1) + ? "-" + : Long.toString(request.getEntity().getSize())); + } + } catch (Throwable t) { + // Error while getting the request's entity, cf issue #931 + Engine.getLogger(LogService.class) + .log(Level.SEVERE, "Cannot retrieve size of request's entity", t); + sb.append("-"); + } + + // Append the duration + sb.append('\t'); + sb.append(duration); + + // Append the host reference + sb.append('\t'); + sb.append((request.getHostRef() == null) ? "-" : request.getHostRef().toString()); + + // Append the agent name + sb.append('\t'); + String agentName = request.getClientInfo().getAgent(); + sb.append((agentName == null) ? "-" : agentName); + + // Append the referrer + sb.append('\t'); + sb.append( + (request.getReferrerRef() == null) + ? "-" + : request.getReferrerRef().getIdentifier()); + + return sb.toString(); + } + + /** + * Returns the URI template of loggable resource references. Returns null by default, meaning + * all requests are loggable, independent of their target resource URI reference. + * + * @return The URI template of loggable resource references. + * @see Request#getResourceRef() + */ + public Template getLoggableTemplate() { + return loggableTemplate; + } + + /** + * Returns the name of the JDK's logger to use when logging access calls. The default name will + * follow this pattern: "org.restlet.MyComponent.LogService", where "MyComponent" will + * correspond to the simple class name of your component subclass or to the base "Component" + * class. + * + * @return The name of the JDK's logger to use when logging access calls. + */ + public String getLoggerName() { + return this.loggerName; + } + + /** + * Returns the URI reference of the log properties. + * + * @return The URI reference of the log properties. + */ + public Reference getLogPropertiesRef() { + return logPropertiesRef; + } + + /** + * Returns the format used when logging responses. + * + * @return The format used, or null if the default one is used. + * @see org.restlet.routing.Template for format syntax and variables. + */ + public String getResponseLogFormat() { + return this.responseLogFormat; + } + + /** + * Format an access log entry. If the log template property isn't provided, then a default IIS + * like format is used. + * + * @param response The response to log. + * @param duration The call duration. + * @return The formatted log entry. + */ + public String getResponseLogMessage(Response response, int duration) { + final String result; + + // Format the call into a log entry + if (this.responseLogTemplate != null) { + result = this.responseLogTemplate.format(response.getRequest(), response); + } else { + result = getDefaultResponseLogMessage(response, duration); + } + + return result; + } + + /** + * Indicates if the identity check (as specified by RFC1413) is enabled. The default value is false. + * + * @return True if the identity check is enabled. + */ + public boolean isIdentityCheck() { + return this.identityCheck; + } + + /** + * Indicates if the call should be logged during the processing chain. By default, it tries to + * match the request URI with the {@link #getLoggableTemplate()} URI template otherwise is + * returns true. + * + * @param request The request to log. + * @return True if the call should be logged during the processing chain. + */ + public boolean isLoggable(Request request) { + return getLoggableTemplate() == null + || getLoggableTemplate().match(request.getResourceRef().getTargetRef().toString()) + > 0; + } + + /** + * Indicates if the identity check (as specified by RFC1413) is enabled. + * + * @param identityCheck True if the identity check is enabled. + */ + public void setIdentityCheck(boolean identityCheck) { + this.identityCheck = identityCheck; + } + + /** + * Sets the URI template of loggable resource references. + * + * @param loggableTemplateRef The URI template of loggable resource references. + * @see #setLoggableTemplate(Template) + */ + public void setLoggableTemplate(String loggableTemplateRef) { + if (loggableTemplateRef != null) { + this.loggableTemplate = new Template(loggableTemplateRef); + } else { + this.loggableTemplate = null; + } + } + + /** + * Sets the URI template of loggable resource references. + * + * @param loggableTemplate The URI template of loggable resource references. + */ + public void setLoggableTemplate(Template loggableTemplate) { + this.loggableTemplate = loggableTemplate; + } + + /** + * Sets the name of the JDK's logger to use when logging access calls. + * + * @param name The name of the JDK's logger to use when logging access calls. + */ + public void setLoggerName(String name) { + this.loggerName = name; + } + + /** + * Sets the URI reference of the log properties. + * + * @param logPropertiesRef The URI reference of the log properties. + */ + public void setLogPropertiesRef(Reference logPropertiesRef) { + this.logPropertiesRef = logPropertiesRef; + } + + /** + * Sets the URI reference of the log properties. + * + * @param logPropertiesUri The URI reference of the log properties. + */ + public void setLogPropertiesRef(String logPropertiesUri) { + setLogPropertiesRef(new Reference(logPropertiesUri)); + } + + /** + * Sets the format to use when logging responses. The default format matches the one of IIS 6. + * + * @param responseLogFormat The format to use when logging responses. + * @see org.restlet.routing.Template for format syntax and variables. + */ + public void setResponseLogFormat(String responseLogFormat) { + this.responseLogFormat = responseLogFormat; + } + + /** + * Starts the log service by attempting to read the log properties if the {@link + * #getLogPropertiesRef()} returns a non-null URI reference. + */ + @Override + public synchronized void start() throws Exception { + super.start(); + + this.responseLogTemplate = + (getResponseLogFormat() == null) ? null : new Template(getResponseLogFormat()); + + if (getLogPropertiesRef() != null) { + Representation logProperties = + new ClientResource(getContext(), getLogPropertiesRef()).get(); + + if (logProperties != null) { + java.util.logging.LogManager.getLogManager() + .readConfiguration(logProperties.getStream()); + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/service/MetadataService.java b/org.restlet/src/main/java/org/restlet/service/MetadataService.java index f005ba23d5..cb04b12f18 100644 --- a/org.restlet/src/main/java/org/restlet/service/MetadataService.java +++ b/org.restlet/src/main/java/org/restlet/service/MetadataService.java @@ -1,792 +1,792 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; -import org.restlet.data.*; -import org.restlet.engine.application.MetadataExtension; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.data.CharacterSet; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.engine.application.MetadataExtension; /** - * Application service providing access to metadata and their associated - * extension names. The list of default mappings is documented in the - * {@link #addCommonExtensions()} method.
+ * Application service providing access to metadata and their associated extension names. The list + * of default mappings is documented in the {@link #addCommonExtensions()} method.
*
* Internally, the mappings are stored as a list of "extension, metadata" pairs. - * + * * @author Jerome Louvel */ public class MetadataService extends Service { - /** The default character set for textual representations. */ - private volatile CharacterSet defaultCharacterSet; - - /** The default encoding for representations. */ - private volatile Encoding defaultEncoding; - - /** The default language for representations. */ - private volatile Language defaultLanguage; - - /** The default media type for representations. */ - private volatile MediaType defaultMediaType; - - /** The list of mappings between extension names and metadata. */ - private final List mappings; - - /** - * Constructor. Sets the default language to {@link Language#ENGLISH_US}, the - * default encoding to {@link Encoding#IDENTITY} (no encoding) and the default - * media type to {@link MediaType#APPLICATION_OCTET_STREAM}. It also calls the - * {@link #addCommonExtensions()} method. - */ - public MetadataService() { - this.defaultCharacterSet = CharacterSet.DEFAULT; - this.defaultEncoding = Encoding.IDENTITY; - this.defaultLanguage = Language.DEFAULT; - this.defaultMediaType = MediaType.APPLICATION_OCTET_STREAM; - this.mappings = new CopyOnWriteArrayList(); - addCommonExtensions(); - } - - /** - * Adds a common list of associations from extensions to metadata.
- * - * The list of languages extensions:
- *

    - *
  • en: English
  • - *
  • es: Spanish
  • - *
  • fr: French
  • - *
- *
- * The list of character set extensions:
- *
    - *
  • ascii: US-ASCII
  • - *
- *
- * The list of media type extensions:
- *
    - *
  • ai: PostScript document
  • - *
  • atom: Atom syndication document
  • - *
  • au: AU audio file
  • - *
  • bin: Binary file
  • - *
  • bmp: Bitmap graphics
  • - *
  • class: Java bytecode
  • - *
  • css: CSS stylesheet
  • - *
  • csv: Comma-separated Values
  • - *
  • dat: Fixed-width Values
  • - *
  • dib: Device-Independent Bitmap Graphics
  • - *
  • doc: Microsoft Word document
  • - *
  • docx: Microsoft Office Word 2007 document
  • - *
  • docm: Office Word 2007 macro-enabled document
  • - *
  • dotx: Office Word 2007 template
  • - *
  • dotm: Office Word 2007 macro-enabled document template
  • - *
  • dtd: XML Document Type Definition
  • - *
  • eps: Encapsulated PostScript
  • - *
  • exe: Executable File (Microsoft Corporation)
  • - *
  • fmt: FreeMarker encoding
  • - *
  • form: Web forms (URL encoded)
  • - *
  • ftl: FreeMarker encoding
  • - *
  • gif: GIF image
  • - *
  • gwt: Java serialized object (using GWT-RPC encoder)
  • - *
  • hqx: BinHex 4 Compressed Archive (Macintosh)
  • - *
  • htm, html: HTML document
  • - *
  • ico: Windows icon (Favicon)
  • - *
  • jad: Java Application Descriptor file
  • - *
  • jar: Java Archive
  • - *
  • java: Java source code
  • - *
  • jnlp: Java Web start launch file
  • - *
  • jpe, jpeg, jpg: JPEG image
  • - *
  • js: JavaScript document
  • - *
  • jsf: Java Server Faces file
  • - *
  • json: JavaScript Object Notation document
  • - *
  • jsonsmile: JavaScript Object Notation smile document
  • - *
  • kar: Karaoke MIDI file
  • - *
  • latex: LaTeX document
  • - *
  • man: Manual file
  • - *
  • mathml: Mathml XML document
  • - *
  • mid, midi: MIDI Audio
  • - *
  • mov, qt: QuickTime video clip (Apple Computer, Inc.)
  • - *
  • mp2, mp3: MPEG Audio Stream file
  • - *
  • mp4: MPEG-4 video file
  • - *
  • mpe, mpeg, mpg: MPEG video clip
  • - *
  • n3: RDF N3 document
  • - *
  • nt: RDF N-Triples document
  • - *
  • odb: OpenDocument Database
  • - *
  • odc: OpenDocument Chart
  • - *
  • odf: OpenDocument Formula
  • - *
  • odg: OpenDocument Drawing
  • - *
  • odi: OpenDocument Image
  • - *
  • odm: OpenDocument Master Document
  • - *
  • odp: OpenDocument Presentation
  • - *
  • ods: OpenDocument Spreadsheet
  • - *
  • odt: OpenDocument Text
  • - *
  • onetoc: Microsoft Office OneNote 2007 TOC
  • - *
  • onetoc2: Office OneNote 2007 TOC
  • - *
  • otg: OpenDocument Drawing Template
  • - *
  • oth: HTML Document Template
  • - *
  • otp: OpenDocument Presentation Template
  • - *
  • ots: OpenDocument Spreadsheet Template
  • - *
  • ott: OpenDocument Text Template
  • - *
  • oxt: OpenOffice.org extension
  • - *
  • pdf: Adobe PDF document
  • - *
  • png: PNG image
  • - *
  • potm: Office PowerPoint 2007 macro-enabled presentation template
  • - *
  • potx: Office PowerPoint 2007 template
  • - *
  • ppam: Office PowerPoint 2007 add-in
  • - *
  • pps, ppt: Microsoft Powerpoint document
  • - *
  • ppsm: Office PowerPoint 2007 macro-enabled slide show
  • - *
  • ppsx: Office PowerPoint 2007 slide show
  • - *
  • pptm: Office PowerPoint 2007 macro-enabled presentation
  • - *
  • pptx: Microsoft Office PowerPoint 2007 presentation
  • - *
  • ps: PostScript document
  • - *
  • rdf: Description Framework document
  • - *
  • rnc: Relax NG Schema document, Compact syntax
  • - *
  • rng: Relax NG Schema document, XML syntax
  • - *
  • rss: RSS file
  • - *
  • rtf: Rich Text Format document
  • - *
  • sav: SPSS Data
  • - *
  • sit: StuffIt compressed archive file
  • - *
  • sldm: Office PowerPoint 2007 macro-enabled slide
  • - *
  • sldx: Office PowerPoint 2007 slide
  • - *
  • snd: Amiga sound
  • - *
  • sps: SPSS Script Syntax
  • - *
  • sta: Stata data file
  • - *
  • svg: Scalable Vector Graphics file
  • - *
  • swf: Adobe Flash file
  • - *
  • tar: Tape Archive file
  • - *
  • tex: Tex file
  • - *
  • tif, tiff: Tagged Image Format File
  • - *
  • tsv: Tab-separated Values
  • - *
  • txt: Plain text
  • - *
  • ulw: MU-LAW (US telephony format)
  • - *
  • vm: Velocity encoding
  • - *
  • vrml: Virtual Reality Modeling Language file
  • - *
  • vxml: VoiceXML source file
  • - *
  • wadl: Web Application Description Language document
  • - *
  • wav: Waveform audio
  • - *
  • wrl: Plain text VRML file
  • - *
  • xht, xhtml: XHTML document
  • - *
  • xlam: Office Excel 2007 add-in
  • - *
  • xls: Microsoft Excel document
  • - *
  • xlsb: Office Excel 2007 binary workbook
  • - *
  • xlsm: Office Excel 2007 macro-enabled workbook
  • - *
  • xlsx: Microsoft Office Excel 2007 workbook
  • - *
  • xltm: Office Excel 2007 macro-enabled workbook template
  • - *
  • xltx: Office Excel 2007 template
  • - *
  • xmi: XMI document
  • - *
  • xml: XML document
  • - *
  • xsd: W3C XML Schema document
  • - *
  • xsl, xslt: XSL Transform file
  • - *
  • xul: XML User Interface Language file
  • - *
  • yaml: YAML text format
  • - *
  • z: UNIX compressed archive file
  • - *
  • zip: Zip archive
  • - *
- */ - public void addCommonExtensions() { - List dm = new ArrayList(); - - ext(dm, "en", Language.ENGLISH); - ext(dm, "es", Language.SPANISH); - ext(dm, "fr", Language.FRENCH); - - ext(dm, "ascii", CharacterSet.US_ASCII); - - ext(dm, "ai", MediaType.APPLICATION_POSTSCRIPT); - ext(dm, "atom", MediaType.APPLICATION_ATOM); - ext(dm, "atomcat", MediaType.APPLICATION_ATOMPUB_CATEGORY); - ext(dm, "atomsvc", MediaType.APPLICATION_ATOMPUB_SERVICE); - ext(dm, "au", MediaType.AUDIO_BASIC); - ext(dm, "bin", MediaType.APPLICATION_OCTET_STREAM); - ext(dm, "bmp", MediaType.IMAGE_BMP); - ext(dm, "class", MediaType.APPLICATION_JAVA); - ext(dm, "css", MediaType.TEXT_CSS); - ext(dm, "csv", MediaType.TEXT_CSV); - ext(dm, "dat", MediaType.TEXT_DAT); - ext(dm, "dib", MediaType.IMAGE_BMP); - ext(dm, "doc", MediaType.APPLICATION_WORD); - ext(dm, "docm", MediaType.APPLICATION_MSOFFICE_DOCM); - ext(dm, "docx", MediaType.APPLICATION_MSOFFICE_DOCX); - ext(dm, "dotm", MediaType.APPLICATION_MSOFFICE_DOTM); - ext(dm, "dotx", MediaType.APPLICATION_MSOFFICE_DOTX); - ext(dm, "dtd", MediaType.APPLICATION_XML_DTD); - ext(dm, "ecore", MediaType.APPLICATION_ECORE); - ext(dm, "eps", MediaType.APPLICATION_POSTSCRIPT); - ext(dm, "exe", MediaType.APPLICATION_OCTET_STREAM); - ext(dm, "fmt", Encoding.FREEMARKER); - ext(dm, "form", MediaType.APPLICATION_WWW_FORM); - ext(dm, "ftl", Encoding.FREEMARKER, true); - ext(dm, "gif", MediaType.IMAGE_GIF); - ext(dm, "gwt", MediaType.APPLICATION_JAVA_OBJECT_GWT); - ext(dm, "hqx", MediaType.APPLICATION_MAC_BINHEX40); - ext(dm, "ico", MediaType.IMAGE_ICON); - ext(dm, "jad", MediaType.TEXT_J2ME_APP_DESCRIPTOR); - ext(dm, "jar", MediaType.APPLICATION_JAVA_ARCHIVE); - ext(dm, "java", MediaType.TEXT_PLAIN); - ext(dm, "jnlp", MediaType.APPLICATION_JNLP); - ext(dm, "jpe", MediaType.IMAGE_JPEG); - ext(dm, "jpeg", MediaType.IMAGE_JPEG); - ext(dm, "jpg", MediaType.IMAGE_JPEG); - ext(dm, "js", MediaType.APPLICATION_JAVASCRIPT); - ext(dm, "jsf", MediaType.TEXT_PLAIN); - ext(dm, "kar", MediaType.AUDIO_MIDI); - ext(dm, "latex", MediaType.APPLICATION_LATEX); - ext(dm, "latin1", CharacterSet.ISO_8859_1); - ext(dm, "mac", CharacterSet.MACINTOSH); - ext(dm, "man", MediaType.APPLICATION_TROFF_MAN); - ext(dm, "mathml", MediaType.APPLICATION_MATHML); - ext(dm, "mid", MediaType.AUDIO_MIDI); - ext(dm, "midi", MediaType.AUDIO_MIDI); - ext(dm, "mov", MediaType.VIDEO_QUICKTIME); - ext(dm, "mp2", MediaType.AUDIO_MPEG); - ext(dm, "mp3", MediaType.AUDIO_MPEG); - ext(dm, "mp4", MediaType.VIDEO_MP4); - ext(dm, "mpe", MediaType.VIDEO_MPEG); - ext(dm, "mpeg", MediaType.VIDEO_MPEG); - ext(dm, "mpg", MediaType.VIDEO_MPEG); - ext(dm, "n3", MediaType.TEXT_RDF_N3); - ext(dm, "nt", MediaType.TEXT_PLAIN); - ext(dm, "odb", MediaType.APPLICATION_OPENOFFICE_ODB); - ext(dm, "odc", MediaType.APPLICATION_OPENOFFICE_ODC); - ext(dm, "odf", MediaType.APPLICATION_OPENOFFICE_ODF); - ext(dm, "odi", MediaType.APPLICATION_OPENOFFICE_ODI); - ext(dm, "odm", MediaType.APPLICATION_OPENOFFICE_ODM); - ext(dm, "odg", MediaType.APPLICATION_OPENOFFICE_ODG); - ext(dm, "odp", MediaType.APPLICATION_OPENOFFICE_ODP); - ext(dm, "ods", MediaType.APPLICATION_OPENOFFICE_ODS); - ext(dm, "odt", MediaType.APPLICATION_OPENOFFICE_ODT); - ext(dm, "onetoc", MediaType.APPLICATION_MSOFFICE_ONETOC); - ext(dm, "onetoc2", MediaType.APPLICATION_MSOFFICE_ONETOC2); - ext(dm, "otg", MediaType.APPLICATION_OPENOFFICE_OTG); - ext(dm, "oth", MediaType.APPLICATION_OPENOFFICE_OTH); - ext(dm, "otp", MediaType.APPLICATION_OPENOFFICE_OTP); - ext(dm, "ots", MediaType.APPLICATION_OPENOFFICE_OTS); - ext(dm, "ott", MediaType.APPLICATION_OPENOFFICE_OTT); - ext(dm, "oxt", MediaType.APPLICATION_OPENOFFICE_OXT); - ext(dm, "pdf", MediaType.APPLICATION_PDF); - ext(dm, "png", MediaType.IMAGE_PNG); - ext(dm, "potx", MediaType.APPLICATION_MSOFFICE_POTX); - ext(dm, "potm", MediaType.APPLICATION_MSOFFICE_POTM); - ext(dm, "ppam", MediaType.APPLICATION_MSOFFICE_PPAM); - ext(dm, "pps", MediaType.APPLICATION_POWERPOINT); - ext(dm, "ppsm", MediaType.APPLICATION_MSOFFICE_PPSM); - ext(dm, "ppsx", MediaType.APPLICATION_MSOFFICE_PPSX); - ext(dm, "ppt", MediaType.APPLICATION_POWERPOINT); - ext(dm, "pptm", MediaType.APPLICATION_MSOFFICE_PPTM); - ext(dm, "pptx", MediaType.APPLICATION_MSOFFICE_PPTX); - ext(dm, "ps", MediaType.APPLICATION_POSTSCRIPT); - ext(dm, "qt", MediaType.VIDEO_QUICKTIME); - ext(dm, "rdf", MediaType.APPLICATION_RDF_XML); - ext(dm, "rnc", MediaType.APPLICATION_RELAXNG_COMPACT); - ext(dm, "rng", MediaType.APPLICATION_RELAXNG_XML); - ext(dm, "rss", MediaType.APPLICATION_RSS); - ext(dm, "rtf", MediaType.APPLICATION_RTF); - ext(dm, "sav", MediaType.APPLICATION_SPSS_SAV); - ext(dm, "sit", MediaType.APPLICATION_STUFFIT); - ext(dm, "sldm", MediaType.APPLICATION_MSOFFICE_SLDM); - ext(dm, "sldx", MediaType.APPLICATION_MSOFFICE_SLDX); - ext(dm, "snd", MediaType.AUDIO_BASIC); - ext(dm, "sps", MediaType.APPLICATION_SPSS_SPS); - ext(dm, "sta", MediaType.APPLICATION_STATA_STA); - ext(dm, "svg", MediaType.IMAGE_SVG); - ext(dm, "swf", MediaType.APPLICATION_FLASH); - ext(dm, "tar", MediaType.APPLICATION_TAR); - ext(dm, "tex", MediaType.APPLICATION_TEX); - ext(dm, "tif", MediaType.IMAGE_TIFF); - ext(dm, "tiff", MediaType.IMAGE_TIFF); - ext(dm, "tsv", MediaType.TEXT_TSV); - ext(dm, "ulw", MediaType.AUDIO_BASIC); - ext(dm, "utf16", CharacterSet.UTF_16); - ext(dm, "utf8", CharacterSet.UTF_8); - ext(dm, "vm", Encoding.VELOCITY); - ext(dm, "vrml", MediaType.MODEL_VRML); - ext(dm, "vxml", MediaType.APPLICATION_VOICEXML); - ext(dm, "wadl", MediaType.APPLICATION_WADL); - ext(dm, "wav", MediaType.AUDIO_WAV); - ext(dm, "win", CharacterSet.WINDOWS_1252); - ext(dm, "wrl", MediaType.MODEL_VRML); - ext(dm, "xht", MediaType.APPLICATION_XHTML); - ext(dm, "xls", MediaType.APPLICATION_EXCEL); - ext(dm, "xlsx", MediaType.APPLICATION_MSOFFICE_XLSX); - ext(dm, "xlsm", MediaType.APPLICATION_MSOFFICE_XLSM); - ext(dm, "xltx", MediaType.APPLICATION_MSOFFICE_XLTX); - ext(dm, "xltm", MediaType.APPLICATION_MSOFFICE_XLTM); - ext(dm, "xlsb", MediaType.APPLICATION_MSOFFICE_XLSB); - ext(dm, "xlam", MediaType.APPLICATION_MSOFFICE_XLAM); - ext(dm, "xmi", MediaType.APPLICATION_XMI); - ext(dm, "xsd", MediaType.APPLICATION_W3C_SCHEMA); - ext(dm, "xsl", MediaType.APPLICATION_W3C_XSLT); - ext(dm, "xslt", MediaType.APPLICATION_W3C_XSLT); - ext(dm, "xul", MediaType.APPLICATION_XUL); - ext(dm, "yaml", MediaType.APPLICATION_YAML); - ext(dm, "yaml", MediaType.TEXT_YAML); - ext(dm, "z", MediaType.APPLICATION_COMPRESS); - ext(dm, "zip", MediaType.APPLICATION_ZIP); - ext(dm, "htm", MediaType.TEXT_HTML); - ext(dm, "html", MediaType.TEXT_HTML); - ext(dm, "json", MediaType.APPLICATION_JSON); - ext(dm, "jsonsmile", MediaType.APPLICATION_JSON_SMILE); - ext(dm, "txt", MediaType.TEXT_PLAIN, true); - ext(dm, "xhtml", MediaType.APPLICATION_XHTML); - ext(dm, "xml", MediaType.TEXT_XML); - ext(dm, "xml", MediaType.APPLICATION_XML); - - // Add all those mappings - this.mappings.addAll(dm); - } - - /** - * Maps an extension to some metadata (media type, language or character set) to - * an extension. - * - * @param extension The extension name. - * @param metadata The metadata to map. - */ - public void addExtension(String extension, Metadata metadata) { - addExtension(extension, metadata, false); - } - - /** - * Maps an extension to some metadata (media type, language or character set) to - * an extension. - * - * @param extension The extension name. - * @param metadata The metadata to map. - * @param preferred indicates if this mapping is the preferred one. - */ - public void addExtension(String extension, Metadata metadata, boolean preferred) { - if (preferred) { - // Add the mapping at the beginning of the list - this.mappings.add(0, new MetadataExtension(extension, metadata)); - } else { - // Add the mapping at the end of the list - this.mappings.add(new MetadataExtension(extension, metadata)); - } - } - - /** - * clears the mappings for all extensions. - */ - public void clearExtensions() { - this.mappings.clear(); - } - - /** - * Creates a new extension mapping. - * - * @param extensions The extensions list to update. - * @param extension The extension name. - * @param metadata The associated metadata. - * @return The new extension mapping. - */ - private void ext(List extensions, String extension, Metadata metadata) { - ext(extensions, extension, metadata, false); - } - - /** - * Creates a new extension mapping. - * - * @param extensions The extensions list to update. - * @param extension The extension name. - * @param metadata The associated metadata. - * @param preferred indicates if this mapping is the preferred one. - * @return The new extension mapping. - */ - private void ext(List extensions, String extension, Metadata metadata, boolean preferred) { - if (preferred) { - // Add the mapping at the beginning of the list - extensions.add(0, new MetadataExtension(extension, metadata)); - } else { - // Add the mapping at the end of the list - extensions.add(new MetadataExtension(extension, metadata)); - } - } - - /** - * Return the ordered list of extension names mapped to character set. - * - * @return The ordered list of extension names mapped to character set. - */ - public List getAllCharacterSetExtensionNames() { - List result = new ArrayList(); - - for (MetadataExtension mapping : this.mappings) { - if ((mapping.getMetadata() instanceof CharacterSet) && !result.contains(mapping.getName())) { - result.add(mapping.getName()); - } - } - - return result; - } - - /** - * Returns all the character sets associated to this extension. It returns null - * if the extension was not declared. - * - * @param extension The extension name without any delimiter. - * @return The list of character sets associated to this extension. - */ - public List getAllCharacterSets(String extension) { - List result = null; - - if (extension != null) { - // Look for all registered convenient mapping. - for (MetadataExtension metadataExtension : this.mappings) { - if (extension.equals(metadataExtension.getName()) - && (metadataExtension.getMetadata() instanceof CharacterSet)) { - if (result == null) { - result = new ArrayList(); - } - - result.add(metadataExtension.getCharacterSet()); - } - } - } - - return result; - } - - /** - * Return the ordered list of extension names mapped to encodings. - * - * @return The ordered list of extension names mapped to encodings. - */ - public List getAllEncodingExtensionNames() { - List result = new ArrayList(); - - for (MetadataExtension mapping : this.mappings) { - if ((mapping.getMetadata() instanceof Encoding) && !result.contains(mapping.getName())) { - result.add(mapping.getName()); - } - } - - return result; - } - - /** - * Return the ordered list of extension names. - * - * @return The ordered list of extension names. - */ - public List getAllExtensionNames() { - List result = new ArrayList(); - - for (MetadataExtension mapping : this.mappings) { - if (!result.contains(mapping.getName())) { - result.add(mapping.getName()); - } - } - - return result; - } - - /** - * Return the ordered list of extension names mapped to languages. - * - * @return The ordered list of extension names mapped to languages. - */ - public List getAllLanguageExtensionNames() { - List result = new ArrayList(); - - for (MetadataExtension mapping : this.mappings) { - if ((mapping.getMetadata() instanceof Language) && !result.contains(mapping.getName())) { - result.add(mapping.getName()); - } - } - - return result; - } - - /** - * Returns all the languages associated to this extension. It returns null if - * the extension was not declared. - * - * @param extension The extension name without any delimiter. - * @return The list of languages associated to this extension. - */ - public List getAllLanguages(String extension) { - List result = null; - - if (extension != null) { - // Look for all registered convenient mapping. - for (MetadataExtension metadataExtension : this.mappings) { - if (extension.equals(metadataExtension.getName()) - && (metadataExtension.getMetadata() instanceof Language)) { - if (result == null) { - result = new ArrayList(); - } - - result.add(metadataExtension.getLanguage()); - } - } - } - - return result; - } - - /** - * Return the ordered list of extension names mapped to media types. - * - * @return The ordered list of extension names mapped to media types. - */ - public List getAllMediaTypeExtensionNames() { - List result = new ArrayList(); - - for (MetadataExtension mapping : this.mappings) { - if ((mapping.getMetadata() instanceof MediaType) && !result.contains(mapping.getName())) { - result.add(mapping.getName()); - } - } - - return result; - } - - /** - * Returns all the media types associated to this extension. It returns null if - * the extension was not declared. - * - * @param extension The extension name without any delimiter. - * @return The list of media type associated to this extension. - */ - public List getAllMediaTypes(String extension) { - List result = null; - - if (extension != null) { - // Look for all registered convenient mapping. - for (MetadataExtension metadataExtension : this.mappings) { - if (extension.equals(metadataExtension.getName()) - && (metadataExtension.getMetadata() instanceof MediaType)) { - if (result == null) { - result = new ArrayList(); - } - - result.add(metadataExtension.getMediaType()); - } - } - } - - return result; - } - - /** - * Returns all the metadata associated to this extension. It returns null if the - * extension was not declared. - * - * @param extension The extension name without any delimiter. - * @return The list of metadata associated to this extension. - */ - public List getAllMetadata(String extension) { - List result = null; - - if (extension != null) { - // Look for all registered convenient mapping. - for (MetadataExtension metadataExtension : this.mappings) { - if (extension.equals(metadataExtension.getName())) { - if (result == null) { - result = new ArrayList(); - } - - result.add(metadataExtension.getMetadata()); - } - } - } - - return result; - } - - /** - * Returns the character set associated to this extension. It returns null if - * the extension was not declared of it is corresponds to another type of - * medatata such as a media type. If several metadata are associated to the same - * extension then only the first matching metadata is returned. - * - * - * @param extension The extension name without any delimiter. - * @return The character set associated to this extension. - */ - public CharacterSet getCharacterSet(String extension) { - return getMetadata(extension, CharacterSet.class); - } - - /** - * Returns the default character set for textual representations. - * - * @return The default character set for textual representations. - */ - public CharacterSet getDefaultCharacterSet() { - return this.defaultCharacterSet; - } - - /** - * Returns the default encoding for representations. - * - * @return The default encoding for representations. - */ - public Encoding getDefaultEncoding() { - return this.defaultEncoding; - } - - /** - * Returns the default language for representations. - * - * @return The default language for representations. - */ - public Language getDefaultLanguage() { - return this.defaultLanguage; - } - - /** - * Returns the default media type for representations. - * - * @return The default media type for representations. - */ - public MediaType getDefaultMediaType() { - return this.defaultMediaType; - } - - /** - * Returns the encoding associated to this extension. It returns null if the - * extension was not declared or if it corresponds to another type of medatata - * such as a media type. If some metadata are associated to the same - * extension then only the first matching metadata is returned. - * - * @param extension The extension name without any delimiter. - * @return The encoding associated to this extension. - */ - public Encoding getEncoding(String extension) { - return getMetadata(extension, Encoding.class); - } - - /** - * Returns the first extension mapping to this metadata. - * - * @param metadata The metadata to find. - * @return The first extension mapping to this metadata. - */ - public String getExtension(Metadata metadata) { - if (metadata != null) { - // Look for the first registered convenient mapping. - for (final MetadataExtension metadataExtension : this.mappings) { - if (metadata.equals(metadataExtension.getMetadata())) { - return metadataExtension.getName(); - } - } - } - return null; - } - - /** - * Returns the language associated to this extension. It returns null if the - * extension was not declared of it is corresponds to another type of medatata - * such as a media type. If several metadata are associated to the same - * extension then only the first matching metadata is returned. - * - * @param extension The extension name without any delimiter. - * @return The language associated to this extension. - */ - public Language getLanguage(String extension) { - return getMetadata(extension, Language.class); - } - - /** - * Returns the mediatype associated to this extension. It returns null if the - * extension was not declared of it is corresponds to another type of medatata - * such as a language. If several metadata are associated to the same extension - * (ex: 'xml' for both 'text/xml' and 'application/xml') then only the first - * matching metadata is returned. - * - * - * @param extension The extension name without any delimiter. - * @return The media type associated to this extension. - */ - public MediaType getMediaType(String extension) { - return getMetadata(extension, MediaType.class); - } - - /** - * Returns the metadata associated to this extension. It returns null if the - * extension was not declared. If several metadata are associated to the same - * extension (ex: 'xml' for both 'text/xml' and 'application/xml') then only the - * first matching metadata is returned. - * - * @param extension The extension name without any delimiter. - * @return The metadata associated to this extension. - */ - public Metadata getMetadata(String extension) { - if (extension != null) { - // Look for the first registered convenient mapping. - for (final MetadataExtension metadataExtension : this.mappings) { - if (extension.equals(metadataExtension.getName())) { - return metadataExtension.getMetadata(); - } - } - } - - return null; - } - - /** - * Returns the metadata associated to this extension. It returns null if the - * extension was not declared or is not of the target metadata type. - * - * @param - * @param extension The extension name without any delimiter. - * @param metadataType The target metadata type. - * @return The metadata associated to this extension. - */ - public T getMetadata(String extension, Class metadataType) { - Metadata metadata = getMetadata(extension); - - if (metadata != null && metadataType.isAssignableFrom(metadata.getClass())) { - return metadataType.cast(metadata); - } - - return null; - } - - /** - * Sets the default character set for local representations. - * - * @param defaultCharacterSet The default character set for local - * representations. - */ - public void setDefaultCharacterSet(CharacterSet defaultCharacterSet) { - this.defaultCharacterSet = defaultCharacterSet; - } - - /** - * Sets the default encoding for local representations. - * - * @param defaultEncoding The default encoding for local representations. - */ - public void setDefaultEncoding(Encoding defaultEncoding) { - this.defaultEncoding = defaultEncoding; - } - - /** - * Sets the default language for local representations. - * - * @param defaultLanguage The default language for local representations. - */ - public void setDefaultLanguage(Language defaultLanguage) { - this.defaultLanguage = defaultLanguage; - } - - /** - * Sets the default media type for local representations. - * - * @param defaultMediaType The default media type for local representations. - */ - public void setDefaultMediaType(MediaType defaultMediaType) { - this.defaultMediaType = defaultMediaType; - } - + /** The default character set for textual representations. */ + private volatile CharacterSet defaultCharacterSet; + + /** The default encoding for representations. */ + private volatile Encoding defaultEncoding; + + /** The default language for representations. */ + private volatile Language defaultLanguage; + + /** The default media type for representations. */ + private volatile MediaType defaultMediaType; + + /** The list of mappings between extension names and metadata. */ + private final List mappings; + + /** + * Constructor. Sets the default language to {@link Language#ENGLISH_US}, the default encoding + * to {@link Encoding#IDENTITY} (no encoding) and the default media type to {@link + * MediaType#APPLICATION_OCTET_STREAM}. It also calls the {@link #addCommonExtensions()} method. + */ + public MetadataService() { + this.defaultCharacterSet = CharacterSet.DEFAULT; + this.defaultEncoding = Encoding.IDENTITY; + this.defaultLanguage = Language.DEFAULT; + this.defaultMediaType = MediaType.APPLICATION_OCTET_STREAM; + this.mappings = new CopyOnWriteArrayList<>(); + addCommonExtensions(); + } + + /** + * Adds a common list of associations from extensions to metadata.
+ * The list of languages extensions:
+ * + *
    + *
  • en: English + *
  • es: Spanish + *
  • fr: French + *
+ * + *
+ * The list of character set extensions:
+ * + *
    + *
  • ascii: US-ASCII + *
+ * + *
+ * The list of media type extensions:
+ * + *
    + *
  • ai: PostScript document + *
  • atom: Atom syndication document + *
  • au: AU audio file + *
  • bin: Binary file + *
  • bmp: Bitmap graphics + *
  • class: Java bytecode + *
  • css: CSS stylesheet + *
  • csv: Comma-separated Values + *
  • dat: Fixed-width Values + *
  • dib: Device-Independent Bitmap Graphics + *
  • doc: Microsoft Word document + *
  • docx: Microsoft Office Word 2007 document + *
  • docm: Office Word 2007 macro-enabled document + *
  • dotx: Office Word 2007 template + *
  • dotm: Office Word 2007 macro-enabled document template + *
  • dtd: XML Document Type Definition + *
  • eps: Encapsulated PostScript + *
  • exe: Executable File (Microsoft Corporation) + *
  • fmt: FreeMarker encoding + *
  • form: Web forms (URL encoded) + *
  • ftl: FreeMarker encoding + *
  • gif: GIF image + *
  • gwt: Java serialized object (using GWT-RPC encoder) + *
  • hqx: BinHex 4 Compressed Archive (Macintosh) + *
  • htm, html: HTML document + *
  • ico: Windows icon (Favicon) + *
  • jad: Java Application Descriptor file + *
  • jar: Java Archive + *
  • java: Java source code + *
  • jnlp: Java Web start launch file + *
  • jpe, jpeg, jpg: JPEG image + *
  • js: JavaScript document + *
  • jsf: Java Server Faces file + *
  • json: JavaScript Object Notation document + *
  • jsonsmile: JavaScript Object Notation smile document + *
  • kar: Karaoke MIDI file + *
  • latex: LaTeX document + *
  • man: Manual file + *
  • mathml: Mathml XML document + *
  • mid, midi: MIDI Audio + *
  • mov, qt: QuickTime video clip (Apple Computer, Inc.) + *
  • mp2, mp3: MPEG Audio Stream file + *
  • mp4: MPEG-4 video file + *
  • mpe, mpeg, mpg: MPEG video clip + *
  • n3: RDF N3 document + *
  • nt: RDF N-Triples document + *
  • odb: OpenDocument Database + *
  • odc: OpenDocument Chart + *
  • odf: OpenDocument Formula + *
  • odg: OpenDocument Drawing + *
  • odi: OpenDocument Image + *
  • odm: OpenDocument Master Document + *
  • odp: OpenDocument Presentation + *
  • ods: OpenDocument Spreadsheet + *
  • odt: OpenDocument Text + *
  • onetoc: Microsoft Office OneNote 2007 TOC + *
  • onetoc2: Office OneNote 2007 TOC + *
  • otg: OpenDocument Drawing Template + *
  • oth: HTML Document Template + *
  • otp: OpenDocument Presentation Template + *
  • ots: OpenDocument Spreadsheet Template + *
  • ott: OpenDocument Text Template + *
  • oxt: OpenOffice.org extension + *
  • pdf: Adobe PDF document + *
  • png: PNG image + *
  • potm: Office PowerPoint 2007 macro-enabled presentation template + *
  • potx: Office PowerPoint 2007 template + *
  • ppam: Office PowerPoint 2007 add-in + *
  • pps, ppt: Microsoft Powerpoint document + *
  • ppsm: Office PowerPoint 2007 macro-enabled slide show + *
  • ppsx: Office PowerPoint 2007 slide show + *
  • pptm: Office PowerPoint 2007 macro-enabled presentation + *
  • pptx: Microsoft Office PowerPoint 2007 presentation + *
  • ps: PostScript document + *
  • rdf: Description Framework document + *
  • rnc: Relax NG Schema document, Compact syntax + *
  • rng: Relax NG Schema document, XML syntax + *
  • rss: RSS file + *
  • rtf: Rich Text Format document + *
  • sav: SPSS Data + *
  • sit: StuffIt compressed archive file + *
  • sldm: Office PowerPoint 2007 macro-enabled slide + *
  • sldx: Office PowerPoint 2007 slide + *
  • snd: Amiga sound + *
  • sps: SPSS Script Syntax + *
  • sta: Stata data file + *
  • svg: Scalable Vector Graphics file + *
  • swf: Adobe Flash file + *
  • tar: Tape Archive file + *
  • tex: Tex file + *
  • tif, tiff: Tagged Image Format File + *
  • tsv: Tab-separated Values + *
  • txt: Plain text + *
  • ulw: MU-LAW (US telephony format) + *
  • vm: Velocity encoding + *
  • vrml: Virtual Reality Modeling Language file + *
  • vxml: VoiceXML source file + *
  • wadl: Web Application Description Language document + *
  • wav: Waveform audio + *
  • wrl: Plain text VRML file + *
  • xht, xhtml: XHTML document + *
  • xlam: Office Excel 2007 add-in + *
  • xls: Microsoft Excel document + *
  • xlsb: Office Excel 2007 binary workbook + *
  • xlsm: Office Excel 2007 macro-enabled workbook + *
  • xlsx: Microsoft Office Excel 2007 workbook + *
  • xltm: Office Excel 2007 macro-enabled workbook template + *
  • xltx: Office Excel 2007 template + *
  • xmi: XMI document + *
  • xml: XML document + *
  • xsd: W3C XML Schema document + *
  • xsl, xslt: XSL Transform file + *
  • xul: XML User Interface Language file + *
  • yaml: YAML text format + *
  • z: UNIX compressed archive file + *
  • zip: Zip archive + *
+ */ + public void addCommonExtensions() { + List dm = new ArrayList(); + + ext(dm, "en", Language.ENGLISH); + ext(dm, "es", Language.SPANISH); + ext(dm, "fr", Language.FRENCH); + + ext(dm, "ascii", CharacterSet.US_ASCII); + + ext(dm, "ai", MediaType.APPLICATION_POSTSCRIPT); + ext(dm, "atom", MediaType.APPLICATION_ATOM); + ext(dm, "atomcat", MediaType.APPLICATION_ATOMPUB_CATEGORY); + ext(dm, "atomsvc", MediaType.APPLICATION_ATOMPUB_SERVICE); + ext(dm, "au", MediaType.AUDIO_BASIC); + ext(dm, "bin", MediaType.APPLICATION_OCTET_STREAM); + ext(dm, "bmp", MediaType.IMAGE_BMP); + ext(dm, "class", MediaType.APPLICATION_JAVA); + ext(dm, "css", MediaType.TEXT_CSS); + ext(dm, "csv", MediaType.TEXT_CSV); + ext(dm, "dat", MediaType.TEXT_DAT); + ext(dm, "dib", MediaType.IMAGE_BMP); + ext(dm, "doc", MediaType.APPLICATION_WORD); + ext(dm, "docm", MediaType.APPLICATION_MSOFFICE_DOCM); + ext(dm, "docx", MediaType.APPLICATION_MSOFFICE_DOCX); + ext(dm, "dotm", MediaType.APPLICATION_MSOFFICE_DOTM); + ext(dm, "dotx", MediaType.APPLICATION_MSOFFICE_DOTX); + ext(dm, "dtd", MediaType.APPLICATION_XML_DTD); + ext(dm, "ecore", MediaType.APPLICATION_ECORE); + ext(dm, "eps", MediaType.APPLICATION_POSTSCRIPT); + ext(dm, "exe", MediaType.APPLICATION_OCTET_STREAM); + ext(dm, "fmt", Encoding.FREEMARKER); + ext(dm, "form", MediaType.APPLICATION_WWW_FORM); + ext(dm, "ftl", Encoding.FREEMARKER, true); + ext(dm, "gif", MediaType.IMAGE_GIF); + ext(dm, "gwt", MediaType.APPLICATION_JAVA_OBJECT_GWT); + ext(dm, "hqx", MediaType.APPLICATION_MAC_BINHEX40); + ext(dm, "ico", MediaType.IMAGE_ICON); + ext(dm, "jad", MediaType.TEXT_J2ME_APP_DESCRIPTOR); + ext(dm, "jar", MediaType.APPLICATION_JAVA_ARCHIVE); + ext(dm, "java", MediaType.TEXT_PLAIN); + ext(dm, "jnlp", MediaType.APPLICATION_JNLP); + ext(dm, "jpe", MediaType.IMAGE_JPEG); + ext(dm, "jpeg", MediaType.IMAGE_JPEG); + ext(dm, "jpg", MediaType.IMAGE_JPEG); + ext(dm, "js", MediaType.APPLICATION_JAVASCRIPT); + ext(dm, "jsf", MediaType.TEXT_PLAIN); + ext(dm, "kar", MediaType.AUDIO_MIDI); + ext(dm, "latex", MediaType.APPLICATION_LATEX); + ext(dm, "latin1", CharacterSet.ISO_8859_1); + ext(dm, "mac", CharacterSet.MACINTOSH); + ext(dm, "man", MediaType.APPLICATION_TROFF_MAN); + ext(dm, "mathml", MediaType.APPLICATION_MATHML); + ext(dm, "mid", MediaType.AUDIO_MIDI); + ext(dm, "midi", MediaType.AUDIO_MIDI); + ext(dm, "mov", MediaType.VIDEO_QUICKTIME); + ext(dm, "mp2", MediaType.AUDIO_MPEG); + ext(dm, "mp3", MediaType.AUDIO_MPEG); + ext(dm, "mp4", MediaType.VIDEO_MP4); + ext(dm, "mpe", MediaType.VIDEO_MPEG); + ext(dm, "mpeg", MediaType.VIDEO_MPEG); + ext(dm, "mpg", MediaType.VIDEO_MPEG); + ext(dm, "n3", MediaType.TEXT_RDF_N3); + ext(dm, "nt", MediaType.TEXT_PLAIN); + ext(dm, "odb", MediaType.APPLICATION_OPENOFFICE_ODB); + ext(dm, "odc", MediaType.APPLICATION_OPENOFFICE_ODC); + ext(dm, "odf", MediaType.APPLICATION_OPENOFFICE_ODF); + ext(dm, "odi", MediaType.APPLICATION_OPENOFFICE_ODI); + ext(dm, "odm", MediaType.APPLICATION_OPENOFFICE_ODM); + ext(dm, "odg", MediaType.APPLICATION_OPENOFFICE_ODG); + ext(dm, "odp", MediaType.APPLICATION_OPENOFFICE_ODP); + ext(dm, "ods", MediaType.APPLICATION_OPENOFFICE_ODS); + ext(dm, "odt", MediaType.APPLICATION_OPENOFFICE_ODT); + ext(dm, "onetoc", MediaType.APPLICATION_MSOFFICE_ONETOC); + ext(dm, "onetoc2", MediaType.APPLICATION_MSOFFICE_ONETOC2); + ext(dm, "otg", MediaType.APPLICATION_OPENOFFICE_OTG); + ext(dm, "oth", MediaType.APPLICATION_OPENOFFICE_OTH); + ext(dm, "otp", MediaType.APPLICATION_OPENOFFICE_OTP); + ext(dm, "ots", MediaType.APPLICATION_OPENOFFICE_OTS); + ext(dm, "ott", MediaType.APPLICATION_OPENOFFICE_OTT); + ext(dm, "oxt", MediaType.APPLICATION_OPENOFFICE_OXT); + ext(dm, "pdf", MediaType.APPLICATION_PDF); + ext(dm, "png", MediaType.IMAGE_PNG); + ext(dm, "potx", MediaType.APPLICATION_MSOFFICE_POTX); + ext(dm, "potm", MediaType.APPLICATION_MSOFFICE_POTM); + ext(dm, "ppam", MediaType.APPLICATION_MSOFFICE_PPAM); + ext(dm, "pps", MediaType.APPLICATION_POWERPOINT); + ext(dm, "ppsm", MediaType.APPLICATION_MSOFFICE_PPSM); + ext(dm, "ppsx", MediaType.APPLICATION_MSOFFICE_PPSX); + ext(dm, "ppt", MediaType.APPLICATION_POWERPOINT); + ext(dm, "pptm", MediaType.APPLICATION_MSOFFICE_PPTM); + ext(dm, "pptx", MediaType.APPLICATION_MSOFFICE_PPTX); + ext(dm, "ps", MediaType.APPLICATION_POSTSCRIPT); + ext(dm, "qt", MediaType.VIDEO_QUICKTIME); + ext(dm, "rdf", MediaType.APPLICATION_RDF_XML); + ext(dm, "rnc", MediaType.APPLICATION_RELAXNG_COMPACT); + ext(dm, "rng", MediaType.APPLICATION_RELAXNG_XML); + ext(dm, "rss", MediaType.APPLICATION_RSS); + ext(dm, "rtf", MediaType.APPLICATION_RTF); + ext(dm, "sav", MediaType.APPLICATION_SPSS_SAV); + ext(dm, "sit", MediaType.APPLICATION_STUFFIT); + ext(dm, "sldm", MediaType.APPLICATION_MSOFFICE_SLDM); + ext(dm, "sldx", MediaType.APPLICATION_MSOFFICE_SLDX); + ext(dm, "snd", MediaType.AUDIO_BASIC); + ext(dm, "sps", MediaType.APPLICATION_SPSS_SPS); + ext(dm, "sta", MediaType.APPLICATION_STATA_STA); + ext(dm, "svg", MediaType.IMAGE_SVG); + ext(dm, "swf", MediaType.APPLICATION_FLASH); + ext(dm, "tar", MediaType.APPLICATION_TAR); + ext(dm, "tex", MediaType.APPLICATION_TEX); + ext(dm, "tif", MediaType.IMAGE_TIFF); + ext(dm, "tiff", MediaType.IMAGE_TIFF); + ext(dm, "tsv", MediaType.TEXT_TSV); + ext(dm, "ulw", MediaType.AUDIO_BASIC); + ext(dm, "utf16", CharacterSet.UTF_16); + ext(dm, "utf8", CharacterSet.UTF_8); + ext(dm, "vm", Encoding.VELOCITY); + ext(dm, "vrml", MediaType.MODEL_VRML); + ext(dm, "vxml", MediaType.APPLICATION_VOICEXML); + ext(dm, "wadl", MediaType.APPLICATION_WADL); + ext(dm, "wav", MediaType.AUDIO_WAV); + ext(dm, "win", CharacterSet.WINDOWS_1252); + ext(dm, "wrl", MediaType.MODEL_VRML); + ext(dm, "xht", MediaType.APPLICATION_XHTML); + ext(dm, "xls", MediaType.APPLICATION_EXCEL); + ext(dm, "xlsx", MediaType.APPLICATION_MSOFFICE_XLSX); + ext(dm, "xlsm", MediaType.APPLICATION_MSOFFICE_XLSM); + ext(dm, "xltx", MediaType.APPLICATION_MSOFFICE_XLTX); + ext(dm, "xltm", MediaType.APPLICATION_MSOFFICE_XLTM); + ext(dm, "xlsb", MediaType.APPLICATION_MSOFFICE_XLSB); + ext(dm, "xlam", MediaType.APPLICATION_MSOFFICE_XLAM); + ext(dm, "xmi", MediaType.APPLICATION_XMI); + ext(dm, "xsd", MediaType.APPLICATION_W3C_SCHEMA); + ext(dm, "xsl", MediaType.APPLICATION_W3C_XSLT); + ext(dm, "xslt", MediaType.APPLICATION_W3C_XSLT); + ext(dm, "xul", MediaType.APPLICATION_XUL); + ext(dm, "yaml", MediaType.APPLICATION_YAML); + ext(dm, "yaml", MediaType.TEXT_YAML); + ext(dm, "z", MediaType.APPLICATION_COMPRESS); + ext(dm, "zip", MediaType.APPLICATION_ZIP); + ext(dm, "htm", MediaType.TEXT_HTML); + ext(dm, "html", MediaType.TEXT_HTML); + ext(dm, "json", MediaType.APPLICATION_JSON); + ext(dm, "jsonsmile", MediaType.APPLICATION_JSON_SMILE); + ext(dm, "txt", MediaType.TEXT_PLAIN, true); + ext(dm, "xhtml", MediaType.APPLICATION_XHTML); + ext(dm, "xml", MediaType.TEXT_XML); + ext(dm, "xml", MediaType.APPLICATION_XML); + + // Add all those mappings + this.mappings.addAll(dm); + } + + /** + * Maps an extension to some metadata (media type, language or character set) to an extension. + * + * @param extension The extension name. + * @param metadata The metadata to map. + */ + public void addExtension(String extension, Metadata metadata) { + addExtension(extension, metadata, false); + } + + /** + * Maps an extension to some metadata (media type, language or character set) to an extension. + * + * @param extension The extension name. + * @param metadata The metadata to map. + * @param preferred indicates if this mapping is the preferred one. + */ + public void addExtension(String extension, Metadata metadata, boolean preferred) { + if (preferred) { + // Add the mapping at the beginning of the list + this.mappings.add(0, new MetadataExtension(extension, metadata)); + } else { + // Add the mapping at the end of the list + this.mappings.add(new MetadataExtension(extension, metadata)); + } + } + + /** clears the mappings for all extensions. */ + public void clearExtensions() { + this.mappings.clear(); + } + + /** + * Creates a new extension mapping. + * + * @param extensions The extensions list to update. + * @param extension The extension name. + * @param metadata The associated metadata. + */ + private void ext(List extensions, String extension, Metadata metadata) { + ext(extensions, extension, metadata, false); + } + + /** + * Creates a new extension mapping. + * + * @param extensions The extensions list to update. + * @param extension The extension name. + * @param metadata The associated metadata. + * @param preferred indicates if this mapping is the preferred one. + */ + private void ext( + List extensions, + String extension, + Metadata metadata, + boolean preferred) { + if (preferred) { + // Add the mapping at the beginning of the list + extensions.add(0, new MetadataExtension(extension, metadata)); + } else { + // Add the mapping at the end of the list + extensions.add(new MetadataExtension(extension, metadata)); + } + } + + /** + * Return the ordered list of extension names mapped to a character set. + * + * @return The ordered list of extension names mapped to a character set. + */ + public List getAllCharacterSetExtensionNames() { + List result = new ArrayList(); + + for (MetadataExtension mapping : this.mappings) { + if ((mapping.getMetadata() instanceof CharacterSet) + && !result.contains(mapping.getName())) { + result.add(mapping.getName()); + } + } + + return result; + } + + /** + * Returns all the character sets associated with this extension. It returns null if the + * extension was not declared. + * + * @param extension The extension name without any delimiter. + * @return The list of character sets associated with this extension. + */ + public List getAllCharacterSets(String extension) { + List result = null; + + if (extension != null) { + // Look for all registered convenient mapping. + for (MetadataExtension metadataExtension : this.mappings) { + if (extension.equals(metadataExtension.getName()) + && (metadataExtension.getMetadata() instanceof CharacterSet)) { + if (result == null) { + result = new ArrayList<>(); + } + + result.add(metadataExtension.getCharacterSet()); + } + } + } + + return result; + } + + /** + * Return the ordered list of extension names mapped to encodings. + * + * @return The ordered list of extension names mapped to encodings. + */ + public List getAllEncodingExtensionNames() { + List result = new ArrayList(); + + for (MetadataExtension mapping : this.mappings) { + if ((mapping.getMetadata() instanceof Encoding) + && !result.contains(mapping.getName())) { + result.add(mapping.getName()); + } + } + + return result; + } + + /** + * Return the ordered list of extension names. + * + * @return The ordered list of extension names. + */ + public List getAllExtensionNames() { + List result = new ArrayList(); + + for (MetadataExtension mapping : this.mappings) { + if (!result.contains(mapping.getName())) { + result.add(mapping.getName()); + } + } + + return result; + } + + /** + * Return the ordered list of extension names mapped to languages. + * + * @return The ordered list of extension names mapped to languages. + */ + public List getAllLanguageExtensionNames() { + List result = new ArrayList(); + + for (MetadataExtension mapping : this.mappings) { + if ((mapping.getMetadata() instanceof Language) + && !result.contains(mapping.getName())) { + result.add(mapping.getName()); + } + } + + return result; + } + + /** + * Returns all the languages associated with this extension. It returns null if the extension + * was not declared. + * + * @param extension The extension name without any delimiter. + * @return The list of languages associated with this extension. + */ + public List getAllLanguages(String extension) { + List result = null; + + if (extension != null) { + // Look for all registered convenient mapping. + for (MetadataExtension metadataExtension : this.mappings) { + if (extension.equals(metadataExtension.getName()) + && (metadataExtension.getMetadata() instanceof Language)) { + if (result == null) { + result = new ArrayList<>(); + } + + result.add(metadataExtension.getLanguage()); + } + } + } + + return result; + } + + /** + * Return the ordered list of extension names mapped to media types. + * + * @return The ordered list of extension names mapped to media types. + */ + public List getAllMediaTypeExtensionNames() { + List result = new ArrayList<>(); + + for (MetadataExtension mapping : this.mappings) { + if ((mapping.getMetadata() instanceof MediaType) + && !result.contains(mapping.getName())) { + result.add(mapping.getName()); + } + } + + return result; + } + + /** + * Returns all the media types associated with this extension. It returns null if the extension + * was not declared. + * + * @param extension The extension name without any delimiter. + * @return The list of media type associated with this extension. + */ + public List getAllMediaTypes(String extension) { + List result = null; + + if (extension != null) { + // Look for all registered convenient mapping. + for (MetadataExtension metadataExtension : this.mappings) { + if (extension.equals(metadataExtension.getName()) + && (metadataExtension.getMetadata() instanceof MediaType)) { + if (result == null) { + result = new ArrayList<>(); + } + + result.add(metadataExtension.getMediaType()); + } + } + } + + return result; + } + + /** + * Returns all the metadata associated with this extension. It returns null if the extension was + * not declared. + * + * @param extension The extension name without any delimiter. + * @return The list of metadata associated with this extension. + */ + public List getAllMetadata(String extension) { + List result = null; + + if (extension != null) { + // Look for all registered convenient mapping. + for (MetadataExtension metadataExtension : this.mappings) { + if (extension.equals(metadataExtension.getName())) { + if (result == null) { + result = new ArrayList(); + } + + result.add(metadataExtension.getMetadata()); + } + } + } + + return result; + } + + /** + * Returns the character set associated with this extension. It returns null if the extension + * was not declared of it is corresponding to another type of metadata such as a media type. If + * some metadata is associated with the same extension, then only the first matching metadata + * is returned. + * + * @param extension The extension name without any delimiter. + * @return The character set associated with this extension. + */ + public CharacterSet getCharacterSet(String extension) { + return getMetadata(extension, CharacterSet.class); + } + + /** + * Returns the default character set for textual representations. + * + * @return The default character set for textual representations. + */ + public CharacterSet getDefaultCharacterSet() { + return this.defaultCharacterSet; + } + + /** + * Returns the default encoding for representations. + * + * @return The default encoding for representations. + */ + public Encoding getDefaultEncoding() { + return this.defaultEncoding; + } + + /** + * Returns the default language for representations. + * + * @return The default language for representations. + */ + public Language getDefaultLanguage() { + return this.defaultLanguage; + } + + /** + * Returns the default media type for representations. + * + * @return The default media type for representations. + */ + public MediaType getDefaultMediaType() { + return this.defaultMediaType; + } + + /** + * Returns the encoding associated with this extension. It returns null if the extension was not + * declared or if it corresponds to another type of metadata such as a media type. If some + * metadata is associated with the same extension, then only the first matching metadata is + * returned. + * + * @param extension The extension name without any delimiter. + * @return The encoding associated with this extension. + */ + public Encoding getEncoding(String extension) { + return getMetadata(extension, Encoding.class); + } + + /** + * Returns the first extension mapping to this metadata. + * + * @param metadata The metadata to find. + * @return The first extension mapping to this metadata. + */ + public String getExtension(Metadata metadata) { + if (metadata != null) { + // Look for the first registered convenient mapping. + for (final MetadataExtension metadataExtension : this.mappings) { + if (metadata.equals(metadataExtension.getMetadata())) { + return metadataExtension.getName(); + } + } + } + return null; + } + + /** + * Returns the language associated with this extension. It returns null if the extension was not + * declared of it is corresponding to another type of metadata such as a media type. If some + * metadata is associated with the same extension, then only the first matching metadata is + * returned. + * + * @param extension The extension name without any delimiter. + * @return The language associated with this extension. + */ + public Language getLanguage(String extension) { + return getMetadata(extension, Language.class); + } + + /** + * Returns the media type associated with this extension. It returns null if the extension was + * not declared of it is corresponding to another type of metadata such as a language. If some + * metadata is associated with the same extension (ex: 'xml' for both 'text/xml' and + * 'application/xml'), then only the first matching metadata is returned. + * + * @param extension The extension name without any delimiter. + * @return The media type associated with this extension. + */ + public MediaType getMediaType(String extension) { + return getMetadata(extension, MediaType.class); + } + + /** + * Returns the metadata associated with this extension. It returns null if the extension was not + * declared. If some metadata is associated with the same extension (ex: 'xml' for both + * 'text/xml' and 'application/xml'), then only the first matching metadata is returned. + * + * @param extension The extension name without any delimiter. + * @return The metadata associated with this extension. + */ + public Metadata getMetadata(String extension) { + if (extension != null) { + // Look for the first registered convenient mapping. + for (final MetadataExtension metadataExtension : this.mappings) { + if (extension.equals(metadataExtension.getName())) { + return metadataExtension.getMetadata(); + } + } + } + + return null; + } + + /** + * Returns the metadata associated with this extension. It returns null if the extension was not + * declared or is not of the target metadata type. + * + * @param + * @param extension The extension name without any delimiter. + * @param metadataType The target metadata type. + * @return The metadata associated with this extension. + */ + public T getMetadata(String extension, Class metadataType) { + Metadata metadata = getMetadata(extension); + + if (metadata != null && metadataType.isAssignableFrom(metadata.getClass())) { + return metadataType.cast(metadata); + } + + return null; + } + + /** + * Sets the default character set for local representations. + * + * @param defaultCharacterSet The default character set for local representations. + */ + public void setDefaultCharacterSet(CharacterSet defaultCharacterSet) { + this.defaultCharacterSet = defaultCharacterSet; + } + + /** + * Sets the default encoding for local representations. + * + * @param defaultEncoding The default encoding for local representations. + */ + public void setDefaultEncoding(Encoding defaultEncoding) { + this.defaultEncoding = defaultEncoding; + } + + /** + * Sets the default language for local representations. + * + * @param defaultLanguage The default language for local representations. + */ + public void setDefaultLanguage(Language defaultLanguage) { + this.defaultLanguage = defaultLanguage; + } + + /** + * Sets the default media type for local representations. + * + * @param defaultMediaType The default media type for local representations. + */ + public void setDefaultMediaType(MediaType defaultMediaType) { + this.defaultMediaType = defaultMediaType; + } } diff --git a/org.restlet/src/main/java/org/restlet/service/RangeService.java b/org.restlet/src/main/java/org/restlet/service/RangeService.java index 816bf2ffe4..c6402b2f5c 100644 --- a/org.restlet/src/main/java/org/restlet/service/RangeService.java +++ b/org.restlet/src/main/java/org/restlet/service/RangeService.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; import org.restlet.Context; @@ -14,35 +13,31 @@ import org.restlet.routing.Filter; /** - * Application service automatically exposes ranges of response entities. This - * allows resources to not care for requested ranges and return full - * representations that will then be transparently wrapped in partial - * representations by this service, allowing the client to benefit from partial + * Application service automatically exposes ranges of response entities. This allows resources to + * not care for requested ranges and return full representations that will then be transparently + * wrapped in partial representations by this service, allowing the client to benefit from partial * downloads. - * + * * @author Jerome Louvel */ public class RangeService extends Service { - /** - * Constructor. - */ - public RangeService() { - super(); - } - - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - */ - public RangeService(boolean enabled) { - super(enabled); - } + /** Constructor. */ + public RangeService() { + super(); + } - @Override - public Filter createInboundFilter(Context context) { - return new RangeFilter(context); - } + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + */ + public RangeService(boolean enabled) { + super(enabled); + } + @Override + public Filter createInboundFilter(Context context) { + return new RangeFilter(context); + } } diff --git a/org.restlet/src/main/java/org/restlet/service/Service.java b/org.restlet/src/main/java/org/restlet/service/Service.java index bd15bd04c0..41088204d6 100644 --- a/org.restlet/src/main/java/org/restlet/service/Service.java +++ b/org.restlet/src/main/java/org/restlet/service/Service.java @@ -1,141 +1,136 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; import org.restlet.Context; /** - * Generic service associated to a component or an application. The life cycle - * of a service is tightly related to the one of the associated component or - * application.
+ * Generic service associated with a component or an application. The life cycle of a service is + * tightly related to the one of the associated component or application.
*
- * If you want to use a specific service, you can always disable it before it is - * actually started via the {@link #setEnabled(boolean)} method. - * + * If you want to use a specific service, you can always disable it before it is actually started + * via the {@link #setEnabled(boolean)} method. + * * @author Jerome Louvel */ public abstract class Service { - /** The context. */ - private volatile Context context; - - /** Indicates if the service has been enabled. */ - private volatile boolean enabled; - - /** Indicates if the service was started. */ - private volatile boolean started; - - /** - * Constructor. Enables the service by default. - */ - public Service() { - this(true); - } - - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - */ - public Service(boolean enabled) { - this.context = null; - this.enabled = enabled; - } - - /** - * Create the filter that should be invoked for incoming calls. - * - * @param context The current context. - * @return The new filter or null. - */ - public org.restlet.routing.Filter createInboundFilter(org.restlet.Context context) { - return null; - } - - /** - * Create the filter that should be invoked for outgoing calls. - * - * @param context The current context. - * @return The new filter or null. - * @see Context#getClientDispatcher() - */ - public org.restlet.routing.Filter createOutboundFilter(org.restlet.Context context) { - return null; - } - - /** - * Returns the context. - * - * @return The context. - */ - public Context getContext() { - return this.context; - } - - /** - * Indicates if the service should be enabled. - * - * @return True if the service should be enabled. - */ - public boolean isEnabled() { - return this.enabled; - } - - /** - * Indicates if the service is started. - * - * @return True if the service is started. - */ - public boolean isStarted() { - return this.started; - } - - /** - * Indicates if the service is stopped. - * - * @return True if the service is stopped. - */ - public boolean isStopped() { - return !this.started; - } - - /** - * Sets the context. - * - * @param context The context. - */ - public void setContext(Context context) { - this.context = context; - } - - /** - * Indicates if the service should be enabled. - * - * @param enabled True if the service should be enabled. - */ - public synchronized void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - /** Starts the Restlet. */ - public synchronized void start() throws Exception { - if (isEnabled()) { - this.started = true; - } - } - - /** Stops the Restlet. */ - public synchronized void stop() throws Exception { - if (isEnabled()) { - this.started = false; - } - } - + /** The context. */ + private volatile Context context; + + /** Indicates if the service has been enabled. */ + private volatile boolean enabled; + + /** Indicates if the service was started. */ + private volatile boolean started; + + /** Constructor. Enables the service by default. */ + public Service() { + this(true); + } + + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + */ + public Service(boolean enabled) { + this.context = null; + this.enabled = enabled; + } + + /** + * Create the filter that should be invoked for incoming calls. + * + * @param context The current context. + * @return The new filter or null. + */ + public org.restlet.routing.Filter createInboundFilter(org.restlet.Context context) { + return null; + } + + /** + * Create the filter that should be invoked for outgoing calls. + * + * @param context The current context. + * @return The new filter or null. + * @see Context#getClientDispatcher() + */ + public org.restlet.routing.Filter createOutboundFilter(org.restlet.Context context) { + return null; + } + + /** + * Returns the context. + * + * @return The context. + */ + public Context getContext() { + return this.context; + } + + /** + * Indicates if the service should be enabled. + * + * @return True if the service should be enabled. + */ + public boolean isEnabled() { + return this.enabled; + } + + /** + * Indicates if the service is started. + * + * @return True if the service is started. + */ + public boolean isStarted() { + return this.started; + } + + /** + * Indicates if the service is stopped. + * + * @return True if the service is stopped. + */ + public boolean isStopped() { + return !this.started; + } + + /** + * Sets the context. + * + * @param context The context. + */ + public void setContext(Context context) { + this.context = context; + } + + /** + * Indicates if the service should be enabled. + * + * @param enabled True if the service should be enabled. + */ + public synchronized void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** Starts the Restlet. */ + public synchronized void start() throws Exception { + if (isEnabled()) { + this.started = true; + } + } + + /** Stops the Restlet. */ + public synchronized void stop() throws Exception { + if (isEnabled()) { + this.started = false; + } + } } diff --git a/org.restlet/src/main/java/org/restlet/service/StatusService.java b/org.restlet/src/main/java/org/restlet/service/StatusService.java index 0d94b40066..9097b0826a 100644 --- a/org.restlet/src/main/java/org/restlet/service/StatusService.java +++ b/org.restlet/src/main/java/org/restlet/service/StatusService.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; import org.restlet.Application; import org.restlet.Context; import org.restlet.Request; @@ -21,342 +23,345 @@ import org.restlet.resource.Resource; import org.restlet.resource.ResourceException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - /** - * Service to handle error statuses. If an exception is thrown within your - * application or Restlet code, it will be intercepted by this service if it is - * enabled.
+ * Service to handle error statuses. If an exception is thrown within your application or Restlet + * code, it will be intercepted by this service if it is enabled.
*
- * When an exception or an error is caught, the - * {@link #getStatus(Throwable, Request, Response)} method is first invoked to - * obtain the status that you want to set on the response. If this method isn't - * overridden or returns null, the {@link Status#SERVER_ERROR_INTERNAL} constant - * will be set by default.
+ * When an exception or an error is caught, the {@link #toStatus(Throwable, Request, Response)} + * method is first invoked to obtain the status that you want to set on the response. If this method + * isn't overridden or returns null, the {@link Status#SERVER_ERROR_INTERNAL} constant will be set + * by default.
*
- * Also, when the status of a response returned is an error status (see - * {@link Status#isError()}, the - * {@link #getRepresentation(Status, Request, Response)} method is then invoked - * to give your service a chance to override the default error page.
+ * Also, when the status of a response returned is an error status (see {@link Status#isError()}, + * the {@link #toRepresentation(Status, Request, Response)} method is then invoked to give your + * service a chance to override the default error page.
*
- * If you want to customize the default behavior, you need to create a subclass - * of StatusService that overrides some or all of the methods mentioned above. - * Then, just create a instance of your class and set it on your Component or - * Application via the setStatusService() methods.
+ * If you want to customize the default behavior, you need to create a subclass of StatusService + * that overrides some or all of the methods mentioned above. Then, just create a instance of your + * class and set it on your Component or Application via the setStatusService() methods.
*
- * In case the response's entity has already been set, the status service does - * not generate an error representation. You can turn off this default behavior - * by calling the {@link #setOverwriting(boolean)} method. - * + * In case the response's entity has already been set, the status service does not generate an error + * representation. You can turn off this default behavior by calling the {@link + * #setOverwriting(boolean)} method. + * * @author Jerome Louvel */ public class StatusService extends Service { - /** The service used to select the preferred variant. */ - private volatile ConnegService connegService; - - /** The email address to contact in case of error. */ - private volatile String contactEmail; - - /** The service used to convert between status/throwable and representation. */ - private volatile ConverterService converterService; - - /** The home URI to propose in case of error. */ - private volatile Reference homeRef; - - /** The service used to select the preferred variant. */ - private volatile MetadataService metadataService; - - /** True if an existing entity should be overwritten. */ - private volatile boolean overwriting; - - /** - * Constructor. By default, it creates the necessary services. - */ - public StatusService() { - this(true); - } - - /** - * Constructor. By default, it creates the necessary services. - * - * @param enabled True if the service has been enabled. - * - */ - public StatusService(boolean enabled) { - this(enabled, new ConverterService(), new MetadataService(), new ConnegService()); - } - - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - * @param converterService The service used to convert between status/throwable - * and representation. - * @param metadataService The service used to select the preferred variant. - * @param connegService The service used to select the preferred variant. - */ - public StatusService(boolean enabled, ConverterService converterService, MetadataService metadataService, - ConnegService connegService) { - super(enabled); - this.converterService = converterService; - this.metadataService = metadataService; - this.connegService = connegService; - this.contactEmail = null; - this.homeRef = new Reference("/"); - this.overwriting = false; - } - - @Override - public org.restlet.routing.Filter createInboundFilter(Context context) { - return new org.restlet.engine.application.StatusFilter(context, this); - } - - /** - * Returns the service used to select the preferred variant. - * - * @return The service used to select the preferred variant. - */ - public ConnegService getConnegService() { - return connegService; - } - - /** - * Returns the email address to contact in case of error. This is typically used - * when creating the status representations. - * - * @return The email address to contact in case of error. - */ - public String getContactEmail() { - return this.contactEmail; - } - - /** - * Returns the service used to convert between status/throwable and - * representation. - * - * @return The service used to convert between status/throwable and - * representation. - */ - public ConverterService getConverterService() { - return converterService; - } - - /** - * Returns the home URI to propose in case of error. - * - * @return The home URI to propose in case of error. - */ - public Reference getHomeRef() { - return this.homeRef; - } - - /** - * Returns the service used to select the preferred variant. - * - * @return The service used to select the preferred variant. - */ - public MetadataService getMetadataService() { - return metadataService; - } - - /** - * Indicates if an existing entity should be overwritten. False by default. - * - * @return True if an existing entity should be overwritten. - */ - public boolean isOverwriting() { - return this.overwriting; - } - - /** - * Sets the service used to select the preferred variant. - * - * @param connegService The service used to select the preferred variant. - */ - public void setConnegService(ConnegService connegService) { - this.connegService = connegService; - } - - /** - * Sets the email address to contact in case of error. This is typically used - * when creating the status representations. - * - * @param contactEmail The email address to contact in case of error. - */ - public void setContactEmail(String contactEmail) { - this.contactEmail = contactEmail; - } - - /** - * Sets the service used to convert between status/throwable and representation. - * - * @param converterService The service used to convert between status/throwable - * and representation. - */ - public void setConverterService(ConverterService converterService) { - this.converterService = converterService; - } - - /** - * Sets the home URI to propose in case of error. - * - * @param homeRef The home URI to propose in case of error. - */ - public void setHomeRef(Reference homeRef) { - this.homeRef = homeRef; - } - - /** - * Sets the service used to select the preferred variant. - * - * @param metadataService The service used to select the preferred variant. - */ - public void setMetadataService(MetadataService metadataService) { - this.metadataService = metadataService; - } - - /** - * Indicates if an existing entity should be overwritten. - * - * @param overwriting True if an existing entity should be overwritten. - */ - public void setOverwriting(boolean overwriting) { - this.overwriting = overwriting; - } - - /** - * Returns a representation for the given status. In order to customize the - * default representation, this method can be overridden. It returns a - * {@link org.restlet.data.Status} representation by default or a - * {@link java.lang.Throwable} representation if the throwable is annotated with - * {@link org.restlet.resource.Status}. - * - * @param status The status to represent. - * @param request The request handled. - * @param response The response updated. - * @return The representation of the given status. - */ - public Representation toRepresentation(Status status, Request request, Response response) { - Representation result = null; - - // Do content negotiation for status - if (converterService != null && connegService != null && metadataService != null) { - Object representationObject = null; - - // Serialize exception if any and if {@link org.restlet.resource.Status} annotation asks for it - Throwable cause = status.getThrowable(); - - if (cause != null) { - org.restlet.engine.resource.ThrowableAnnotationInfo tai = org.restlet.engine.resource.AnnotationUtils - .getInstance().getThrowableAnnotationInfo(cause.getClass()); - - if (tai != null && tai.isSerializable()) { - if (Application.getCurrent() != null && !Application.getCurrent().isDebugging()) { - // We clear the stack trace to prevent technical - // information leak - cause.setStackTrace(new StackTraceElement[] {}); - - if (cause.getCause() != null) { - Context.getCurrentLogger().log(Level.WARNING, - "The cause of the exception should be null except in debug mode"); - } - } - - representationObject = cause; - } - } - - try { - // Default representation match with the status properties - if (representationObject == null) { - representationObject = new StatusInfo(status, getContactEmail(), getHomeRef().toString()); - } - - List variants = org.restlet.engine.converter.ConverterUtils - .getVariants(representationObject.getClass(), null); - if (variants == null) { - variants = new ArrayList<>(); - } - - Variant variant = connegService.getPreferredVariant(variants, request, metadataService); - result = converterService.toRepresentation(representationObject, variant); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, - "Could not serialize throwable class " + ((cause == null) ? null : cause.getClass()), e); - } - } - - return result; - } - - /** - * Returns a representation for the given status.
- * In order to customize the default representation, this method can be - * overridden. By default, it invokes - * {@link #toRepresentation(Status, Request, Response)} - * - * @param status The status to represent. - * @param resource The parent resource. - * @return The representation of the given status. - */ - public Representation toRepresentation(Status status, Resource resource) { - return toRepresentation(status, resource.getRequest(), resource.getResponse()); - } - - /** - * Returns a status for a given exception or error. By default it unwraps the - * status of {@link ResourceException}. For other exceptions or errors, it - * returns an {@link Status#SERVER_ERROR_INTERNAL} status.
- *
- * In order to customize the default behavior, this method can be overridden. - * - * @param throwable The exception or error caught. - * @param request The request handled. - * @param response The response updated. - * @return The representation of the given status. - */ - public Status toStatus(Throwable throwable, Request request, Response response) { - Status result; - - Status defaultStatus = Status.SERVER_ERROR_INTERNAL; - Throwable t = throwable; - - // If throwable is a ResourceException, use its status and the cause. - if (throwable instanceof ResourceException) { - defaultStatus = ((ResourceException) throwable).getStatus(); - - if (throwable.getCause() != null && throwable.getCause() != throwable) { - t = throwable.getCause(); - } - } - - // look for Status annotation - org.restlet.engine.resource.ThrowableAnnotationInfo tai = org.restlet.engine.resource.AnnotationUtils - .getInstance().getThrowableAnnotationInfo(t.getClass()); - - if (tai != null) { - result = new Status(tai.getStatus(), t); - } else { - result = new Status(defaultStatus, t); - } - - return result; - } - - /** - * Returns a status for a given exception or error. By default it returns an - * {@link Status#SERVER_ERROR_INTERNAL} status and logs a severe message.
- * In order to customize the default behavior, this method can be overridden. - * - * @param throwable The exception or error caught. - * @param resource The parent resource. - * @return The representation of the given status. - */ - public Status toStatus(Throwable throwable, Resource resource) { - return toStatus(throwable, (resource == null) ? null : resource.getRequest(), - (resource == null) ? null : resource.getResponse()); - } + /** The service used to select the preferred variant. */ + private volatile ConnegService connegService; + + /** The email address to contact in case of error. */ + private volatile String contactEmail; + + /** The service used to convert between status/throwable and representation. */ + private volatile ConverterService converterService; + + /** The home URI to propose in case of error. */ + private volatile Reference homeRef; + + /** The service used to select the preferred variant. */ + private volatile MetadataService metadataService; + + /** True if an existing entity should be overwritten. */ + private volatile boolean overwriting; + + /** Constructor. By default, it creates the necessary services. */ + public StatusService() { + this(true); + } + + /** + * Constructor. By default, it creates the necessary services. + * + * @param enabled True if the service has been enabled. + */ + public StatusService(boolean enabled) { + this(enabled, new ConverterService(), new MetadataService(), new ConnegService()); + } + + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + * @param converterService The service used to convert between status/throwable and + * representation. + * @param metadataService The service used to select the preferred variant. + * @param connegService The service used to select the preferred variant. + */ + public StatusService( + boolean enabled, + ConverterService converterService, + MetadataService metadataService, + ConnegService connegService) { + super(enabled); + this.converterService = converterService; + this.metadataService = metadataService; + this.connegService = connegService; + this.contactEmail = null; + this.homeRef = new Reference("/"); + this.overwriting = false; + } + + @Override + public org.restlet.routing.Filter createInboundFilter(Context context) { + return new org.restlet.engine.application.StatusFilter(context, this); + } + + /** + * Returns the service used to select the preferred variant. + * + * @return The service used to select the preferred variant. + */ + public ConnegService getConnegService() { + return connegService; + } + + /** + * Returns the email address to contact in case of error. This is typically used when creating + * the status representations. + * + * @return The email address to contact in case of error. + */ + public String getContactEmail() { + return this.contactEmail; + } + + /** + * Returns the service used to convert between status/throwable and representation. + * + * @return The service used to convert between status/throwable and representation. + */ + public ConverterService getConverterService() { + return converterService; + } + + /** + * Returns the home URI to propose in case of error. + * + * @return The home URI to propose in case of error. + */ + public Reference getHomeRef() { + return this.homeRef; + } + + /** + * Returns the service used to select the preferred variant. + * + * @return The service used to select the preferred variant. + */ + public MetadataService getMetadataService() { + return metadataService; + } + + /** + * Indicates if an existing entity should be overwritten. False by default. + * + * @return True if an existing entity should be overwritten. + */ + public boolean isOverwriting() { + return this.overwriting; + } + + /** + * Sets the service used to select the preferred variant. + * + * @param connegService The service used to select the preferred variant. + */ + public void setConnegService(ConnegService connegService) { + this.connegService = connegService; + } + + /** + * Sets the email address to contact in case of error. This is typically used when creating the + * status representations. + * + * @param contactEmail The email address to contact in case of error. + */ + public void setContactEmail(String contactEmail) { + this.contactEmail = contactEmail; + } + + /** + * Sets the service used to convert between status/throwable and representation. + * + * @param converterService The service used to convert between status/throwable and + * representation. + */ + public void setConverterService(ConverterService converterService) { + this.converterService = converterService; + } + + /** + * Sets the home URI to propose in case of error. + * + * @param homeRef The home URI to propose in case of error. + */ + public void setHomeRef(Reference homeRef) { + this.homeRef = homeRef; + } + + /** + * Sets the service used to select the preferred variant. + * + * @param metadataService The service used to select the preferred variant. + */ + public void setMetadataService(MetadataService metadataService) { + this.metadataService = metadataService; + } + + /** + * Indicates if an existing entity should be overwritten. + * + * @param overwriting True if an existing entity should be overwritten. + */ + public void setOverwriting(boolean overwriting) { + this.overwriting = overwriting; + } + + /** + * Returns a representation for the given status. To customize the default representation, this + * method can be overridden. It returns a {@link org.restlet.data.Status} representation by + * default or a {@link java.lang.Throwable} representation if the throwable is annotated with + * {@link org.restlet.resource.Status}. + * + * @param status The status to represent. + * @param request The request handled. + * @param response The response updated. + * @return The representation of the given status. + */ + public Representation toRepresentation(Status status, Request request, Response response) { + Representation result = null; + + // Do content negotiation for status + if (converterService != null && connegService != null && metadataService != null) { + Object representationObject = null; + + // Serialize exception if any and if {@link org.restlet.resource.Status} annotation asks + // for it + Throwable cause = status.getThrowable(); + + if (cause != null) { + org.restlet.engine.resource.ThrowableAnnotationInfo tai = + org.restlet.engine.resource.AnnotationUtils.getInstance() + .getThrowableAnnotationInfo(cause.getClass()); + + if (tai != null && tai.isSerializable()) { + if (Application.getCurrent() != null + && !Application.getCurrent().isDebugging()) { + // We clear the stack trace to prevent technical + // information leak + cause.setStackTrace(new StackTraceElement[] {}); + + if (cause.getCause() != null) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "The cause of the exception should be null except in debug mode"); + } + } + + representationObject = cause; + } + } + + try { + // Default representation match with the status properties + if (representationObject == null) { + representationObject = + new StatusInfo(status, getContactEmail(), getHomeRef().toString()); + } + + List variants = + org.restlet.engine.converter.ConverterUtils.getVariants( + representationObject.getClass(), null); + if (variants == null) { + variants = new ArrayList<>(); + } + + Variant variant = + connegService.getPreferredVariant(variants, request, metadataService); + result = converterService.toRepresentation(representationObject, variant); + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Could not serialize throwable class " + + ((cause == null) ? null : cause.getClass()), + e); + } + } + + return result; + } + + /** + * Returns a representation for the given status.
+ * To customize the default representation, this method can be overridden. By default, it + * invokes {@link #toRepresentation(Status, Request, Response)} + * + * @param status The status to represent. + * @param resource The parent resource. + * @return The representation of the given status. + */ + public Representation toRepresentation(Status status, Resource resource) { + return toRepresentation(status, resource.getRequest(), resource.getResponse()); + } + + /** + * Returns a status for a given exception or error. By default, it unwraps the status of {@link + * ResourceException}. For other exceptions or errors, it returns an {@link + * Status#SERVER_ERROR_INTERNAL} status.
+ *
+ * To customize the default behavior, this method can be overridden. + * + * @param throwable The exception or error caught. + * @param request The request handled. + * @param response The response updated. + * @return The representation of the given status. + */ + public Status toStatus(Throwable throwable, Request request, Response response) { + Status result; + + Status defaultStatus = Status.SERVER_ERROR_INTERNAL; + Throwable t = throwable; + + // If throwable is a ResourceException, use its status and the cause. + if (throwable instanceof ResourceException) { + defaultStatus = ((ResourceException) throwable).getStatus(); + + if (throwable.getCause() != null && throwable.getCause() != throwable) { + t = throwable.getCause(); + } + } + + // look for Status annotation + org.restlet.engine.resource.ThrowableAnnotationInfo tai = + org.restlet.engine.resource.AnnotationUtils.getInstance() + .getThrowableAnnotationInfo(t.getClass()); + + if (tai != null) { + result = new Status(tai.getStatus(), t); + } else { + result = new Status(defaultStatus, t); + } + + return result; + } + + /** + * Returns a status for a given exception or error. By default, it returns an {@link + * Status#SERVER_ERROR_INTERNAL} status and logs a severe message.
+ * To customize the default behavior, this method can be overridden. + * + * @param throwable The exception or error caught. + * @param resource The parent resource. + * @return The representation of the given status. + */ + public Status toStatus(Throwable throwable, Resource resource) { + return toStatus( + throwable, + (resource == null) ? null : resource.getRequest(), + (resource == null) ? null : resource.getResponse()); + } } diff --git a/org.restlet/src/main/java/org/restlet/service/TaskService.java b/org.restlet/src/main/java/org/restlet/service/TaskService.java index a11c717c9e..6c59becc4b 100644 --- a/org.restlet/src/main/java/org/restlet/service/TaskService.java +++ b/org.restlet/src/main/java/org/restlet/service/TaskService.java @@ -1,14 +1,28 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; import org.restlet.Application; import org.restlet.Context; import org.restlet.Response; @@ -16,652 +30,626 @@ import org.restlet.engine.util.ContextualRunnable; import org.restlet.routing.VirtualHost; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.*; -import java.util.logging.Level; - /** - * Application service capable of running and scheduling tasks asynchronously. - * The service instance returned will not invoke the runnable task in the - * current thread.
+ * Application service capable of running and scheduling tasks asynchronously. The service instance + * returned will not invoke the runnable task in the current thread.
*
- * In addition to allowing pooling, this method will ensure that the threads - * executing the tasks will have the thread local variables copied from the - * calling thread. This will ensure that call to static methods like - * {@link Application#getCurrent()} still work.
+ * In addition to allowing pooling, this method will ensure that the threads executing the tasks + * will have the thread local variables copied from the calling thread. This will ensure that call + * to static methods like {@link Application#getCurrent()} still work.
*
- * Also, note that this executor service will be shared among all Restlets and - * Resources that are part of your context. In general this context corresponds - * to a parent Application's context. If you want to have your own service - * instance, you can use the {@link TaskService#wrap(ScheduledExecutorService)} - * method to ensure that thread local variables are correctly set. - * + * Also, note that this executor service will be shared among all Restlets and Resources that are + * part of your context. In general this context corresponds to a parent Application's context. If + * you want to have your own service instance, you can use the {@link + * TaskService#wrap(ScheduledExecutorService)} method to ensure that thread local variables are + * correctly set. + * * @author Jerome Louvel * @author Doug Lea (docs of ExecutorService in public domain) * @author Tim Peierls */ public class TaskService extends Service implements ScheduledExecutorService { - /** - * The default thread factory. - * - * @author Jerome Louvel - * @author Tim Peierls - */ - private static class RestletThreadFactory implements ThreadFactory { - - /** - * Indicates whether or not the thread is a daemon thread. True by default. - */ - private boolean daemon; - - final ThreadFactory factory = Executors.defaultThreadFactory(); - - public RestletThreadFactory(boolean daemon) { - this.daemon = daemon; - } - - public Thread newThread(Runnable runnable) { - Thread t = factory.newThread(runnable); - - // Default factory is documented as producing names of the - // form "pool-N-thread-M". - t.setName(t.getName().replaceFirst("pool", "restlet")); - t.setDaemon(daemon); - return t; - } - } - - /** - * Wraps a JDK executor service to ensure that the threads executing the tasks - * will have the thread local variables copied from the calling thread. This - * will ensure that call to static methods like {@link Application#getCurrent()} - * still work. - * - * @param executorService The JDK service to wrap. - * @return The wrapper service to use. - */ - public static ScheduledExecutorService wrap(final ScheduledExecutorService executorService) { - return new ScheduledExecutorService() { - - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - return executorService.awaitTermination(timeout, unit); - } - - public void execute(final Runnable runnable) { - // Save the thread local variables - final Application currentApplication = Application.getCurrent(); - final Context currentContext = Context.getCurrent(); - final Integer currentVirtualHost = VirtualHost.getCurrent(); - final Response currentResponse = Response.getCurrent(); - - executorService.execute(new Runnable() { - public void run() { - // Copy the thread local variables - Response.setCurrent(currentResponse); - Context.setCurrent(currentContext); - VirtualHost.setCurrent(currentVirtualHost); - Application.setCurrent(currentApplication); - - if (runnable instanceof ContextualRunnable) { - ClassLoader tccl = Thread.currentThread().getContextClassLoader(); - try { - // Run the user task - Thread.currentThread() - .setContextClassLoader(((ContextualRunnable) runnable).getContextClassLoader()); - runnable.run(); - } finally { - Engine.clearThreadLocalVariables(); - Thread.currentThread().setContextClassLoader(tccl); - } - } else { - try { - // Run the user task - runnable.run(); - } finally { - Engine.clearThreadLocalVariables(); - } - } - } - }); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public List invokeAll(Collection tasks) throws InterruptedException { - return executorService.invokeAll(tasks); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public List invokeAll(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException { - return executorService.invokeAll(tasks, timeout, unit); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Object invokeAny(Collection tasks) throws InterruptedException, ExecutionException { - return executorService.invokeAny(tasks); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Object invokeAny(Collection tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return executorService.invokeAny(tasks, timeout, unit); - } - - public boolean isShutdown() { - return executorService.isShutdown(); - } - - public boolean isTerminated() { - return executorService.isTerminated(); - } - - public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { - return executorService.schedule(callable, delay, unit); - } - - public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - return executorService.schedule(command, delay, unit); - } - - public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, - TimeUnit unit) { - return executorService.scheduleAtFixedRate(command, initialDelay, period, unit); - } - - public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, - TimeUnit unit) { - return executorService.scheduleWithFixedDelay(command, initialDelay, delay, unit); - } - - public void shutdown() { - executorService.shutdown(); - } - - public List shutdownNow() { - return executorService.shutdownNow(); - } - - public Future submit(Callable task) { - return executorService.submit(task); - } - - public Future submit(Runnable task) { - return executorService.submit(task); - } - - public Future submit(Runnable task, T result) { - return executorService.submit(task, result); - } - }; - } - - /** The core pool size defining the maximum number of threads. */ - private volatile int corePoolSize; - - /** - * Indicates whether or not the threads are daemon threads. True by default. - */ - private volatile boolean daemon; - - /** - * Allow {@link #shutdown()} and {@link #shutdownNow()} methods to effectively - * shutdown the wrapped executor service. - */ - private volatile boolean shutdownAllowed; - - /** The wrapped JDK executor service. */ - private volatile ScheduledExecutorService wrapped; - - /** - * Constructor. Enables the service and set the core pool size to 4 by default. - */ - public TaskService() { - this(true); - } - - /** - * Constructor. Set the core pool size to 4 by default. - * - * @param enabled True if the service has been enabled. - */ - public TaskService(boolean enabled) { - this(enabled, true); - } - - /** - * Constructor. Set the core pool size to 4 by default. - * - * @param enabled True if the service has been enabled. - * @param daemon True if the threads are created as daemon threads. - */ - public TaskService(boolean enabled, boolean daemon) { - this(enabled, 4); - this.daemon = daemon; - } - - /** - * Constructor. The default minimum size - * - * @param enabled True if the service has been enabled. - * @param corePoolSize The core pool size defining the maximum number of - * threads. - */ - public TaskService(boolean enabled, int corePoolSize) { - super(enabled); - this.corePoolSize = corePoolSize; - this.shutdownAllowed = false; - } - - /** - * Constructor. - * - * @param corePoolSize The core pool size defining the maximum number of - * threads. - */ - public TaskService(int corePoolSize) { - this(true, corePoolSize); - } - - /** - * Blocks until all tasks have completed execution after a shutdown request, or - * the timeout occurs, or the current thread is interrupted, whichever happens - * first. - * - * @param timeout The maximum time to wait. - * @param unit The time unit. - * @return True if this executor terminated and false if the timeout elapsed - * before termination. - */ - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - startIfNeeded(); - return getWrapped().awaitTermination(timeout, unit); - } - - /** - * Creates a new JDK executor service that will be wrapped. By default it calls - * {@link Executors#newCachedThreadPool(ThreadFactory)}, passing the result of - * {@link #createThreadFactory()} as a parameter. - * - * @param corePoolSize The core pool size defining the maximum number of - * threads. - * @return A new JDK executor service. - */ - protected ScheduledExecutorService createExecutorService(int corePoolSize) { - return Executors.newScheduledThreadPool(corePoolSize, createThreadFactory()); - } - - /** - * Creates a new thread factory that will properly name the Restlet created - * threads with a "restlet-" prefix. - * - * @return A new thread factory. - */ - protected ThreadFactory createThreadFactory() { - return new RestletThreadFactory(daemon); - } - - /** - * Executes the given command asynchronously. - * - * @param command The command to execute. - */ - public void execute(Runnable command) { - startIfNeeded(); - getWrapped().execute(command); - } - - /** - * Returns the core pool size defining the maximum number of threads. - * - * @return The core pool size defining the maximum number of threads. - */ - public int getCorePoolSize() { - return corePoolSize; - } - - /** - * Returns the wrapped JDK executor service. - * - * @return The wrapped JDK executor service. - */ - private ScheduledExecutorService getWrapped() { - return wrapped; - } - - /** - * Executes the given tasks, returning a list of Futures holding their status - * and results when all complete.
- *
- * Due to a breaking change between Java SE versions 5 and 6, and in order to - * maintain compatibility both at the source and binary level, we have removed - * the generic information from this method. You can check the - * {@link ExecutorService} interface for typing details. - * - * @param tasks The task to execute. - * @return The list of futures. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public List invokeAll(Collection tasks) throws InterruptedException { - startIfNeeded(); - return getWrapped().invokeAll(tasks); - } - - /** - * Executes the given tasks, returning a list of Futures holding their status - * and results when all complete or the timeout expires, whichever happens - * first. Future.isDone() is true for each element of the returned list. Upon - * return, tasks that have not completed are canceled. Note that a completed - * task could have terminated either normally or by throwing an exception. The - * results of this method are undefined if the given collection is modified - * while this operation is in progress.
- *
- * Due to a breaking change between Java SE versions 5 and 6, and in order to - * maintain compatibility both at the source and binary level, we have removed - * the generic information from this method. You can check the - * {@link ExecutorService} interface for typing details. - * - * @param tasks The task to execute. - * @param timeout The maximum time to wait. - * @param unit The time unit. - * @return The list of futures. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public List invokeAll(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException { - startIfNeeded(); - return getWrapped().invokeAll(tasks, timeout, unit); - } - - /** - * Executes the given tasks, returning the result of one that has completed - * successfully (i.e., without throwing an exception), if any do. Upon normal or - * exceptional return, tasks that have not completed are cancelled. The results - * of this method are undefined if the given collection is modified while this - * operation is in progress. - * - * Due to a breaking change between Java SE versions 5 and 6, and in order to - * maintain compatibility both at the source and binary level, we have removed - * the generic information from this method. You can check the - * {@link ExecutorService} interface for typing details. - * - * @param tasks The task to execute. - * @return The result returned by one of the tasks. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Object invokeAny(Collection tasks) throws InterruptedException, ExecutionException { - startIfNeeded(); - return getWrapped().invokeAny(tasks); - } - - /** - * Executes the given tasks, returning the result of one that has completed - * successfully (i.e., without throwing an exception), if any do before the - * given timeout elapses. Upon normal or exceptional return, tasks that have not - * completed are cancelled. The results of this method are undefined if the - * given collection is modified while this operation is in progress. - * - * Due to a breaking change between Java SE versions 5 and 6, and in order to - * maintain compatibility both at the source and binary level, we have removed - * the generic information from this method. You can check the - * {@link ExecutorService} interface for typing details. - * - * @param tasks The task to execute. - * @param timeout The maximum time to wait. - * @param unit The time unit. - * @return The result returned by one of the tasks. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Object invokeAny(Collection tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - startIfNeeded(); - return getWrapped().invokeAny(tasks, timeout, unit); - } - - /** - * Indicates whether the threads are created as daemon threads. - * - * @return True if the threads are created as daemon threads. - */ - public boolean isDaemon() { - return daemon; - } - - /** - * Returns true if this executor has been shut down. - * - * @return True if this executor has been shut down. - */ - public boolean isShutdown() { - return (getWrapped() == null) || getWrapped().isShutdown(); - } - - /** - * Indicates if the {@link #shutdown()} and {@link #shutdownNow()} methods are - * allowed to effectively shutdown the wrapped executor service. Return false by - * default. - * - * @return True if shutdown is allowed. - */ - public boolean isShutdownAllowed() { - return shutdownAllowed; - } - - /** - * Returns true if all tasks have completed following shut down. Note that - * isTerminated is never true unless either shutdown or shutdownNow was called - * first. - * - * @return True if all tasks have completed following shut down. - */ - public boolean isTerminated() { - return (getWrapped() == null) || getWrapped().isTerminated(); - } - - /** - * Creates and executes a ScheduledFuture that becomes enabled after the given - * delay. - * - * @param callable The function to execute. - * @param delay The time from now to delay execution. - * @param unit The time unit of the delay parameter. - * @return a ScheduledFuture that can be used to extract result or cancel. - * @throws RejectedExecutionException if task cannot be scheduled for execution. - * @throws NullPointerException if callable is null - */ - public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { - startIfNeeded(); - return getWrapped().schedule(callable, delay, unit); - } - - /** - * Creates and executes a one-shot action that becomes enabled after the given - * delay. - * - * @param command The task to execute. - * @param delay The time from now to delay execution. - * @param unit The time unit of the delay parameter. - * @return a Future representing pending completion of the task, and whose get() - * method will return null upon completion. - * @throws RejectedExecutionException if task cannot be scheduled for execution. - * @throws NullPointerException if command is null - */ - public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - startIfNeeded(); - return getWrapped().schedule(command, delay, unit); - } - - /** - * Creates and executes a periodic action that becomes enabled first after the - * given initial delay, and subsequently with the given period; that is - * executions will commence after initialDelay then initialDelay+period, then - * initialDelay + 2 * period, and so on. If any execution of the task encounters - * an exception, subsequent executions are suppressed. Otherwise, the task will - * only terminate via cancellation or termination of the executor. - * - * @param command The task to execute. - * @param initialDelay The time to delay first execution. - * @param period The period between successive executions. - * @param unit The time unit of the initialDelay and period parameters - * @return a Future representing pending completion of the task, and whose get() - * method will throw an exception upon cancellation. - * @throws RejectedExecutionException if task cannot be scheduled for execution. - * @throws NullPointerException if command is null - * @throws IllegalArgumentException if period less than or equal to zero. - */ - public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - startIfNeeded(); - return getWrapped().scheduleAtFixedRate(command, initialDelay, period, unit); - } - - /** - * Creates and executes a periodic action that becomes enabled first after the - * given initial delay, and subsequently with the given delay between the - * termination of one execution and the commencement of the next. If any - * execution of the task encounters an exception, subsequent executions are - * suppressed. Otherwise, the task will only terminate via cancellation or - * termination of the executor. - * - * @param command The task to execute. - * @param initialDelay The time to delay first execution. - * @param delay The delay between the termination of one execution and - * the commencement of the next. - * @param unit The time unit of the initialDelay and delay parameters - * @return a Future representing pending completion of the task, and whose - * get() method will throw an exception upon cancellation. - * @throws RejectedExecutionException if task cannot be scheduled for execution. - * @throws NullPointerException if command is null - * @throws IllegalArgumentException if delay less than or equal to zero. - */ - public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { - startIfNeeded(); - return getWrapped().scheduleWithFixedDelay(command, initialDelay, delay, unit); - } - - /** - * Sets the core pool size defining the maximum number of threads. - * - * @param corePoolSize The core pool size defining the maximum number of - * threads. - */ - public void setCorePoolSize(int corePoolSize) { - this.corePoolSize = corePoolSize; - } - - /** - * Indicates whether or not the threads are daemon threads. True by default. - * - * @param daemon True if the threads are daemon threads. - */ - public void setDaemon(boolean daemon) { - this.daemon = daemon; - } - - /** - * Indicates if the {@link #shutdown()} and {@link #shutdownNow()} methods are - * allowed to effectively shutdown the wrapped executor service. - * - * @param allowShutdown True if shutdown is allowed. - */ - public void setShutdownAllowed(boolean allowShutdown) { - this.shutdownAllowed = allowShutdown; - } - - /** - * Sets the wrapped JDK executor service. - * - * @param wrapped The wrapped JDK executor service. - */ - private void setWrapped(ScheduledExecutorService wrapped) { - this.wrapped = wrapped; - } - - /** - * Initiates an orderly shutdown in which previously submitted tasks are - * executed, but no new tasks will be accepted. - */ - public void shutdown() { - if (isShutdownAllowed() && (getWrapped() != null)) { - getWrapped().shutdown(); - } - } - - /** - * Attempts to stop all actively executing tasks, halts the processing of - * waiting tasks, and returns a list of the tasks that were awaiting execution. - * - * @return The list of tasks that never commenced execution; - */ - public List shutdownNow() { - return isShutdownAllowed() && (getWrapped() != null) ? getWrapped().shutdownNow() - : Collections.emptyList(); - } - - @Override - public synchronized void start() throws Exception { - if ((getWrapped() == null) || getWrapped().isShutdown()) { - setWrapped(wrap(createExecutorService(getCorePoolSize()))); - } - - super.start(); - } - - /** - * Starts the task service if needed. - */ - private void startIfNeeded() { - if (!isStarted()) { - try { - start(); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to start the task service", e); - } - } - } - - @Override - public synchronized void stop() throws Exception { - super.stop(); - - if ((getWrapped() != null) && !getWrapped().isShutdown()) { - getWrapped().shutdown(); - } - } - - /** - * Submits a value-returning task for execution and returns a Future - * representing the pending results of the task. - * - * @param task The task to submit. - * @return A Future representing pending completion of the task, and whose get() - * method will return the given result upon completion. - */ - public Future submit(Callable task) { - startIfNeeded(); - return getWrapped().submit(task); - } - - /** - * - * @param task The task to submit. - * @return A Future representing pending completion of the task, and whose get() - * method will return the given result upon completion. - */ - public Future submit(Runnable task) { - startIfNeeded(); - return getWrapped().submit(task); - } - - /** - * - * @param task The task to submit. - * @param result The result to return. - * @return A Future representing pending completion of the task, and whose get() - * method will return the given result upon completion. - */ - public Future submit(Runnable task, T result) { - startIfNeeded(); - return getWrapped().submit(task, result); - } - + /** + * The default thread factory. + * + * @author Jerome Louvel + * @author Tim Peierls + */ + private static class RestletThreadFactory implements ThreadFactory { + + /** Indicates whether the thread is a daemon thread. True by default. */ + private boolean daemon; + + final ThreadFactory factory = Executors.defaultThreadFactory(); + + public RestletThreadFactory(boolean daemon) { + this.daemon = daemon; + } + + public Thread newThread(Runnable runnable) { + Thread t = factory.newThread(runnable); + + // Default factory is documented as producing names of the + // form "pool-N-thread-M". + t.setName(t.getName().replaceFirst("pool", "restlet")); + t.setDaemon(daemon); + return t; + } + } + + /** + * Wraps a JDK executor service to ensure that the threads executing the tasks will have the + * thread local variables copied from the calling thread. This will ensure that call to static + * methods like {@link Application#getCurrent()} still work. + * + * @param executorService The JDK service to wrap. + * @return The wrapper service to use. + */ + public static ScheduledExecutorService wrap(final ScheduledExecutorService executorService) { + return new ScheduledExecutorService() { + + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + return executorService.awaitTermination(timeout, unit); + } + + public void execute(final Runnable runnable) { + // Save the thread local variables + final Application currentApplication = Application.getCurrent(); + final Context currentContext = Context.getCurrent(); + final Integer currentVirtualHost = VirtualHost.getCurrent(); + final Response currentResponse = Response.getCurrent(); + + executorService.execute( + new Runnable() { + public void run() { + // Copy the thread local variables + Response.setCurrent(currentResponse); + Context.setCurrent(currentContext); + VirtualHost.setCurrent(currentVirtualHost); + Application.setCurrent(currentApplication); + + if (runnable instanceof ContextualRunnable) { + ClassLoader tccl = + Thread.currentThread().getContextClassLoader(); + try { + // Run the user task + Thread.currentThread() + .setContextClassLoader( + ((ContextualRunnable) runnable) + .getContextClassLoader()); + runnable.run(); + } finally { + Engine.clearThreadLocalVariables(); + Thread.currentThread().setContextClassLoader(tccl); + } + } else { + try { + // Run the user task + runnable.run(); + } finally { + Engine.clearThreadLocalVariables(); + } + } + } + }); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public List invokeAll(Collection tasks) throws InterruptedException { + return executorService.invokeAll(tasks); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public List invokeAll(Collection tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return executorService.invokeAll(tasks, timeout, unit); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Object invokeAny(Collection tasks) + throws InterruptedException, ExecutionException { + return executorService.invokeAny(tasks); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Object invokeAny(Collection tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return executorService.invokeAny(tasks, timeout, unit); + } + + public boolean isShutdown() { + return executorService.isShutdown(); + } + + public boolean isTerminated() { + return executorService.isTerminated(); + } + + public ScheduledFuture schedule( + Callable callable, long delay, TimeUnit unit) { + return executorService.schedule(callable, delay, unit); + } + + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return executorService.schedule(command, delay, unit); + } + + public ScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + return executorService.scheduleAtFixedRate(command, initialDelay, period, unit); + } + + public ScheduledFuture scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + return executorService.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + + public void shutdown() { + executorService.shutdown(); + } + + public List shutdownNow() { + return executorService.shutdownNow(); + } + + public Future submit(Callable task) { + return executorService.submit(task); + } + + public Future submit(Runnable task) { + return executorService.submit(task); + } + + public Future submit(Runnable task, T result) { + return executorService.submit(task, result); + } + }; + } + + /** The core pool size defining the maximum number of threads. */ + private volatile int corePoolSize; + + /** Indicates whether the threads are daemon threads. True by default. */ + private volatile boolean daemon; + + /** + * Allow {@link #shutdown()} and {@link #shutdownNow()} methods to effectively shutdown the + * wrapped executor service. + */ + private volatile boolean shutdownAllowed; + + /** The wrapped JDK executor service. */ + private volatile ScheduledExecutorService wrapped; + + /** Constructor. Enables the service and set the core pool size to 4 by default. */ + public TaskService() { + this(true); + } + + /** + * Constructor. Set the core pool size to 4 by default. + * + * @param enabled True if the service has been enabled. + */ + public TaskService(boolean enabled) { + this(enabled, true); + } + + /** + * Constructor. Set the core pool size to 4 by default. + * + * @param enabled True if the service has been enabled. + * @param daemon True if the threads are created as daemon threads. + */ + public TaskService(boolean enabled, boolean daemon) { + this(enabled, 4); + this.daemon = daemon; + } + + /** + * Constructor. The default minimum size + * + * @param enabled True if the service has been enabled. + * @param corePoolSize The core pool size defining the maximum number of threads. + */ + public TaskService(boolean enabled, int corePoolSize) { + super(enabled); + this.corePoolSize = corePoolSize; + this.shutdownAllowed = false; + } + + /** + * Constructor. + * + * @param corePoolSize The core pool size defining the maximum number of threads. + */ + public TaskService(int corePoolSize) { + this(true, corePoolSize); + } + + /** + * Blocks until all tasks have completed execution after a shutdown request, or the timeout + * occurs, or the current thread is interrupted, whichever happens first. + * + * @param timeout The maximum time to wait. + * @param unit The time unit. + * @return True if this executor terminated and false if the timeout elapsed before termination. + */ + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + startIfNeeded(); + return getWrapped().awaitTermination(timeout, unit); + } + + /** + * Creates a new JDK executor service that will be wrapped. By default, it calls {@link + * Executors#newCachedThreadPool(ThreadFactory)}, passing the result of {@link + * #createThreadFactory()} as a parameter. + * + * @param corePoolSize The core pool size defining the maximum number of threads. + * @return A new JDK executor service. + */ + protected ScheduledExecutorService createExecutorService(int corePoolSize) { + return Executors.newScheduledThreadPool(corePoolSize, createThreadFactory()); + } + + /** + * Creates a new thread factory that will properly name the Restlet created threads with a + * "restlet-" prefix. + * + * @return A new thread factory. + */ + protected ThreadFactory createThreadFactory() { + return new RestletThreadFactory(daemon); + } + + /** + * Executes the given command asynchronously. + * + * @param command The command to execute. + */ + public void execute(Runnable command) { + startIfNeeded(); + getWrapped().execute(command); + } + + /** + * Returns the core pool size defining the maximum number of threads. + * + * @return The core pool size defining the maximum number of threads. + */ + public int getCorePoolSize() { + return corePoolSize; + } + + /** + * Returns the wrapped JDK executor service. + * + * @return The wrapped JDK executor service. + */ + private ScheduledExecutorService getWrapped() { + return wrapped; + } + + /** + * Executes the given tasks, returning a list of Futures holding their status and results when + * all complete.
+ *
+ * Due to a breaking change between Java SE versions 5 and 6, and to maintain compatibility both + * at the source and binary level, we have removed the generic information from this method. You + * can check the {@link ExecutorService} interface for typing details. + * + * @param tasks The task to execute. + * @return The list of futures. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public List invokeAll(Collection tasks) throws InterruptedException { + startIfNeeded(); + return getWrapped().invokeAll(tasks); + } + + /** + * Executes the given tasks, returning a list of Futures holding their status and results when + * all complete or the timeout expires, whichever happens first. Future.isDone() is true for + * each element of the returned list. Upon return, tasks that have not completed are canceled. + * Note that a completed task could have terminated either normally or by throwing an exception. + * The results of this method are undefined if the given collection is modified while this + * operation is in progress.
+ *
+ * Due to a breaking change between Java SE versions 5 and 6, and to maintain compatibility both + * at the source and binary level, we have removed the generic information from this method. You + * can check the {@link ExecutorService} interface for typing details. + * + * @param tasks The task to execute. + * @param timeout The maximum time to wait. + * @param unit The time unit. + * @return The list of futures. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public List invokeAll(Collection tasks, long timeout, TimeUnit unit) + throws InterruptedException { + startIfNeeded(); + return getWrapped().invokeAll(tasks, timeout, unit); + } + + /** + * Executes the given tasks, returning the result of one that has completed successfully (i.e., + * without throwing an exception) if any do. Upon normal or exceptional return, tasks that have + * not completed are canceled. The results of this method are undefined if the given collection + * is modified while this operation is in progress. + * + *

Due to a breaking change between Java SE versions 5 and 6, and to maintain compatibility + * both at the source and binary level, we have removed the generic information from this + * method. You can check the {@link ExecutorService} interface for typing details. + * + * @param tasks The task to execute. + * @return The result returned by one of the tasks. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public Object invokeAny(Collection tasks) throws InterruptedException, ExecutionException { + startIfNeeded(); + return getWrapped().invokeAny(tasks); + } + + /** + * Executes the given tasks, returning the result of one that has completed successfully (i.e., + * without throwing an exception) if any do before the given timeout elapses. Upon normal or + * exceptional return, tasks that have not completed are canceled. The results of this method + * are undefined if the given collection is modified while this operation is in progress. + * + *

Due to a breaking change between Java SE versions 5 and 6, and to maintain compatibility + * both at the source and binary level, we have removed the generic information from this + * method. You can check the {@link ExecutorService} interface for typing details. + * + * @param tasks The task to execute. + * @param timeout The maximum time to wait. + * @param unit The time unit. + * @return The result returned by one of the tasks. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public Object invokeAny(Collection tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + startIfNeeded(); + return getWrapped().invokeAny(tasks, timeout, unit); + } + + /** + * Indicates whether the threads are created as daemon threads. + * + * @return True if the threads are created as daemon threads. + */ + public boolean isDaemon() { + return daemon; + } + + /** + * Returns true if this executor has been shut down. + * + * @return True if this executor has been shut down. + */ + public boolean isShutdown() { + return (getWrapped() == null) || getWrapped().isShutdown(); + } + + /** + * Indicates if the {@link #shutdown()} and {@link #shutdownNow()} methods are allowed to + * effectively shut down the wrapped executor service. Return false by default. + * + * @return True if shutdown is allowed. + */ + public boolean isShutdownAllowed() { + return shutdownAllowed; + } + + /** + * Returns true if all tasks have completed following shut down. Note that isTerminated is never + * true unless either shutdown or shutdownNow was called first. + * + * @return True if all tasks have completed following shut down. + */ + public boolean isTerminated() { + return (getWrapped() == null) || getWrapped().isTerminated(); + } + + /** + * Creates and executes a ScheduledFuture that becomes enabled after the given delay. + * + * @param callable The function to execute. + * @param delay The time from now to delay execution. + * @param unit The time unit of the delay parameter. + * @return a ScheduledFuture that can be used to extract result or cancel. + * @throws RejectedExecutionException if a task cannot be scheduled for execution. + * @throws NullPointerException if callable is null + */ + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + startIfNeeded(); + return getWrapped().schedule(callable, delay, unit); + } + + /** + * Creates and executes a one-shot action that becomes enabled after the given delay. + * + * @param command The task to execute. + * @param delay The time from now to delay execution. + * @param unit The time unit of the delay parameter. + * @return a Future representing pending completion of the task, and whose get() method will + * return null upon completion. + * @throws RejectedExecutionException if a task cannot be scheduled for execution. + * @throws NullPointerException if the command is null + */ + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + startIfNeeded(); + return getWrapped().schedule(command, delay, unit); + } + + /** + * Creates and executes a periodic action that becomes enabled first after the given initial + * delay, and subsequently with the given period; that is executions will begin after + * initialDelay then initialDelay+period, then initialDelay + 2 * period, and so on. If any + * execution of the task encounters an exception, later executions are suppressed. Otherwise, + * the task will only terminate via cancellation or termination of the executor. + * + * @param command The task to execute. + * @param initialDelay The time to delay the first execution. + * @param period The period between successive executions. + * @param unit The time unit of the initialDelay and period parameters + * @return a Future representing pending completion of the task, and whose get() method will + * throw an exception upon cancellation. + * @throws RejectedExecutionException if a task cannot be scheduled for execution. + * @throws NullPointerException if the command is null + * @throws IllegalArgumentException if period less than or equal to zero. + */ + public ScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + startIfNeeded(); + return getWrapped().scheduleAtFixedRate(command, initialDelay, period, unit); + } + + /** + * Creates and executes a periodic action that becomes enabled first after the given initial + * delay, and subsequently with the given delay between the termination of one execution and the + * commencement of the next. If any execution of the task encounters an exception, later + * executions are suppressed. Otherwise, the task will only terminate via cancellation or + * termination of the executor. + * + * @param command The task to execute. + * @param initialDelay The time to delay the first execution. + * @param delay The delay between the termination of one execution and the commencement of the + * next. + * @param unit The time unit of the initialDelay and delay parameters + * @return a Future representing pending completion of the task, and whose get() method + * will throw an exception upon cancellation. + * @throws RejectedExecutionException if a task cannot be scheduled for execution. + * @throws NullPointerException if the command is null + * @throws IllegalArgumentException if delay less than or equal to zero. + */ + public ScheduledFuture scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + startIfNeeded(); + return getWrapped().scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + + /** + * Sets the core pool size defining the maximum number of threads. + * + * @param corePoolSize The core pool size defining the maximum number of threads. + */ + public void setCorePoolSize(int corePoolSize) { + this.corePoolSize = corePoolSize; + } + + /** + * Indicates whether or not the threads are daemon threads. True by default. + * + * @param daemon True if the threads are daemon threads. + */ + public void setDaemon(boolean daemon) { + this.daemon = daemon; + } + + /** + * Indicates if the {@link #shutdown()} and {@link #shutdownNow()} methods are allowed to + * effectively shutdown the wrapped executor service. + * + * @param allowShutdown True if shutdown is allowed. + */ + public void setShutdownAllowed(boolean allowShutdown) { + this.shutdownAllowed = allowShutdown; + } + + /** + * Sets the wrapped JDK executor service. + * + * @param wrapped The wrapped JDK executor service. + */ + private void setWrapped(ScheduledExecutorService wrapped) { + this.wrapped = wrapped; + } + + /** + * Initiates an orderly shutdown in which previously submitted tasks are executed, but no new + * tasks will be accepted. + */ + public void shutdown() { + if (isShutdownAllowed() && (getWrapped() != null)) { + getWrapped().shutdown(); + } + } + + /** + * Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and + * returns a list of the tasks that were awaiting execution. + * + * @return The list of tasks that never began execution; + */ + public List shutdownNow() { + return isShutdownAllowed() && (getWrapped() != null) + ? getWrapped().shutdownNow() + : Collections.emptyList(); + } + + @Override + public synchronized void start() throws Exception { + if ((getWrapped() == null) || getWrapped().isShutdown()) { + setWrapped(wrap(createExecutorService(getCorePoolSize()))); + } + + super.start(); + } + + /** Starts the task service if needed. */ + private void startIfNeeded() { + if (!isStarted()) { + try { + start(); + } catch (Exception e) { + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to start the task service", e); + } + } + } + + @Override + public synchronized void stop() throws Exception { + super.stop(); + + if ((getWrapped() != null) && !getWrapped().isShutdown()) { + getWrapped().shutdown(); + } + } + + /** + * Submits a value-returning task for execution and returns a Future representing the pending + * results of the task. + * + * @param task The task to submit. + * @return A Future representing pending completion of the task, and whose get() method will + * return the given result upon completion. + */ + public Future submit(Callable task) { + startIfNeeded(); + return getWrapped().submit(task); + } + + /** + * @param task The task to submit. + * @return A Future representing pending completion of the task, and whose get() method will + * return the given result upon completion. + */ + public Future submit(Runnable task) { + startIfNeeded(); + return getWrapped().submit(task); + } + + /** + * @param task The task to submit. + * @param result The result to return. + * @return A Future representing pending completion of the task, and whose get() method will + * return the given result upon completion. + */ + public Future submit(Runnable task, T result) { + startIfNeeded(); + return getWrapped().submit(task, result); + } } diff --git a/org.restlet/src/main/java/org/restlet/service/TunnelService.java b/org.restlet/src/main/java/org/restlet/service/TunnelService.java index 38b38d40dc..dec7b6a0ca 100644 --- a/org.restlet/src/main/java/org/restlet/service/TunnelService.java +++ b/org.restlet/src/main/java/org/restlet/service/TunnelService.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; import org.restlet.Context; @@ -17,12 +16,12 @@ import org.restlet.routing.Filter; /** - * Application service tunneling request method or client preferences. The - * tunneling can use query parameters, file-like extensions, and specific - * headers. This is particularly useful for browser-based applications that - * can't fully control the HTTP requests sent.
+ * Application service tunneling request method or client preferences. The tunneling can use query + * parameters, file-like extensions, and specific headers. This is particularly useful for + * browser-based applications that can't fully control the HTTP requests sent.
*
* Here is the list of the default parameter names supported: + * * * * @@ -78,427 +77,417 @@ * MOVE, etc.). * *
list of the default parameter names supported
+ * *
- * The client preferences can also be updated according to the user agent - * properties (its name, version, operating system, or other) available via the - * {@link ClientInfo#getAgentAttributes()} method. Check the - * {@link #isUserAgentTunnel()} method.
+ * The client preferences can also be updated according to the user agent properties (its name, + * version, operating system, or other) available via the {@link ClientInfo#getAgentAttributes()} + * method. Check the {@link #isUserAgentTunnel()} method.
*
- * The list of new media type preferences is loaded from a property file called - * "accept.properties" located in the classpath in the subdirectory - * "org/restlet/service". This property file is composed of blocks of - * properties. One "block" of properties starts either with the beginning of the - * properties file or with the end of the previous block. One block ends with - * the "acceptNew" property which contains the value of the new Accept header. - * Here is a sample block.
- * + * The list of new media type preferences is loaded from a property file called "accept.properties" + * located in the classpath in the subdirectory "org/restlet/service". This property file is + * composed of blocks of properties. One "block" of properties starts either with the beginning of + * the properties file or with the end of the previous block. One block ends with the "acceptNew" + * property which contains the value of the new Accept header. Here is a sample block.
+ * *

  * agentName: firefox
  * acceptOld: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,\*\/\*;q=0.5
  * acceptNew: application/xhtml+xml,text/html,text/xml;q=0.9,application/xml;q=0.9,text/plain;q=0.8,image/png,\*\/\*;q=0.5
  * 
- * - * Each declared property is a condition that must be filled to update - * the client preferences. For example, "agentName: firefox" expresses the fact - * this block concerns only "firefox" clients.
+ * + * Each declared property is a condition that must be filled to update the client preferences. For + * example, "agentName: firefox" expresses the fact this block concerns only "firefox" clients.
*
- * The "acceptOld" property allows checking the value of the current "Accept" - * header. If it equals to the value of the "acceptOld" property or if the - * "acceptOld" property is empty, then the preferences will be updated. This can - * be useful for AJAX clients that look like their browser (same agentName, - * agentVersion, etc.) but can provide their own "Accept" header. - * + * The "acceptOld" property allows checking the value of the current "Accept" header. If it equals + * to the value of the "acceptOld" property or if the "acceptOld" property is empty, then the + * preferences will be updated. This can be useful for AJAX clients that look like their browser + * (same agentName, agentVersion, etc.) but can provide their own "Accept" header. + * * @author Jerome Louvel */ public class TunnelService extends Service { - /** The name of the parameter containing the accepted character set. */ - private volatile String characterSetParameter; - - /** The name of the parameter containing the accepted encoding. */ - private volatile String encodingParameter; - - /** - * Indicates if the client preferences can be tunneled via file-like extensions. - */ - private volatile boolean extensionsTunnel; - - /** Indicates if the method can be tunneled via the header. */ - private volatile boolean headersTunnel; - - /** The name of the parameter containing the accepted language. */ - private volatile String languageParameter; - - /** The name of the parameter containing the accepted media type. */ - private volatile String mediaTypeParameter; - - /** The name of the header that contains the method name. */ - private volatile String methodHeader; - - /** The name of the parameter containing the method name. */ - private volatile String methodParameter; - - /** Indicates if the method name can be tunneled. */ - private volatile boolean methodTunnel; - - /** Indicates if the client preferences can be tunneled. */ - private volatile boolean preferencesTunnel; - - /** - * Indicates if the method and client preferences can be tunneled via query - * parameters. - */ - private volatile boolean queryTunnel; - - /** - * Indicates if the client preferences can be tunneled via the user agent - * string. - */ - private volatile boolean userAgentTunnel; - - /** - * Constructor that enables the query tunnel and disables the extensions and - * user agent tunnels. - * - * @param methodTunnel Indicates if the method name can be tunneled. - * @param preferencesTunnel Indicates if the client preferences can be tunneled - * by query parameters or file-like extensions or user - * agent string. - */ - public TunnelService(boolean methodTunnel, boolean preferencesTunnel) { - this(true, methodTunnel, preferencesTunnel); - } - - /** - * Constructor that enables the query tunnel and disables the extensions and - * user agent tunnels. - * - * @param enabled True if the service has been enabled. - * @param methodTunnel Indicates if the method name can be tunneled. - * @param preferencesTunnel Indicates if the client preferences can be tunneled - * by query parameters or file-like extensions or user - * agent string. - */ - public TunnelService(boolean enabled, boolean methodTunnel, boolean preferencesTunnel) { - this(enabled, methodTunnel, preferencesTunnel, true, false); - } - - /** - * Constructor that disables the user agent tunnel. - * - * @param enabled True if the service has been enabled. - * @param methodTunnel Indicates if the method can be tunneled using a - * query parameter. - * @param preferencesTunnel Indicates if the client preferences can be tunneled - * using query parameters or file-like extensions or - * user agent string. - * @param queryTunnel Indicates if tunneling can use query parameters. - * @param extensionsTunnel Indicates if tunneling can use file-like extensions. - */ - public TunnelService(boolean enabled, boolean methodTunnel, boolean preferencesTunnel, boolean queryTunnel, - boolean extensionsTunnel) { - this(enabled, methodTunnel, preferencesTunnel, queryTunnel, extensionsTunnel, false); - } - - /** - * Constructor that enables the header tunneling. - * - * @param enabled True if the service has been enabled. - * @param methodTunnel Indicates if the method can be tunneled using a - * query parameter. - * @param preferencesTunnel Indicates if the client preferences can be tunneled - * using query parameters or file-like extensions or - * user agent string. - * @param queryTunnel Indicates if tunneling can use query parameters. - * @param extensionsTunnel Indicates if tunneling can use file-like extensions. - * @param userAgentTunnel Indicates if tunneling can use user agent string. - */ - public TunnelService(boolean enabled, boolean methodTunnel, boolean preferencesTunnel, boolean queryTunnel, - boolean extensionsTunnel, boolean userAgentTunnel) { - this(enabled, methodTunnel, preferencesTunnel, queryTunnel, extensionsTunnel, userAgentTunnel, true); - } - - /** - * Constructor. - * - * @param enabled True if the service has been enabled. - * @param methodTunnel Indicates if the method can be tunneled using a - * query parameter. - * @param preferencesTunnel Indicates if the client preferences can be tunneled - * using query parameters or file-like extensions or - * user agent string. - * @param queryTunnel Indicates if tunneling can use query parameters. - * @param extensionsTunnel Indicates if tunneling can use file-like extensions. - * @param userAgentTunnel Indicates if tunneling can use user agent string. - * @param headersTunnel Indicates if method can be tunneled via a specific - * header. - */ - public TunnelService(boolean enabled, boolean methodTunnel, boolean preferencesTunnel, boolean queryTunnel, - boolean extensionsTunnel, boolean userAgentTunnel, boolean headersTunnel) { - super(enabled); - - this.extensionsTunnel = extensionsTunnel; - this.methodTunnel = methodTunnel; - this.preferencesTunnel = preferencesTunnel; - this.queryTunnel = queryTunnel; - this.userAgentTunnel = userAgentTunnel; - this.headersTunnel = headersTunnel; - - this.characterSetParameter = "charset"; - this.encodingParameter = "encoding"; - this.languageParameter = "language"; - this.mediaTypeParameter = "media"; - this.methodParameter = "method"; - this.methodHeader = HeaderConstants.HEADER_X_HTTP_METHOD_OVERRIDE; - } - - /** - * Indicates if the request from a given client can be tunneled. The default - * implementation always returns true. This could be customized to restrict the - * usage of the tunnel service. - * - * @param client The client to test. - * @return True if the request from a given client can be tunneled. - */ - public boolean allowClient(ClientInfo client) { - return true; - } - - @Override - public Filter createInboundFilter(Context context) { - return new TunnelFilter(context); - } - - /** - * Returns the character set parameter name. - * - * @return The character set parameter name. - */ - public String getCharacterSetParameter() { - return this.characterSetParameter; - } - - /** - * Returns the name of the parameter containing the accepted encoding. - * - * @return The name of the parameter containing the accepted encoding. - */ - public String getEncodingParameter() { - return this.encodingParameter; - } - - /** - * Returns the name of the parameter containing the accepted language. - * - * @return The name of the parameter containing the accepted language. - */ - public String getLanguageParameter() { - return this.languageParameter; - } - - /** - * Returns the name of the parameter containing the accepted media type. - * - * @return The name of the parameter containing the accepted media type. - */ - public String getMediaTypeParameter() { - return this.mediaTypeParameter; - } - - /** - * Returns the name of the header containing the method name. - * - * @return the name of the header containing the method name. - */ - public String getMethodHeader() { - return methodHeader; - } - - /** - * Returns the method parameter name. - * - * @return The method parameter name. - */ - public String getMethodParameter() { - return this.methodParameter; - } - - /** - * Indicates if the client preferences can be tunneled via the extensions. - * Returns false by default. - * - * @return True if the client preferences can be tunneled via the extensions - * @see Request#getOriginalRef() - */ - public boolean isExtensionsTunnel() { - return this.extensionsTunnel; - } - - /** - * Indicates if the method can be tunneled via the header. Returns true by - * default. - * - * @return True if the method can be tunneled via the header. - */ - public boolean isHeadersTunnel() { - return headersTunnel; - } - - /** - * Indicates if the method name can be tunneled. Returns true by default. - * - * @return True if the method name can be tunneled. - */ - public boolean isMethodTunnel() { - return this.methodTunnel; - } - - /** - * Indicates if the client preferences can be tunneled via the query parameters - * or via file extensions. Returns true by default. - * - * @return True if the client preferences can be tunneled. - */ - public boolean isPreferencesTunnel() { - return this.preferencesTunnel; - } - - /** - * Indicates if the method and client preferences can be tunneled via query - * parameters or file extensions. Returns true by default. - * - * @return True if the method and client preferences can be tunneled. - */ - public boolean isQueryTunnel() { - return this.queryTunnel; - } - - /** - * Indicates if the client preferences can be tunneled according to the user - * agent. Returns false by default. - * - * @return True if the client preferences can be tunneled according to the user - * agent. - */ - public boolean isUserAgentTunnel() { - return this.userAgentTunnel; - } - - /** - * Sets the character set parameter name. - * - * @param parameterName The character set parameter name. - */ - public void setCharacterSetParameter(String parameterName) { - this.characterSetParameter = parameterName; - } - - /** - * Sets the name of the parameter containing the accepted encoding. - * - * @param parameterName The name of the parameter containing the accepted - * encoding. - */ - public void setEncodingParameter(String parameterName) { - this.encodingParameter = parameterName; - } - - /** - * Indicates if the client preferences can be tunneled via the extensions. - * - * @param extensionTunnel True if the client preferences can be tunneled via the - * extensions. - * @see Request#getOriginalRef() - */ - public void setExtensionsTunnel(boolean extensionTunnel) { - this.extensionsTunnel = extensionTunnel; - } - - /** - * Indicates if the method can be tunneled via the header. - * - * @param headersTunnel True if the method can be tunneled via the header. - */ - public void setHeadersTunnel(boolean headersTunnel) { - this.headersTunnel = headersTunnel; - } - - /** - * Sets the name of the parameter containing the accepted language. - * - * @param parameterName The name of the parameter containing the accepted - * language. - */ - public void setLanguageParameter(String parameterName) { - this.languageParameter = parameterName; - } - - /** - * Sets the name of the parameter containing the accepted media type. - * - * @param parameterName The name of the parameter containing the accepted media - * type. - */ - public void setMediaTypeParameter(String parameterName) { - this.mediaTypeParameter = parameterName; - } - - /** - * Sets the name of the header containing the method name. - * - * @param methodHeader The name of the header containing the method name. - */ - public void setMethodHeader(String methodHeader) { - this.methodHeader = methodHeader; - } - - /** - * Sets the method parameter name. - * - * @param parameterName The method parameter name. - */ - public void setMethodParameter(String parameterName) { - this.methodParameter = parameterName; - } - - /** - * Indicates if the method name can be tunneled. - * - * @param methodTunnel True if the method name can be tunneled. - */ - public void setMethodTunnel(boolean methodTunnel) { - this.methodTunnel = methodTunnel; - } - - /** - * Indicates if the client preferences can be tunneled via the query parameters. - * - * @param preferencesTunnel True if the client preferences can be tunneled via - * the query parameters. - */ - public void setPreferencesTunnel(boolean preferencesTunnel) { - this.preferencesTunnel = preferencesTunnel; - } - - /** - * Indicates if the method and client preferences can be tunneled via query - * parameters. - * - * @param queryTunnel True if the method and client preferences can be tunneled - * via query parameters. - */ - public void setQueryTunnel(boolean queryTunnel) { - this.queryTunnel = queryTunnel; - } - - /** - * Indicates if the client preferences can be tunneled according to the user - * agent. - * - * @param userAgentTunnel True if the client preferences can be tunneled - * according to the user agent. - */ - public void setUserAgentTunnel(boolean userAgentTunnel) { - this.userAgentTunnel = userAgentTunnel; - } + /** The name of the parameter containing the accepted character set. */ + private volatile String characterSetParameter; + + /** The name of the parameter containing the accepted encoding. */ + private volatile String encodingParameter; + + /** Indicates if the client preferences can be tunneled via file-like extensions. */ + private volatile boolean extensionsTunnel; + + /** Indicates if the method can be tunneled via the header. */ + private volatile boolean headersTunnel; + + /** The name of the parameter containing the accepted language. */ + private volatile String languageParameter; + + /** The name of the parameter containing the accepted media type. */ + private volatile String mediaTypeParameter; + + /** The name of the header that contains the method name. */ + private volatile String methodHeader; + + /** The name of the parameter containing the method name. */ + private volatile String methodParameter; + + /** Indicates if the method name can be tunneled. */ + private volatile boolean methodTunnel; + + /** Indicates if the client preferences can be tunneled. */ + private volatile boolean preferencesTunnel; + + /** Indicates if the method and client preferences can be tunneled via query parameters. */ + private volatile boolean queryTunnel; + + /** Indicates if the client preferences can be tunneled via the user agent string. */ + private volatile boolean userAgentTunnel; + + /** + * Constructor that enables the query tunnel and disables the extensions and user agent tunnels. + * + * @param methodTunnel Indicates if the method name can be tunneled. + * @param preferencesTunnel Indicates if the client preferences can be tunneled by query + * parameters or file-like extensions or user agent string. + */ + public TunnelService(boolean methodTunnel, boolean preferencesTunnel) { + this(true, methodTunnel, preferencesTunnel); + } + + /** + * Constructor that enables the query tunnel and disables the extensions and user agent tunnels. + * + * @param enabled True if the service has been enabled. + * @param methodTunnel Indicates if the method name can be tunneled. + * @param preferencesTunnel Indicates if the client preferences can be tunneled by query + * parameters or file-like extensions or user agent string. + */ + public TunnelService(boolean enabled, boolean methodTunnel, boolean preferencesTunnel) { + this(enabled, methodTunnel, preferencesTunnel, true, false); + } + + /** + * Constructor that disables the user agent tunnel. + * + * @param enabled True if the service has been enabled. + * @param methodTunnel Indicates if the method can be tunneled using a query parameter. + * @param preferencesTunnel Indicates if the client preferences can be tunneled using query + * parameters or file-like extensions or user agent string. + * @param queryTunnel Indicates if tunneling can use query parameters. + * @param extensionsTunnel Indicates if tunneling can use file-like extensions. + */ + public TunnelService( + boolean enabled, + boolean methodTunnel, + boolean preferencesTunnel, + boolean queryTunnel, + boolean extensionsTunnel) { + this(enabled, methodTunnel, preferencesTunnel, queryTunnel, extensionsTunnel, false); + } + + /** + * Constructor that enables the header tunneling. + * + * @param enabled True if the service has been enabled. + * @param methodTunnel Indicates if the method can be tunneled using a query parameter. + * @param preferencesTunnel Indicates if the client preferences can be tunneled using query + * parameters or file-like extensions or user agent string. + * @param queryTunnel Indicates if tunneling can use query parameters. + * @param extensionsTunnel Indicates if tunneling can use file-like extensions. + * @param userAgentTunnel Indicates if tunneling can use user agent string. + */ + public TunnelService( + boolean enabled, + boolean methodTunnel, + boolean preferencesTunnel, + boolean queryTunnel, + boolean extensionsTunnel, + boolean userAgentTunnel) { + this( + enabled, + methodTunnel, + preferencesTunnel, + queryTunnel, + extensionsTunnel, + userAgentTunnel, + true); + } + + /** + * Constructor. + * + * @param enabled True if the service has been enabled. + * @param methodTunnel Indicates if the method can be tunneled using a query parameter. + * @param preferencesTunnel Indicates if the client preferences can be tunneled using query + * parameters or file-like extensions or user agent string. + * @param queryTunnel Indicates if tunneling can use query parameters. + * @param extensionsTunnel Indicates if tunneling can use file-like extensions. + * @param userAgentTunnel Indicates if tunneling can use user agent string. + * @param headersTunnel Indicates if method can be tunneled via a specific header. + */ + public TunnelService( + boolean enabled, + boolean methodTunnel, + boolean preferencesTunnel, + boolean queryTunnel, + boolean extensionsTunnel, + boolean userAgentTunnel, + boolean headersTunnel) { + super(enabled); + + this.extensionsTunnel = extensionsTunnel; + this.methodTunnel = methodTunnel; + this.preferencesTunnel = preferencesTunnel; + this.queryTunnel = queryTunnel; + this.userAgentTunnel = userAgentTunnel; + this.headersTunnel = headersTunnel; + + this.characterSetParameter = "charset"; + this.encodingParameter = "encoding"; + this.languageParameter = "language"; + this.mediaTypeParameter = "media"; + this.methodParameter = "method"; + this.methodHeader = HeaderConstants.HEADER_X_HTTP_METHOD_OVERRIDE; + } + + /** + * Indicates if the request from a given client can be tunneled. The default implementation + * always returns true. This could be customized to restrict the usage of the tunnel service. + * + * @param client The client to test. + * @return True if the request from a given client can be tunneled. + */ + public boolean allowClient(ClientInfo client) { + return true; + } + + @Override + public Filter createInboundFilter(Context context) { + return new TunnelFilter(context); + } + + /** + * Returns the character set parameter name. + * + * @return The character set parameter name. + */ + public String getCharacterSetParameter() { + return this.characterSetParameter; + } + + /** + * Returns the name of the parameter containing the accepted encoding. + * + * @return The name of the parameter containing the accepted encoding. + */ + public String getEncodingParameter() { + return this.encodingParameter; + } + + /** + * Returns the name of the parameter containing the accepted language. + * + * @return The name of the parameter containing the accepted language. + */ + public String getLanguageParameter() { + return this.languageParameter; + } + + /** + * Returns the name of the parameter containing the accepted media type. + * + * @return The name of the parameter containing the accepted media type. + */ + public String getMediaTypeParameter() { + return this.mediaTypeParameter; + } + + /** + * Returns the name of the header containing the method name. + * + * @return the name of the header containing the method name. + */ + public String getMethodHeader() { + return methodHeader; + } + + /** + * Returns the method parameter name. + * + * @return The method parameter name. + */ + public String getMethodParameter() { + return this.methodParameter; + } + + /** + * Indicates if the client preferences can be tunneled via the extensions. Returns false by + * default. + * + * @return True if the client preferences can be tunneled via the extensions + * @see Request#getOriginalRef() + */ + public boolean isExtensionsTunnel() { + return this.extensionsTunnel; + } + + /** + * Indicates if the method can be tunneled via the header. Returns true by default. + * + * @return True if the method can be tunneled via the header. + */ + public boolean isHeadersTunnel() { + return headersTunnel; + } + + /** + * Indicates if the method name can be tunneled. Returns true by default. + * + * @return True if the method name can be tunneled. + */ + public boolean isMethodTunnel() { + return this.methodTunnel; + } + + /** + * Indicates if the client preferences can be tunneled via the query parameters or via file + * extensions. Returns true by default. + * + * @return True if the client preferences can be tunneled. + */ + public boolean isPreferencesTunnel() { + return this.preferencesTunnel; + } + + /** + * Indicates if the method and client preferences can be tunneled via query parameters or file + * extensions. Returns true by default. + * + * @return True if the method and client preferences can be tunneled. + */ + public boolean isQueryTunnel() { + return this.queryTunnel; + } + + /** + * Indicates if the client preferences can be tunneled according to the user agent. Returns + * false by default. + * + * @return True if the client preferences can be tunneled according to the user agent. + */ + public boolean isUserAgentTunnel() { + return this.userAgentTunnel; + } + + /** + * Sets the character set parameter name. + * + * @param parameterName The character set parameter name. + */ + public void setCharacterSetParameter(String parameterName) { + this.characterSetParameter = parameterName; + } + + /** + * Sets the name of the parameter containing the accepted encoding. + * + * @param parameterName The name of the parameter containing the accepted encoding. + */ + public void setEncodingParameter(String parameterName) { + this.encodingParameter = parameterName; + } + + /** + * Indicates if the client preferences can be tunneled via the extensions. + * + * @param extensionTunnel True if the client preferences can be tunneled via the extensions. + * @see Request#getOriginalRef() + */ + public void setExtensionsTunnel(boolean extensionTunnel) { + this.extensionsTunnel = extensionTunnel; + } + + /** + * Indicates if the method can be tunneled via the header. + * + * @param headersTunnel True if the method can be tunneled via the header. + */ + public void setHeadersTunnel(boolean headersTunnel) { + this.headersTunnel = headersTunnel; + } + + /** + * Sets the name of the parameter containing the accepted language. + * + * @param parameterName The name of the parameter containing the accepted language. + */ + public void setLanguageParameter(String parameterName) { + this.languageParameter = parameterName; + } + + /** + * Sets the name of the parameter containing the accepted media type. + * + * @param parameterName The name of the parameter containing the accepted media type. + */ + public void setMediaTypeParameter(String parameterName) { + this.mediaTypeParameter = parameterName; + } + + /** + * Sets the name of the header containing the method name. + * + * @param methodHeader The name of the header containing the method name. + */ + public void setMethodHeader(String methodHeader) { + this.methodHeader = methodHeader; + } + + /** + * Sets the method parameter name. + * + * @param parameterName The method parameter name. + */ + public void setMethodParameter(String parameterName) { + this.methodParameter = parameterName; + } + + /** + * Indicates if the method name can be tunneled. + * + * @param methodTunnel True if the method name can be tunneled. + */ + public void setMethodTunnel(boolean methodTunnel) { + this.methodTunnel = methodTunnel; + } + + /** + * Indicates if the client preferences can be tunneled via the query parameters. + * + * @param preferencesTunnel True if the client preferences can be tunneled via the query + * parameters. + */ + public void setPreferencesTunnel(boolean preferencesTunnel) { + this.preferencesTunnel = preferencesTunnel; + } + + /** + * Indicates if the method and client preferences can be tunneled via query parameters. + * + * @param queryTunnel True if the method and client preferences can be tunneled via query + * parameters. + */ + public void setQueryTunnel(boolean queryTunnel) { + this.queryTunnel = queryTunnel; + } + + /** + * Indicates if the client preferences can be tunneled according to the user agent. + * + * @param userAgentTunnel True if the client preferences can be tunneled according to the user + * agent. + */ + public void setUserAgentTunnel(boolean userAgentTunnel) { + this.userAgentTunnel = userAgentTunnel; + } } diff --git a/org.restlet/src/main/java/org/restlet/util/ClientList.java b/org.restlet/src/main/java/org/restlet/util/ClientList.java index 7e88b4883d..8500854c50 100644 --- a/org.restlet/src/main/java/org/restlet/util/ClientList.java +++ b/org.restlet/src/main/java/org/restlet/util/ClientList.java @@ -1,78 +1,76 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Client; import org.restlet.Context; import org.restlet.data.Protocol; -import java.util.concurrent.CopyOnWriteArrayList; - /** * Modifiable list of client connectors. - * + * * @author Jerome Louvel */ public final class ClientList extends WrapperList { - /** The context. */ - private volatile Context context; + /** The context. */ + private volatile Context context; - /** - * Constructor. - * - * @param context The context. - */ - public ClientList(Context context) { - super(new CopyOnWriteArrayList()); - this.context = context; - } + /** + * Constructor. + * + * @param context The context. + */ + public ClientList(Context context) { + super(new CopyOnWriteArrayList()); + this.context = context; + } - @Override - public boolean add(Client client) { - // Set the client's context, if the client does not have already one. - if (client.getContext() == null) { - client.setContext(getContext().createChildContext()); - } + @Override + public boolean add(Client client) { + // Set the client's context if the client does not already have one. + if (client.getContext() == null) { + client.setContext(getContext().createChildContext()); + } - return super.add(client); - } + return super.add(client); + } - /** - * Adds a new client connector in the map supporting the given protocol. - * - * @param protocol The connector protocol. - * @return The added client. - */ - public Client add(Protocol protocol) { - final Client result = new Client(protocol); - result.setContext(getContext().createChildContext()); - add(result); - return result; - } + /** + * Adds a new client connector in the map supporting the given protocol. + * + * @param protocol The connector protocol. + * @return The added client. + */ + public Client add(Protocol protocol) { + final Client result = new Client(protocol); + result.setContext(getContext().createChildContext()); + add(result); + return result; + } - /** - * Returns the context. - * - * @return The context. - */ - public Context getContext() { - return this.context; - } + /** + * Returns the context. + * + * @return The context. + */ + public Context getContext() { + return this.context; + } - /** - * Sets the context. - * - * @param context The context. - */ - public void setContext(Context context) { - this.context = context; - } + /** + * Sets the context. + * + * @param context The context. + */ + public void setContext(Context context) { + this.context = context; + } } diff --git a/org.restlet/src/main/java/org/restlet/util/NamedValue.java b/org.restlet/src/main/java/org/restlet/util/NamedValue.java index 6c86411423..89773f1735 100644 --- a/org.restlet/src/main/java/org/restlet/util/NamedValue.java +++ b/org.restlet/src/main/java/org/restlet/util/NamedValue.java @@ -1,40 +1,38 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; /** * String couple between a name and a value. - * + * * @author Jerome Louvel */ public interface NamedValue { - /** - * Returns the name of this parameter. - * - * @return The name of this parameter. - */ - abstract String getName(); - - /** - * Returns the value. - * - * @return The value. - */ - abstract V getValue(); + /** + * Returns the name of this parameter. + * + * @return The name of this parameter. + */ + String getName(); - /** - * Sets the value. - * - * @param value The value. - */ - abstract void setValue(V value); + /** + * Returns the value. + * + * @return The value. + */ + V getValue(); + /** + * Sets the value. + * + * @param value The value. + */ + void setValue(V value); } diff --git a/org.restlet/src/main/java/org/restlet/util/Resolver.java b/org.restlet/src/main/java/org/restlet/util/Resolver.java index 48002b277e..00086b74bf 100644 --- a/org.restlet/src/main/java/org/restlet/util/Resolver.java +++ b/org.restlet/src/main/java/org/restlet/util/Resolver.java @@ -1,27 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; +import java.util.Map; import org.restlet.Request; import org.restlet.Response; import org.restlet.engine.util.CallResolver; import org.restlet.engine.util.MapResolver; -import java.util.Map; - /** - * Resolves a name into a value. By default, the {@link #createResolver(Map)} - * static method can adapt a Java map into a resolver. Another useful method is - * {@link #createResolver(Request, Response)}, which can expose a Restlet call - * into a compact data model, with the following variables: - * + * Resolves a name into a value. By default, the {@link #createResolver(Map)} static method can + * adapt a Java map into a resolver. Another useful method is {@link #createResolver(Request, + * Response)}, which can expose a Restlet call into a compact data model, with the following + * variables: + * * * * @@ -200,12 +198,12 @@ * * *
list of supported variables
Integer
+ * *
- * - * Below is the list of name sub-parts, for Reference variables, that can - * replace the asterix in the variable names above:
+ * Below is the list of name sub-parts, for Reference variables, that can replace the asterix in the + * variable names above:
*
- * + * * * @@ -265,40 +263,39 @@ * * *
list of name sub-parts, for Reference variables, that can replace * the asterix in the variable names above
String
- * + * * @author Jerome Louvel */ public abstract class Resolver { - /** - * Creates a resolver that is based on a given map. - * - * @param map Map between names and values. - * @return The map resolver. - */ - public static Resolver createResolver(Map map) { - return new MapResolver(map); - } - - /** - * Creates a resolver that is based on a call (request, response couple). It - * first looks up the response attributes, then the request attributes and - * finally the variables listed in this class Javadocs above. - * - * @param request The request. - * @param response The response. - * @return The call resolver. - */ - public static Resolver createResolver(Request request, Response response) { - return new CallResolver(request, response); - } + /** + * Creates a resolver based on a given map. + * + * @param map Map between names and values. + * @return The map resolver. + */ + public static Resolver createResolver(Map map) { + return new MapResolver(map); + } - /** - * Resolves a name into a value. - * - * @param name The name to resolve. - * @return The resolved value. - */ - public abstract T resolve(String name); + /** + * Creates a resolver based on a call (request, response couple). It first looks up the + * response attributes, then the request attributes, and finally the variables listed in this + * class Javadocs above. + * + * @param request The request. + * @param response The response. + * @return The call resolver. + */ + public static Resolver createResolver(Request request, Response response) { + return new CallResolver(request, response); + } + /** + * Resolves a name into a value. + * + * @param name The name to resolve. + * @return The resolved value. + */ + public abstract T resolve(String name); } diff --git a/org.restlet/src/main/java/org/restlet/util/RouteList.java b/org.restlet/src/main/java/org/restlet/util/RouteList.java index 7b4c234ff5..04718dafd8 100644 --- a/org.restlet/src/main/java/org/restlet/util/RouteList.java +++ b/org.restlet/src/main/java/org/restlet/util/RouteList.java @@ -1,214 +1,210 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; -import org.restlet.routing.Route; - import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ThreadLocalRandom; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.routing.Route; /** - * Modifiable list of routes with some helper methods. Note that this class - * implements the {@link List} interface using the Route class as the generic - * type. This allows you to use an instance of this class as any other - * {@link List}, in particular all the helper methods in - * {@link Collections}.
+ * Modifiable list of routes with some helper methods. Note that this class implements the {@link + * List} interface using the Route class as the generic type. This allows you to use an instance of + * this class as any other {@link List}, in particular all the helper methods in {@link + * Collections}.
*
- * Note that structural changes to this list are thread-safe, using an - * underlying {@link CopyOnWriteArrayList}. - * + * Note that structural changes to this list are thread-safe, using an underlying {@link + * CopyOnWriteArrayList}. + * * @author Jerome Louvel * @see java.util.Collections * @see java.util.List */ public final class RouteList extends WrapperList { - /** The index of the last route used in the round-robin mode. */ - private volatile int lastIndex; - - /** - * Constructor. - */ - public RouteList() { - super(new CopyOnWriteArrayList<>()); - this.lastIndex = -1; - } - - /** - * Constructor. - * - * @param delegate The delegate list. - */ - public RouteList(List delegate) { - super(new CopyOnWriteArrayList<>(delegate)); - this.lastIndex = -1; - } - - /** - * Returns the best route match for a given call. - * - * @param request The request to score. - * @param response The response to score. - * @param requiredScore The minimum score required to have a match. - * @return The best route match or null. - */ - public Route getBest(Request request, Response response, float requiredScore) { - Route result = null; - float bestScore = 0F; - float score; - - for (Route current : this) { - score = current.score(request, response); - - if ((score > bestScore) && (score >= requiredScore)) { - bestScore = score; - result = current; - } - } - - return result; - } - - /** - * Returns the first route match for a given call. - * - * @param request The request to score. - * @param response The response to score. - * @param requiredScore The minimum score required to have a match. - * @return The first route match or null. - */ - public Route getFirst(Request request, Response response, float requiredScore) { - for (Route current : this) { - if (current.score(request, response) >= requiredScore) { - return current; - } - } - - // No match found - return null; - } - - /** - * Returns the last route match for a given call. - * - * @param request The request to score. - * @param response The response to score. - * @param requiredScore The minimum score required to have a match. - * @return The last route match or null. - */ - public synchronized Route getLast(Request request, Response response, float requiredScore) { - for (int j = size() - 1; (j >= 0); j--) { - final Route route = get(j); - if (route.score(request, response) >= requiredScore) { - return route; - } - } - - // No match found - return null; - } - - /** - * Returns a next route match in a round robin mode for a given call. - * - * @param request The request to score. - * @param response The response to score. - * @param requiredScore The minimum score required to have a match. - * @return A next route or null. - */ - public synchronized Route getNext(Request request, Response response, float requiredScore) { - if (!isEmpty()) { - for (final int initialIndex = this.lastIndex++; initialIndex != this.lastIndex; this.lastIndex++) { - if (this.lastIndex >= size()) { - this.lastIndex = 0; - } - - final Route route = get(this.lastIndex); - if (route.score(request, response) >= requiredScore) { - return route; - } - } - } - - // No match found - return null; - } - - /** - * Returns a random route match for a given call. Note that the current - * implementation doesn't uniformly return routes unless they all score above - * the required score. - * - * @param request The request to score. - * @param response The response to score. - * @param requiredScore The minimum score required to have a match. - * @return A random route or null. - */ - public synchronized Route getRandom(Request request, Response response, float requiredScore) { - int length = size(); - - if (length > 0) { - int j = ThreadLocalRandom.current().nextInt(length); - Route route = get(j); - - if (route.score(request, response) >= requiredScore) { - return route; - } - - boolean loopedAround = false; - - do { - if ((j == length) && !loopedAround) { - j = 0; - loopedAround = true; - } - - route = get(j++); - - if (route.score(request, response) >= requiredScore) { - return route; - } - } while ((j < length) || !loopedAround); - } - - // No match found - return null; - } - - /** - * Removes all routes to a given target. - * - * @param target The target Restlet to detach. - */ - public synchronized void removeAll(Restlet target) { - for (int i = size() - 1; i >= 0; i--) { - if (get(i).getNext() == target) { - remove(i); - } - } - } - - /** - * Returns a view of the portion of this list between the specified fromIndex, - * inclusive, and toIndex, exclusive. - * - * @param fromIndex The start position. - * @param toIndex The end position (exclusive). - * @return The sub-list. - */ - @Override - public RouteList subList(int fromIndex, int toIndex) { - return new RouteList(getDelegate().subList(fromIndex, toIndex)); - } + /** The index of the last route used in the round-robin mode. */ + private volatile int lastIndex; + + /** Constructor. */ + public RouteList() { + super(new CopyOnWriteArrayList<>()); + this.lastIndex = -1; + } + + /** + * Constructor. + * + * @param delegate The delegate list. + */ + public RouteList(List delegate) { + super(new CopyOnWriteArrayList<>(delegate)); + this.lastIndex = -1; + } + + /** + * Returns the best route match for a given call. + * + * @param request The request to score. + * @param response The response to score. + * @param requiredScore The minimum score required to have a match. + * @return The best route match or null. + */ + public Route getBest(Request request, Response response, float requiredScore) { + Route result = null; + float bestScore = 0F; + float score; + + for (Route current : this) { + score = current.score(request, response); + + if ((score > bestScore) && (score >= requiredScore)) { + bestScore = score; + result = current; + } + } + + return result; + } + + /** + * Returns the first route match for a given call. + * + * @param request The request to score. + * @param response The response to score. + * @param requiredScore The minimum score required to have a match. + * @return The first route match or null. + */ + public Route getFirst(Request request, Response response, float requiredScore) { + for (Route current : this) { + if (current.score(request, response) >= requiredScore) { + return current; + } + } + + // No match found + return null; + } + + /** + * Returns the last route match for a given call. + * + * @param request The request to score. + * @param response The response to score. + * @param requiredScore The minimum score required to have a match. + * @return The last route match or null. + */ + public synchronized Route getLast(Request request, Response response, float requiredScore) { + for (int j = size() - 1; (j >= 0); j--) { + final Route route = get(j); + if (route.score(request, response) >= requiredScore) { + return route; + } + } + + // No match found + return null; + } + + /** + * Returns a next route match in a round-robin mode for a given call. + * + * @param request The request to score. + * @param response The response to score. + * @param requiredScore The minimum score required to have a match. + * @return A next route or null. + */ + public synchronized Route getNext(Request request, Response response, float requiredScore) { + if (!isEmpty()) { + for (final int initialIndex = this.lastIndex++; + initialIndex != this.lastIndex; + this.lastIndex++) { + if (this.lastIndex >= size()) { + this.lastIndex = 0; + } + + final Route route = get(this.lastIndex); + if (route.score(request, response) >= requiredScore) { + return route; + } + } + } + + // No match found + return null; + } + + /** + * Returns a random route match for a given call. Note that the current implementation doesn't + * uniformly return routes unless they all score above the required score. + * + * @param request The request to score. + * @param response The response to score. + * @param requiredScore The minimum score required to have a match. + * @return A random route or null. + */ + public synchronized Route getRandom(Request request, Response response, float requiredScore) { + int length = size(); + + if (length > 0) { + int j = ThreadLocalRandom.current().nextInt(length); + Route route = get(j); + + if (route.score(request, response) >= requiredScore) { + return route; + } + + boolean loopedAround = false; + + do { + if ((j == length) && !loopedAround) { + j = 0; + loopedAround = true; + } + + route = get(j++); + + if (route.score(request, response) >= requiredScore) { + return route; + } + } while ((j < length) || !loopedAround); + } + + // No match found + return null; + } + + /** + * Removes all routes to a given target. + * + * @param target The target Restlet to detach. + */ + public synchronized void removeAll(Restlet target) { + for (int i = size() - 1; i >= 0; i--) { + if (get(i).getNext() == target) { + remove(i); + } + } + } + + /** + * Returns a view of the portion of this list between the specified fromIndex, inclusive, and + * toIndex, exclusive. + * + * @param fromIndex The start position. + * @param toIndex The end position (exclusive). + * @return The sub-list. + */ + @Override + public RouteList subList(int fromIndex, int toIndex) { + return new RouteList(getDelegate().subList(fromIndex, toIndex)); + } } diff --git a/org.restlet/src/main/java/org/restlet/util/Series.java b/org.restlet/src/main/java/org/restlet/util/Series.java index e622ed12e8..847283ad93 100644 --- a/org.restlet/src/main/java/org/restlet/util/Series.java +++ b/org.restlet/src/main/java/org/restlet/util/Series.java @@ -1,25 +1,28 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; -import org.restlet.Context; - -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.logging.Level; +import org.restlet.Context; /** - * Modifiable list of entries with many helper methods. Note that this class - * uses the Parameter class as the template type. This allows you to use an - * instance of this class as any other java.util.List, in particular all the - * helper methods in java.util.Collections. - * + * Modifiable list of entries with many helper methods. Note that this class uses the Parameter + * class as the template type. This allows you to use an instance of this class as any other + * java.util.List, in particular all the helper methods in java.util.Collections. + * * @author Jerome Louvel * @param The contained type * @see org.restlet.data.Parameter @@ -27,76 +30,74 @@ * @see java.util.List */ public class Series> extends WrapperList { - /** - * A marker for empty values to differentiate from non existing values (null). - */ - public static final Object EMPTY_VALUE = new Object(); - - /** - * Returns an unmodifiable view of the specified series. Attempts to call a - * modification method will throw an UnsupportedOperationException. - * - * @param series The series for which an unmodifiable view should be returned. - * @return The unmodifiable view of the specified series. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Series unmodifiableSeries(final Series series) { - return new Series(series.entryClass, java.util.Collections.unmodifiableList(series.getDelegate())); - } - - /** The entry class. */ - private final Class entryClass; - - /** - * Constructor. - */ - public Series(Class entryClass) { - super(); - this.entryClass = entryClass; - } - - /** - * Constructor. - * - * @param initialCapacity The initial list capacity. - */ - public Series(Class entryClass, int initialCapacity) { - super(initialCapacity); - this.entryClass = entryClass; - } - - /** - * Constructor. - * - * @param delegate The delegate list. - */ - public Series(Class entryClass, List delegate) { - super(delegate); - this.entryClass = entryClass; - } - - /** - * Creates then adds a parameter at the end of the list. - * - * @param name The parameter name. - * @param value The parameter value. - * @return True (as per the general contract of the Collection.add method). - */ - public boolean add(String name, String value) { - return add(createEntry(name, value)); - } - - /** - * Copies the parameters whose name is a key in the given map.
- * If a matching parameter is found, its value is put in the map.
- * If multiple values are found, a list is created and set in the map. - * - * @param params The map controlling the copy. - */ - @SuppressWarnings("unchecked") - public void copyTo(Map params) { - NamedValue param; - Object currentValue = null; + /** A marker for empty values to differentiate from non-existing values (null). */ + public static final Object EMPTY_VALUE = new Object(); + + /** + * Returns an unmodifiable view of the specified series. Attempts to call a modification method + * will throw an UnsupportedOperationException. + * + * @param series The series for which an unmodifiable view should be returned. + * @return The unmodifiable view of the specified series. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static Series unmodifiableSeries( + final Series series) { + return new Series( + series.entryClass, java.util.Collections.unmodifiableList(series.getDelegate())); + } + + /** The entry class. */ + private final Class entryClass; + + /** Constructor. */ + public Series(Class entryClass) { + super(); + this.entryClass = entryClass; + } + + /** + * Constructor. + * + * @param initialCapacity The initial list capacity. + */ + public Series(Class entryClass, int initialCapacity) { + super(initialCapacity); + this.entryClass = entryClass; + } + + /** + * Constructor. + * + * @param delegate The delegate list. + */ + public Series(Class entryClass, List delegate) { + super(delegate); + this.entryClass = entryClass; + } + + /** + * Creates then adds a parameter at the end of the list. + * + * @param name The parameter name. + * @param value The parameter value. + * @return True (as per the general contract of the Collection.add method). + */ + public boolean add(String name, String value) { + return add(createEntry(name, value)); + } + + /** + * Copies the parameters whose name is a key in the given map.
+ * If a matching parameter is found, its value is put in the map.
+ * If multiple values are found, a list is created and set in the map. + * + * @param params The map controlling the copy. + */ + @SuppressWarnings("unchecked") + public void copyTo(Map params) { + NamedValue param; + Object currentValue = null; for (T t : this) { param = t; @@ -132,429 +133,426 @@ public void copyTo(Map params) { } } } - } - - /** - * Creates a new entry. - * - * @param name The name of the entry. - * @param value The value of the entry. - * @return A new entry. - */ - public T createEntry(String name, String value) { - try { - return this.entryClass.getConstructor(String.class, String.class).newInstance(name, value); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to create a series entry", e); - return null; - } - } - - /** - * Tests the equality of two string, potentially null, which a case sensitivity - * flag. - * - * @param value1 The first value. - * @param value2 The second value. - * @param ignoreCase Indicates if the test should be case insensitive. - * @return True if both values are equal. - */ - private boolean equals(String value1, String value2, boolean ignoreCase) { - boolean result = (value1 == value2); - - if (!result) { - if ((value1 != null) && (value2 != null)) { - if (ignoreCase) { - result = value1.equalsIgnoreCase(value2); - } else { - result = value1.equals(value2); - } - } - } - - return result; - } - - /** - * Returns the first parameter found with the given name. - * - * @param name The parameter name (case sensitive). - * @return The first parameter found with the given name. - */ - public T getFirst(String name) { - return getFirst(name, false); - } - - /** - * Returns the first parameter found with the given name. - * - * @param name The parameter name. - * @param ignoreCase Indicates if the name comparison is case insensitive. - * @return The first parameter found with the given name. - */ - public T getFirst(String name, boolean ignoreCase) { - for (T param : this) { - if (equals(param.getName(), name, ignoreCase)) { - return param; - } - } - - return null; - } - - /** - * Returns the value of the first parameter found with the given name. - * - * @param name The parameter name (case-sensitive). - * @return The value of the first parameter found with the given name. - */ - public String getFirstValue(String name) { - return getFirstValue(name, false); - } - - /** - * Returns the value of the first parameter found with the given name. - * - * @param name The parameter name. - * @param ignoreCase Indicates if the name comparison is case-sensitive. - * @return The value of the first parameter found with the given name. - */ - public String getFirstValue(String name, boolean ignoreCase) { - return getFirstValue(name, ignoreCase, null); - } - - /** - * Returns the value of the first parameter found with the given name. - * - * @param name The parameter name. - * @param ignoreCase Indicates if the name comparison is case sensitive. - * @param defaultValue The default value to return if no matching parameter - * found or if the parameter has a null value. - * @return The value of the first parameter found with the given name or the - * default value. - */ - public String getFirstValue(String name, boolean ignoreCase, String defaultValue) { - String result = defaultValue; - NamedValue param = getFirst(name, ignoreCase); - - if ((param != null) && (param.getValue() != null)) { - result = param.getValue(); - } - - return result; - } - - /** - * Returns the value of the first parameter found with the given name. - * - * @param name The parameter name (case-sensitive). - * @param defaultValue The default value to return if no matching parameter - * found or if the parameter has a null value. - * @return The value of the first parameter found with the given name or the - * default value. - */ - public String getFirstValue(String name, String defaultValue) { - return getFirstValue(name, false, defaultValue); - } - - /** - * Returns the set of parameter names (case-sensitive). - * - * @return The set of parameter names. - */ - public Set getNames() { - Set result = new HashSet(); - - for (NamedValue param : this) { - result.add(param.getName()); - } - - return result; - } - - /** - * Returns the values of the parameters with a given name. If multiple - * parameters with the same name are found, all values are concatenated and - * separated by a comma (like for HTTP message headers). - * - * @param name The parameter name (case insensitive). - * @return The values of the parameters with a given name. - */ - public String getValues(String name) { - return getValues(name, ",", true); - } - - /** - * Returns the parameter values with a given name. If multiple parameters with - * the same name are found, all values are concatenated and separated by the - * given separator. - * - * @param name The parameter name. - * @param separator The separator character. - * @param ignoreCase Indicates if the name comparison is case sensitive. - * @return The sequence of values. - */ - public String getValues(String name, String separator, boolean ignoreCase) { - String result = null; - StringBuilder sb = null; - - for (final T param : this) { - if ((ignoreCase && param.getName().equalsIgnoreCase(name)) || param.getName().equals(name)) { - if (sb == null) { - if (result == null) { - result = param.getValue(); - } else { - sb = new StringBuilder(); - sb.append(result).append(separator).append(param.getValue()); - } - } else { - sb.append(separator).append(param.getValue()); - } - } - } - - if (sb != null) { - result = sb.toString(); - } - - return result; - } - - /** - * Returns an array of all the values associated to the given parameter name. - * - * @param name The parameter name to match (case sensitive). - * @return The array of values. - */ - public String[] getValuesArray(String name) { - return getValuesArray(name, false); - } - - /** - * Returns an array of all the values associated to the given parameter name. - * - * @param name The parameter name to match. - * @param ignoreCase Indicates if the name comparison is case sensitive. - * @return The array of values. - */ - public String[] getValuesArray(String name, boolean ignoreCase) { - return getValuesArray(name, ignoreCase, null); - } - - /** - * Returns an array of all the values associated to the given parameter name. - * - * @param name The parameter name to match. - * @param ignoreCase Indicates if the name comparison is case-sensitive. - * @param defaultValue The default value to return if no matching parameter - * found or if the parameter has a null value. - * @return The array of values. - */ - public String[] getValuesArray(String name, boolean ignoreCase, String defaultValue) { - String[] result = null; - List params = subList(name, ignoreCase); - - if ((params.isEmpty()) && (defaultValue != null)) { - result = new String[1]; - result[0] = defaultValue; - } else { - result = new String[params.size()]; - - for (int i = 0; i < params.size(); i++) { - result[i] = params.get(i).getValue(); - } - } - - return result; - } - - /** - * Returns an array of all the values associated to the given parameter name. - * - * @param name The parameter name to match. - * @param defaultValue The default value to return if no matching parameter - * found or if the parameter has a null value. - * @return The array of values. - */ - public String[] getValuesArray(String name, String defaultValue) { - return getValuesArray(name, false, defaultValue); - } - - /** - * Returns a map of name, value pairs. The order of the map keys is respected - * based on the series order. When a name has multiple values, only the first - * one is put in the map. - * - * @return The map of name, value pairs. - */ - public Map getValuesMap() { - Map result = new LinkedHashMap(); - - for (NamedValue param : this) { - if (!result.containsKey(param.getName())) { - result.put(param.getName(), param.getValue()); - } - } - - return result; - } - - /** - * Removes all the parameters with a given name. - * - * @param name The parameter name (case-sensitive). - * @return True if the list changed. - */ - public boolean removeAll(String name) { - return removeAll(name, false); - } - - /** - * Removes all the parameters with a given name. - * - * @param name The parameter name. - * @param ignoreCase Indicates if the name comparison is case-insensitive. - * @return True if the list changed. - */ - public boolean removeAll(String name, boolean ignoreCase) { - boolean changed = false; - NamedValue param = null; - - for (Iterator iter = iterator(); iter.hasNext();) { - param = iter.next(); - - if (equals(param.getName(), name, ignoreCase)) { - iter.remove(); - changed = true; - } - } - - return changed; - } - - /** - * Removes from this list the first entry whose name equals the specified name - * ignoring the case. - * - * @param name The name of the entries to be removed (case-sensitive). - * @return false if no entry has been removed, true otherwise. - */ - public boolean removeFirst(String name) { - return removeFirst(name, false); - } - - /** - * Removes from this list the first entry whose name equals the specified name - * ignoring the case or not. - * - * @param name The name of the entries to be removed. - * @param ignoreCase Indicates if the name comparison is case-insensitive. - * @return false if no entry has been removed, true otherwise. - */ - public boolean removeFirst(String name, boolean ignoreCase) { - boolean changed = false; - NamedValue param = null; - - for (final Iterator iter = iterator(); iter.hasNext() && !changed;) { - param = iter.next(); - if (equals(param.getName(), name, ignoreCase)) { - iter.remove(); - changed = true; - } - } - - return changed; - } - - /** - * Replaces the value of the first parameter with the given name and removes all - * other parameters with the same name. The name matching is case-sensitive. - * - * @param name The parameter name. - * @param value The value to set. - * @return The parameter set or added. - */ - public T set(String name, String value) { - return set(name, value, false); - } - - /** - * Replaces the value of the first parameter with the given name and removes all - * other parameters with the same name. - * - * @param name The parameter name. - * @param value The value to set. - * @param ignoreCase Indicates if the name comparison is case-insensitive. - * @return The parameter set or added. - */ - public T set(String name, String value, boolean ignoreCase) { - T result = null; - T param = null; - boolean found = false; - - for (final Iterator iter = iterator(); iter.hasNext();) { - param = iter.next(); - - if (equals(param.getName(), name, ignoreCase)) { - if (found) { - // Remove other entries with the same name - iter.remove(); - } else { - // Change the value of the first matching entry - found = true; - param.setValue(value); - result = param; - } - } - } - - if (!found) { - add(name, value); - } - - return result; - } - - /** - * Returns a view of the portion of this list between the specified fromIndex, - * inclusive, and toIndex, exclusive. - * - * @param fromIndex The start position. - * @param toIndex The end position (exclusive). - * @return The sub-list. - */ - @Override - public Series subList(int fromIndex, int toIndex) { - return new Series(this.entryClass, getDelegate().subList(fromIndex, toIndex)); - } - - /** - * Returns a list of all the values associated to the parameter name. - * - * @param name The parameter name (case-sensitive). - * @return The list of values. - */ - public Series subList(String name) { - return subList(name, false); - } - - /** - * Returns a list of all the values associated to the parameter name. - * - * @param name The parameter name. - * @param ignoreCase Indicates if the name comparison is case-insensitive. - * @return The list of values. - */ - public Series subList(String name, boolean ignoreCase) { - Series result = new Series(this.entryClass); - - for (T param : this) { - if (equals(param.getName(), name, ignoreCase)) { - result.add(param); - } - } - - return result; - } + } + + /** + * Creates a new entry. + * + * @param name The name of the entry. + * @param value The value of the entry. + * @return A new entry. + */ + public T createEntry(String name, String value) { + try { + return this.entryClass + .getConstructor(String.class, String.class) + .newInstance(name, value); + } catch (Exception e) { + Context.getCurrentLogger().log(Level.WARNING, "Unable to create a series entry", e); + return null; + } + } + + /** + * Tests the equality of two string, potentially null, which a case sensitivity flag. + * + * @param value1 The first value. + * @param value2 The second value. + * @param ignoreCase Indicates if the test should be case-insensitive. + * @return True if both values are equal. + */ + private boolean equals(String value1, String value2, boolean ignoreCase) { + boolean result = (value1 == value2); + + if (!result) { + if ((value1 != null) && (value2 != null)) { + if (ignoreCase) { + result = value1.equalsIgnoreCase(value2); + } else { + result = value1.equals(value2); + } + } + } + + return result; + } + + /** + * Returns the first parameter found with the given name. + * + * @param name The parameter name (case-sensitive). + * @return The first parameter found with the given name. + */ + public T getFirst(String name) { + return getFirst(name, false); + } + + /** + * Returns the first parameter found with the given name. + * + * @param name The parameter name. + * @param ignoreCase Indicates if the name comparison is case-insensitive. + * @return The first parameter found with the given name. + */ + public T getFirst(String name, boolean ignoreCase) { + for (T param : this) { + if (equals(param.getName(), name, ignoreCase)) { + return param; + } + } + + return null; + } + + /** + * Returns the value of the first parameter found with the given name. + * + * @param name The parameter name (case-sensitive). + * @return The value of the first parameter found with the given name. + */ + public String getFirstValue(String name) { + return getFirstValue(name, false); + } + + /** + * Returns the value of the first parameter found with the given name. + * + * @param name The parameter name. + * @param ignoreCase Indicates if the name comparison is case-sensitive. + * @return The value of the first parameter found with the given name. + */ + public String getFirstValue(String name, boolean ignoreCase) { + return getFirstValue(name, ignoreCase, null); + } + + /** + * Returns the value of the first parameter found with the given name. + * + * @param name The parameter name. + * @param ignoreCase Indicates if the name comparison is case-sensitive. + * @param defaultValue The default value to return if no matching parameter found or if the + * parameter has a null value. + * @return The value of the first parameter found with the given name or the default value. + */ + public String getFirstValue(String name, boolean ignoreCase, String defaultValue) { + String result = defaultValue; + NamedValue param = getFirst(name, ignoreCase); + + if ((param != null) && (param.getValue() != null)) { + result = param.getValue(); + } + + return result; + } + + /** + * Returns the value of the first parameter found with the given name. + * + * @param name The parameter name (case-sensitive). + * @param defaultValue The default value to return if no matching parameter found or if the + * parameter has a null value. + * @return The value of the first parameter found with the given name or the default value. + */ + public String getFirstValue(String name, String defaultValue) { + return getFirstValue(name, false, defaultValue); + } + + /** + * Returns the set of parameter names (case-sensitive). + * + * @return The set of parameter names. + */ + public Set getNames() { + Set result = new HashSet(); + + for (NamedValue param : this) { + result.add(param.getName()); + } + + return result; + } + + /** + * Returns the values of the parameters with a given name. If multiple parameters with the same + * name are found, all values are concatenated and separated by a comma (like for HTTP message + * headers). + * + * @param name The parameter name (case-insensitive). + * @return The values of the parameters with a given name. + */ + public String getValues(String name) { + return getValues(name, ",", true); + } + + /** + * Returns the parameter values with a given name. If multiple parameters with the same name are + * found, all values are concatenated and separated by the given separator. + * + * @param name The parameter name. + * @param separator The separator character. + * @param ignoreCase Indicates if the name comparison is case-sensitive. + * @return The sequence of values. + */ + public String getValues(String name, String separator, boolean ignoreCase) { + String result = null; + StringBuilder sb = null; + + for (final T param : this) { + if ((ignoreCase && param.getName().equalsIgnoreCase(name)) + || param.getName().equals(name)) { + if (sb == null) { + if (result == null) { + result = param.getValue(); + } else { + sb = new StringBuilder(); + sb.append(result).append(separator).append(param.getValue()); + } + } else { + sb.append(separator).append(param.getValue()); + } + } + } + + if (sb != null) { + result = sb.toString(); + } + + return result; + } + + /** + * Returns an array of all the values associated with the given parameter name. + * + * @param name The parameter name to match (case-sensitive). + * @return The array of values. + */ + public String[] getValuesArray(String name) { + return getValuesArray(name, false); + } + + /** + * Returns an array of all the values associated with the given parameter name. + * + * @param name The parameter name to match. + * @param ignoreCase Indicates if the name comparison is case-sensitive. + * @return The array of values. + */ + public String[] getValuesArray(String name, boolean ignoreCase) { + return getValuesArray(name, ignoreCase, null); + } + + /** + * Returns an array of all the values associated with the given parameter name. + * + * @param name The parameter name to match. + * @param ignoreCase Indicates if the name comparison is case-sensitive. + * @param defaultValue The default value to return if no matching parameter found or if the + * parameter has a null value. + * @return The array of values. + */ + public String[] getValuesArray(String name, boolean ignoreCase, String defaultValue) { + String[] result = null; + List params = subList(name, ignoreCase); + + if ((params.isEmpty()) && (defaultValue != null)) { + result = new String[1]; + result[0] = defaultValue; + } else { + result = new String[params.size()]; + + for (int i = 0; i < params.size(); i++) { + result[i] = params.get(i).getValue(); + } + } + + return result; + } + + /** + * Returns an array of all the values associated with the given parameter name. + * + * @param name The parameter name to match. + * @param defaultValue The default value to return if no matching parameter found or if the + * parameter has a null value. + * @return The array of values. + */ + public String[] getValuesArray(String name, String defaultValue) { + return getValuesArray(name, false, defaultValue); + } + + /** + * Returns a map of name, value pairs. The order of the map keys is respected based on the + * series order. When a name has multiple values, only the first one is put in the map. + * + * @return The map of name, value pairs. + */ + public Map getValuesMap() { + Map result = new LinkedHashMap(); + + for (NamedValue param : this) { + if (!result.containsKey(param.getName())) { + result.put(param.getName(), param.getValue()); + } + } + + return result; + } + + /** + * Removes all the parameters with a given name. + * + * @param name The parameter name (case-sensitive). + * @return True if the list changed. + */ + public boolean removeAll(String name) { + return removeAll(name, false); + } + + /** + * Removes all the parameters with a given name. + * + * @param name The parameter name. + * @param ignoreCase Indicates if the name comparison is case-insensitive. + * @return True if the list changed. + */ + public boolean removeAll(String name, boolean ignoreCase) { + boolean changed = false; + NamedValue param = null; + + for (Iterator iter = iterator(); iter.hasNext(); ) { + param = iter.next(); + + if (equals(param.getName(), name, ignoreCase)) { + iter.remove(); + changed = true; + } + } + + return changed; + } + + /** + * Removes from this list the first entry whose name equals the specified name ignoring the + * case. + * + * @param name The name of the entries to be removed (case-sensitive). + * @return false if no entry has been removed, true otherwise. + */ + public boolean removeFirst(String name) { + return removeFirst(name, false); + } + + /** + * Removes from this list the first entry whose name equals the specified name ignoring the case + * or not. + * + * @param name The name of the entries to be removed. + * @param ignoreCase Indicates if the name comparison is case-insensitive. + * @return false if no entry has been removed, true otherwise. + */ + public boolean removeFirst(String name, boolean ignoreCase) { + boolean changed = false; + NamedValue param = null; + + for (final Iterator iter = iterator(); iter.hasNext() && !changed; ) { + param = iter.next(); + if (equals(param.getName(), name, ignoreCase)) { + iter.remove(); + changed = true; + } + } + + return changed; + } + + /** + * Replaces the value of the first parameter with the given name and removes all other + * parameters with the same name. The name matching is case-sensitive. + * + * @param name The parameter name. + * @param value The value to set. + * @return The parameter set or added. + */ + public T set(String name, String value) { + return set(name, value, false); + } + + /** + * Replaces the value of the first parameter with the given name and removes all other + * parameters with the same name. + * + * @param name The parameter name. + * @param value The value to set. + * @param ignoreCase Indicates if the name comparison is case-insensitive. + * @return The parameter set or added. + */ + public T set(String name, String value, boolean ignoreCase) { + T result = null; + T param = null; + boolean found = false; + + for (final Iterator iter = iterator(); iter.hasNext(); ) { + param = iter.next(); + + if (equals(param.getName(), name, ignoreCase)) { + if (found) { + // Remove other entries with the same name + iter.remove(); + } else { + // Change the value of the first matching entry + found = true; + param.setValue(value); + result = param; + } + } + } + + if (!found) { + add(name, value); + } + + return result; + } + + /** + * Returns a view of the portion of this list between the specified fromIndex, inclusive, and + * toIndex, exclusive. + * + * @param fromIndex The start position. + * @param toIndex The end position (exclusive). + * @return The sub-list. + */ + @Override + public Series subList(int fromIndex, int toIndex) { + return new Series(this.entryClass, getDelegate().subList(fromIndex, toIndex)); + } + + /** + * Returns a list of all the values associated with the parameter name. + * + * @param name The parameter name (case-sensitive). + * @return The list of values. + */ + public Series subList(String name) { + return subList(name, false); + } + + /** + * Returns a list of all the values associated with the parameter name. + * + * @param name The parameter name. + * @param ignoreCase Indicates if the name comparison is case-insensitive. + * @return The list of values. + */ + public Series subList(String name, boolean ignoreCase) { + Series result = new Series(this.entryClass); + + for (T param : this) { + if (equals(param.getName(), name, ignoreCase)) { + result.add(param); + } + } + return result; + } } diff --git a/org.restlet/src/main/java/org/restlet/util/ServerList.java b/org.restlet/src/main/java/org/restlet/util/ServerList.java index d6a25f00bc..8af54c934a 100644 --- a/org.restlet/src/main/java/org/restlet/util/ServerList.java +++ b/org.restlet/src/main/java/org/restlet/util/ServerList.java @@ -1,138 +1,133 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; +import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Context; import org.restlet.Restlet; import org.restlet.Server; import org.restlet.data.Protocol; -import java.util.concurrent.CopyOnWriteArrayList; - /** * Modifiable list of server connectors. - * + * * @author Jerome Louvel */ public final class ServerList extends WrapperList { - /** The context. */ - private volatile Context context; - - /** The next Restlet of added servers. */ - private volatile Restlet next; - - /** - * Constructor. - * - * @param context The context. - * @param next The next Restlet of added servers. - */ - public ServerList(Context context, Restlet next) { - super(new CopyOnWriteArrayList<>()); - this.context = context; - this.next = next; - } - - /** - * Adds a new server connector in the map supporting the given protocol. - * - * @param protocol The connector protocol. - * @return The added server. - */ - public Server add(Protocol protocol) { - Server result = new Server(protocol, null, protocol.getDefaultPort(), getNext()); - add(result); - return result; - } - - /** - * Adds a new server connector in the map supporting the given protocol on the - * specified port. - * - * @param protocol The connector protocol. - * @param port The listening port. - * @return The added server. - */ - public Server add(Protocol protocol, int port) { - Server result = new Server(protocol, null, port, getNext()); - add(result); - return result; - } - - /** - * Adds a new server connector in the map supporting the given protocol on the - * specified IP address and port. - * - * @param protocol The connector protocol. - * @param address The optional listening IP address (useful if multiple IP - * addresses available). - * @param port The listening port. - * @return The added server. - */ - public Server add(Protocol protocol, String address, int port) { - Server result = new Server(protocol, address, port, getNext()); - add(result); - return result; - } - - /** - * Adds a server at the end of the list. - * - * @return True (as per the general contract of the Collection.add method). - */ - @Override - public boolean add(Server server) { - // Set the server's context, if the server does not have already one. - if (server.getContext() == null) { - server.setContext(getContext().createChildContext()); - } - - server.setNext(getNext()); - return super.add(server); - } - - /** - * Returns the context. - * - * @return The context. - */ - public Context getContext() { - return this.context; - } - - /** - * Returns the next Restlet. - * - * @return The next Restlet. - */ - public Restlet getNext() { - return this.next; - } - - /** - * Sets the context. - * - * @param context The context. - */ - public void setContext(Context context) { - this.context = context; - } - - /** - * Sets the next Restlet. - * - * @param next The next Restlet. - */ - public void setNext(Restlet next) { - this.next = next; - } - + /** The context. */ + private volatile Context context; + + /** The next Restlet of added servers. */ + private volatile Restlet next; + + /** + * Constructor. + * + * @param context The context. + * @param next The next Restlet of added servers. + */ + public ServerList(Context context, Restlet next) { + super(new CopyOnWriteArrayList<>()); + this.context = context; + this.next = next; + } + + /** + * Adds a new server connector in the map supporting the given protocol. + * + * @param protocol The connector protocol. + * @return The added server. + */ + public Server add(Protocol protocol) { + Server result = new Server(protocol, null, protocol.getDefaultPort(), getNext()); + add(result); + return result; + } + + /** + * Adds a new server connector in the map supporting the given protocol on the specified port. + * + * @param protocol The connector protocol. + * @param port The listening port. + * @return The added server. + */ + public Server add(Protocol protocol, int port) { + Server result = new Server(protocol, null, port, getNext()); + add(result); + return result; + } + + /** + * Adds a new server connector in the map supporting the given protocol on the specified IP + * address and port. + * + * @param protocol The connector protocol. + * @param address The optional listening IP address (useful if multiple IP addresses available). + * @param port The listening port. + * @return The added server. + */ + public Server add(Protocol protocol, String address, int port) { + Server result = new Server(protocol, address, port, getNext()); + add(result); + return result; + } + + /** + * Adds a server at the end of the list. + * + * @return True (as per the general contract of the Collection.add method). + */ + @Override + public boolean add(Server server) { + // Set the server's context if the server does not already have one. + if (server.getContext() == null) { + server.setContext(getContext().createChildContext()); + } + + server.setNext(getNext()); + return super.add(server); + } + + /** + * Returns the context. + * + * @return The context. + */ + public Context getContext() { + return this.context; + } + + /** + * Returns the next Restlet. + * + * @return The next Restlet. + */ + public Restlet getNext() { + return this.next; + } + + /** + * Sets the context. + * + * @param context The context. + */ + public void setContext(Context context) { + this.context = context; + } + + /** + * Sets the next Restlet. + * + * @param next The next Restlet. + */ + public void setNext(Restlet next) { + this.next = next; + } } diff --git a/org.restlet/src/main/java/org/restlet/util/ServiceList.java b/org.restlet/src/main/java/org/restlet/util/ServiceList.java index 83033213ae..e9e22151f9 100644 --- a/org.restlet/src/main/java/org/restlet/util/ServiceList.java +++ b/org.restlet/src/main/java/org/restlet/util/ServiceList.java @@ -1,186 +1,183 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; -import org.restlet.Context; -import org.restlet.service.Service; - import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; +import org.restlet.Context; +import org.restlet.service.Service; /** * Modifiable list of services. - * + * * @author Jerome Louvel */ public final class ServiceList extends WrapperList { - /** The context. */ - private volatile Context context; - - /** - * Constructor. - * - * @param context The context. - */ - public ServiceList(Context context) { - super(new CopyOnWriteArrayList<>()); - this.context = context; - } - - @Override - public void add(int index, Service service) { - service.setContext(getContext()); - super.add(index, service); - } - - @Override - public boolean add(Service service) { - service.setContext(getContext()); - return super.add(service); - } - - @Override - public boolean addAll(Collection services) { - if (services != null) { - for (Service service : services) { - service.setContext(getContext()); - } - } - - return super.addAll(services); - } - - @Override - public boolean addAll(int index, Collection services) { - if (services != null) { - for (Service service : services) { - service.setContext(getContext()); - } - } - - return super.addAll(index, services); - } - - /** - * Returns a service matching a given service class. - * - * @param The service type. - * @param clazz The service class to match. - * @return The matched service instance. - */ - @SuppressWarnings("unchecked") - public T get(Class clazz) { - for (Service service : this) { - if (clazz.isAssignableFrom(service.getClass())) { - return (T) service; - } - } - - return null; - } - - /** - * Returns the context. - * - * @return The context. - */ - public Context getContext() { - return this.context; - } - - /** - * Sets the list of services. - * - * @param services The list of services. - */ - public synchronized void set(List services) { - clear(); - - if (services != null) { - addAll(services); - } - } - - /** - * Replaces or adds a service. The replacement is based on the service class. - * - * @param newService The new service to set. - */ - public synchronized void set(Service newService) { - List services = new CopyOnWriteArrayList(); - Service service; - boolean replaced = false; - - for (int i = 0; (i < size()); i++) { - service = get(i); - - if (service != null) { - if (service.getClass().isAssignableFrom(newService.getClass())) { - try { - service.stop(); - } catch (Exception e) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to stop service replaced", e); - } - - services.add(newService); - replaced = true; - } else { - services.add(service); - } - } - } - - if (!replaced) { - services.add(newService); - } - - set(services); - } - - /** - * Sets the context. By default, it also updates the context of already - * registered services. - * - * @param context The context. - */ - public void setContext(Context context) { - this.context = context; - - for (Service service : this) { - service.setContext(context); - } - } - - /** - * Starts each service. - * - * @throws Exception - */ - public void start() throws Exception { - for (Service service : this) { - service.start(); - } - } - - /** - * Stops each service. - * - * @throws Exception - */ - public void stop() throws Exception { - for (Service service : this) { - service.stop(); - } - } - + /** The context. */ + private volatile Context context; + + /** + * Constructor. + * + * @param context The context. + */ + public ServiceList(Context context) { + super(new CopyOnWriteArrayList<>()); + this.context = context; + } + + @Override + public void add(int index, Service service) { + service.setContext(getContext()); + super.add(index, service); + } + + @Override + public boolean add(Service service) { + service.setContext(getContext()); + return super.add(service); + } + + @Override + public boolean addAll(Collection services) { + if (services != null) { + for (Service service : services) { + service.setContext(getContext()); + } + } + + return super.addAll(services); + } + + @Override + public boolean addAll(int index, Collection services) { + if (services != null) { + for (Service service : services) { + service.setContext(getContext()); + } + } + + return super.addAll(index, services); + } + + /** + * Returns a service matching a given service class. + * + * @param The service type. + * @param clazz The service class to match. + * @return The matched service instance. + */ + @SuppressWarnings("unchecked") + public T get(Class clazz) { + for (Service service : this) { + if (clazz.isAssignableFrom(service.getClass())) { + return (T) service; + } + } + + return null; + } + + /** + * Returns the context. + * + * @return The context. + */ + public Context getContext() { + return this.context; + } + + /** + * Sets the list of services. + * + * @param services The list of services. + */ + public synchronized void set(List services) { + clear(); + + if (services != null) { + addAll(services); + } + } + + /** + * Replaces or adds a service. The replacement is based on the service class. + * + * @param newService The new service to set. + */ + public synchronized void set(Service newService) { + List services = new CopyOnWriteArrayList(); + Service service; + boolean replaced = false; + + for (int i = 0; (i < size()); i++) { + service = get(i); + + if (service != null) { + if (service.getClass().isAssignableFrom(newService.getClass())) { + try { + service.stop(); + } catch (Exception e) { + Context.getCurrentLogger() + .log(Level.WARNING, "Unable to stop service replaced", e); + } + + services.add(newService); + replaced = true; + } else { + services.add(service); + } + } + } + + if (!replaced) { + services.add(newService); + } + + set(services); + } + + /** + * Sets the context. By default, it also updates the context of already registered services. + * + * @param context The context. + */ + public void setContext(Context context) { + this.context = context; + + for (Service service : this) { + service.setContext(context); + } + } + + /** + * Starts each service. + * + * @throws Exception + */ + public void start() throws Exception { + for (Service service : this) { + service.start(); + } + } + + /** + * Stops each service. + * + * @throws Exception + */ + public void stop() throws Exception { + for (Service service : this) { + service.stop(); + } + } } diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperList.java b/org.restlet/src/main/java/org/restlet/util/WrapperList.java index 6a6e86558d..eb5898ad01 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperList.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperList.java @@ -1,329 +1,316 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Vector; /** - * List wrapper. Modifiable list that delegates all methods to a wrapped list. - * This allows an easy sub-classing. By default, it wraps a thread-safe - * {@link Vector} instance. - * + * List wrapper. Modifiable list that delegates all methods to a wrapped list. This allows an easy + * sub-classing. By default, it wraps a thread-safe {@link Vector} instance. + * * @author Jerome Louvel - * @see The decorator (aka - * wrapper) pattern + * @see The decorator (aka wrapper) pattern * @see java.util.Collections * @see java.util.List */ public class WrapperList implements List, Iterable { - /** The delegate list. */ - private final List delegate; - - /** - * Constructor. Uses a default initial capacity of 10 items. - */ - public WrapperList() { - this(10); - } - - /** - * Constructor. - * - * @param initialCapacity The initial list capacity. - */ - public WrapperList(int initialCapacity) { - this(new Vector(initialCapacity)); - } - - /** - * Constructor. - * - * @param delegate The delegate list. - */ - public WrapperList(List delegate) { - this.delegate = delegate; - } - - /** - * Adds a element at the end of the list. - * - * @return True (as per the general contract of the Collection.add method). - */ - public boolean add(E element) { - return getDelegate().add(element); - } - - /** - * Inserts the specified element at the specified position in this list. - * - * @param index The insertion position. - * @param element The element to insert. - */ - public void add(int index, E element) { - getDelegate().add(index, element); - } - - /** - * Appends all of the elements in the specified collection to the end of this - * list. - * - * @param elements The collection of elements to append. - */ - public boolean addAll(Collection elements) { - return getDelegate().addAll(elements); - } - - /** - * Inserts all of the elements in the specified collection into this list at the - * specified position. - * - * @param index The insertion position. - * @param elements The collection of elements to insert. - */ - public boolean addAll(int index, Collection elements) { - return getDelegate().addAll(index, elements); - } - - /** - * Removes all of the elements from this list. - */ - public void clear() { - getDelegate().clear(); - } - - /** - * Returns true if this list contains the specified element. - * - * @param element The element to find. - * @return True if this list contains the specified element. - */ - public boolean contains(Object element) { - return getDelegate().contains(element); - } - - /** - * Returns true if this list contains all of the elements of the specified - * collection. - * - * @param elements The collection of elements to find. - * @return True if this list contains all of the elements of the specified - * collection. - */ - public boolean containsAll(Collection elements) { - return getDelegate().containsAll(elements); - } - - /** - * Compares the specified object with this list for equality. - * - * @param o The object to be compared for equality with this list. - * @return True if the specified object is equal to this list. - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o instanceof List) { - List that = (List) o; - return Arrays.equals(this.toArray(), that.toArray()); - } - - return false; - } - - /** - * Returns the element at the specified position in this list. - * - * @param index The element position. - * @return The element at the specified position in this list. - */ - public E get(int index) { - return getDelegate().get(index); - } - - /** - * Returns the delegate list. - * - * @return The delegate list. - */ - protected List getDelegate() { - return this.delegate; - } - - /** - * Returns the hash code value for this list. - * - * @return The hash code value for this list. - */ - @Override - public int hashCode() { - return getDelegate().hashCode(); - } - - /** - * Returns the index in this list of the first occurrence of the specified - * element, or -1 if this list does not contain this element. - * - * @param element The element to find. - * @return The index of the first occurrence. - */ - public int indexOf(Object element) { - return getDelegate().indexOf(element); - } - - /** - * Returns true if this list contains no elements. - */ - public boolean isEmpty() { - return getDelegate().isEmpty(); - } - - /** - * Returns an iterator over the elements in this list in proper sequence. - * - * @return An iterator over the elements in this list in proper sequence. - */ - public Iterator iterator() { - return getDelegate().iterator(); - } - - /** - * Returns the index in this list of the last occurrence of the specified - * element, or -1 if this list does not contain this element. - */ - public int lastIndexOf(Object element) { - return getDelegate().lastIndexOf(element); - } - - /** - * Returns a list iterator of the elements in this list (in proper sequence). - * - * @return A list iterator of the elements in this list (in proper sequence). - */ - public ListIterator listIterator() { - return getDelegate().listIterator(); - } - - /** - * Returns a list iterator of the elements in this list (in proper sequence), - * starting at the specified position in this list. - * - * @param index The starting position. - */ - public ListIterator listIterator(int index) { - return getDelegate().listIterator(index); - } - - /** - * Removes the element at the specified position in this list. - * - * @return The removed element. - */ - public E remove(int index) { - return getDelegate().remove(index); - } - - /** - * Removes the first occurrence in this list of the specified element. - * - * @return True if the list was changed. - */ - public boolean remove(Object element) { - return getDelegate().remove(element); - } - - /** - * Removes from this list all the elements that are contained in the specified - * collection. - * - * @param elements The collection of element to remove. - * @return True if the list changed. - */ - public boolean removeAll(Collection elements) { - return getDelegate().removeAll(elements); - } - - /** - * RemovesRetains only the elements in this list that are contained in the - * specified collection. - * - * @param elements The collection of element to retain. - * @return True if the list changed. - */ - public boolean retainAll(Collection elements) { - return getDelegate().retainAll(elements); - } - - /** - * Replaces the element at the specified position in this list with the - * specified element. - * - * @param index The position of the element to replace. - * @param element The new element. - */ - public E set(int index, E element) { - return getDelegate().set(index, element); - } - - /** - * Returns the number of elements in this list. - * - * @return The number of elements in this list. - */ - public int size() { - return getDelegate().size(); - } - - /** - * Returns a view of the portion of this list between the specified fromIndex, - * inclusive, and toIndex, exclusive. - * - * @param fromIndex The start position. - * @param toIndex The end position (exclusive). - * @return The sub-list. - */ - public List subList(int fromIndex, int toIndex) { - return new WrapperList(getDelegate().subList(fromIndex, toIndex)); - } - - /** - * Returns an array containing all of the elements in this list in proper - * sequence. - * - * @return An array containing all of the elements in this list in proper - * sequence. - */ - public Object[] toArray() { - return getDelegate().toArray(); - } - - /** - * Returns an array containing all of the elements in this list in proper - * sequence; the runtime type of the returned array is that of the specified - * array. - * - * @param a The sample array. - */ - public T[] toArray(T[] a) { - return getDelegate().toArray(a); - } - - /** - * Returns a string representation of the list. - * - * @return A string representation of the list. - */ - @Override - public String toString() { - return getDelegate().toString(); - } + /** The delegate list. */ + private final List delegate; + + /** Constructor. Uses a default initial capacity of 10 items. */ + public WrapperList() { + this(10); + } + + /** + * Constructor. + * + * @param initialCapacity The initial list capacity. + */ + public WrapperList(int initialCapacity) { + this(new Vector(initialCapacity)); + } + + /** + * Constructor. + * + * @param delegate The delegate list. + */ + public WrapperList(List delegate) { + this.delegate = delegate; + } + + /** + * Adds a element at the end of the list. + * + * @return True (as per the general contract of the Collection.add method). + */ + public boolean add(E element) { + return getDelegate().add(element); + } + + /** + * Inserts the specified element at the specified position in this list. + * + * @param index The insertion position. + * @param element The element to insert. + */ + public void add(int index, E element) { + getDelegate().add(index, element); + } + + /** + * Appends all of the elements in the specified collection to the end of this list. + * + * @param elements The collection of elements to append. + */ + public boolean addAll(Collection elements) { + return getDelegate().addAll(elements); + } + + /** + * Inserts all of the elements in the specified collection into this list at the specified + * position. + * + * @param index The insertion position. + * @param elements The collection of elements to insert. + */ + public boolean addAll(int index, Collection elements) { + return getDelegate().addAll(index, elements); + } + + /** Removes all of the elements from this list. */ + public void clear() { + getDelegate().clear(); + } + + /** + * Returns true if this list contains the specified element. + * + * @param element The element to find. + * @return True if this list contains the specified element. + */ + public boolean contains(Object element) { + return getDelegate().contains(element); + } + + /** + * Returns true if this list contains all of the elements of the specified collection. + * + * @param elements The collection of elements to find. + * @return True if this list contains all of the elements of the specified collection. + */ + public boolean containsAll(Collection elements) { + return getDelegate().containsAll(elements); + } + + /** + * Compares the specified object with this list for equality. + * + * @param o The object to be compared for equality with this list. + * @return True if the specified object is equal to this list. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof List) { + List that = (List) o; + return Arrays.equals(this.toArray(), that.toArray()); + } + + return false; + } + + /** + * Returns the element at the specified position in this list. + * + * @param index The element position. + * @return The element at the specified position in this list. + */ + public E get(int index) { + return getDelegate().get(index); + } + + /** + * Returns the delegate list. + * + * @return The delegate list. + */ + protected List getDelegate() { + return this.delegate; + } + + /** + * Returns the hash code value for this list. + * + * @return The hash code value for this list. + */ + @Override + public int hashCode() { + return getDelegate().hashCode(); + } + + /** + * Returns the index in this list of the first occurrence of the specified element, or -1 if + * this list does not contain this element. + * + * @param element The element to find. + * @return The index of the first occurrence. + */ + public int indexOf(Object element) { + return getDelegate().indexOf(element); + } + + /** Returns true if this list contains no elements. */ + public boolean isEmpty() { + return getDelegate().isEmpty(); + } + + /** + * Returns an iterator over the elements in this list in proper sequence. + * + * @return An iterator over the elements in this list in proper sequence. + */ + public Iterator iterator() { + return getDelegate().iterator(); + } + + /** + * Returns the index in this list of the last occurrence of the specified element, or -1 if this + * list does not contain this element. + */ + public int lastIndexOf(Object element) { + return getDelegate().lastIndexOf(element); + } + + /** + * Returns a list iterator of the elements in this list (in proper sequence). + * + * @return A list iterator of the elements in this list (in proper sequence). + */ + public ListIterator listIterator() { + return getDelegate().listIterator(); + } + + /** + * Returns a list iterator of the elements in this list (in proper sequence), starting at the + * specified position in this list. + * + * @param index The starting position. + */ + public ListIterator listIterator(int index) { + return getDelegate().listIterator(index); + } + + /** + * Removes the element at the specified position in this list. + * + * @return The removed element. + */ + public E remove(int index) { + return getDelegate().remove(index); + } + + /** + * Removes the first occurrence in this list of the specified element. + * + * @return True if the list was changed. + */ + public boolean remove(Object element) { + return getDelegate().remove(element); + } + + /** + * Removes from this list all the elements that are contained in the specified collection. + * + * @param elements The collection of elements to remove. + * @return True if the list changed. + */ + public boolean removeAll(Collection elements) { + return getDelegate().removeAll(elements); + } + + /** + * RemovesRetains only the elements in this list that are contained in the specified collection. + * + * @param elements The collection of elements to retain. + * @return True if the list changed. + */ + public boolean retainAll(Collection elements) { + return getDelegate().retainAll(elements); + } + + /** + * Replaces the element at the specified position in this list with the specified element. + * + * @param index The position of the element to replace. + * @param element The new element. + */ + public E set(int index, E element) { + return getDelegate().set(index, element); + } + + /** + * Returns the number of elements in this list. + * + * @return The number of elements in this list. + */ + public int size() { + return getDelegate().size(); + } + + /** + * Returns a view of the portion of this list between the specified fromIndex, inclusive, and + * toIndex, exclusive. + * + * @param fromIndex The start position. + * @param toIndex The end position (exclusive). + * @return The sub-list. + */ + public List subList(int fromIndex, int toIndex) { + return new WrapperList(getDelegate().subList(fromIndex, toIndex)); + } + + /** + * Returns an array containing all the elements in this list in a proper sequence. + * + * @return An array containing all the elements in this list in proper sequence. + */ + public Object[] toArray() { + return getDelegate().toArray(); + } + + /** + * Returns an array containing all the elements in this list in a proper sequence; the runtime + * type of the returned array is that of the specified array. + * + * @param a The sample array. + */ + public T[] toArray(T[] a) { + return getDelegate().toArray(a); + } + + /** + * Returns a string representation of the list. + * + * @return A string representation of the list. + */ + @Override + public String toString() { + return getDelegate().toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java index 2c46ea8352..209783bec2 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; import java.util.Collection; @@ -15,183 +14,173 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Map wrapper. Modifiable map that delegates all methods to a wrapped map. This - * allows an easy subclassing. - * + * Map wrapper. Modifiable map that delegates all methods to a wrapped map. This allows an easy + * subclassing. + * * @author Jerome Louvel - * @see The decorator (aka - * wrapper) pattern + * @see The decorator (aka wrapper) pattern * @see java.util.Collections * @see java.util.List */ public class WrapperMap implements Map { - /** The delegate map. */ - private final Map delegate; - - /** - * Constructor. - */ - public WrapperMap() { - this.delegate = new ConcurrentHashMap(); - } - - /** - * Constructor. - * - * @param delegate The delegate list. - */ - public WrapperMap(Map delegate) { - this.delegate = delegate; - } - - /** - * Removes all mappings from this Map. - */ - public void clear() { - getDelegate().clear(); - } - - /** - * Returns true if this map contains a mapping for the specified key. - * - * @param key The key to look up. - * @return True if this map contains a mapping for the specified key. - */ - public boolean containsKey(Object key) { - return getDelegate().containsKey(key); - } - - /** - * Returns true if this map maps one or more keys to the specified value. - * - * @param value The value to look up - * @return True if this map maps one or more keys to the specified value. - */ - public boolean containsValue(Object value) { - return getDelegate().containsValue(value); - } - - /** - * Returns a set view of the mappings contained in this map. - * - * @return A set view of the mappings contained in this map. - */ - public Set> entrySet() { - return getDelegate().entrySet(); - } - - /** - * Compares the specified object with this map for equality. - * - * @param o Object to be compared for equality with this map. - * @return True if the specified object is equal to this map. - */ - @Override - public boolean equals(Object o) { - return getDelegate().equals(o); - } - - /** - * Returns the value to which this map maps the specified key. - * - * @param key Key whose associated value is to be returned. - * @return The value to which this map maps the specified key, or null if the - * map contains no mapping for this key. - */ - public V get(Object key) { - return getDelegate().get(key); - } - - /** - * Returns the delegate list. - * - * @return The delegate list. - */ - protected Map getDelegate() { - return this.delegate; - } - - /** - * Returns the hash code value for this map. - * - * @return The hash code value for this map. - */ - @Override - public int hashCode() { - return getDelegate().hashCode(); - } - - /** - * Returns true if this map contains no key-value mappings. - * - * @return True if this map contains no key-value mappings. - */ - public boolean isEmpty() { - return getDelegate().isEmpty(); - } - - /** - * Returns a set view of the keys contained in this map. - * - * @return A set view of the keys contained in this map. - */ - public Set keySet() { - return getDelegate().keySet(); - } - - /** - * Associates the specified value with the specified key in this map (optional - * operation). - * - * @param key Key with which the specified value is to be associated. - * @param value Value to be associated with the specified key. - * @return The previous value associated with specified key, or null if there - * was no mapping for key. A null return can also indicate that the map - * previously associated null with the specified key, if the - * implementation supports null values. - */ - public V put(K key, V value) { - return getDelegate().put(key, value); - } - - /** - * Copies all of the mappings from the specified map to this map (optional - * operation). - * - * @param t Mappings to be stored in this map. - */ - public void putAll(Map t) { - getDelegate().putAll(t); - } - - /** - * Removes the mapping for this key from this map if it is present (optional - * operation). - * - * @param key Key whose mapping is to be removed from the map. - * @return The previous value associated with specified key, or null if there - * was no mapping for key. - */ - public V remove(Object key) { - return getDelegate().remove(key); - } - - /** - * Returns the number of key-value mappings in this map. - * - * @return The number of key-value mappings in this map. - */ - public int size() { - return getDelegate().size(); - } - - /** - * Returns a collection view of the values contained in this map. - * - * @return A collection view of the values contained in this map. - */ - public Collection values() { - return getDelegate().values(); - } - + /** The delegate map. */ + private final Map delegate; + + /** Constructor. */ + public WrapperMap() { + this.delegate = new ConcurrentHashMap<>(); + } + + /** + * Constructor. + * + * @param delegate The delegate list. + */ + public WrapperMap(Map delegate) { + this.delegate = delegate; + } + + /** Removes all mappings from this Map. */ + public void clear() { + getDelegate().clear(); + } + + /** + * Returns true if this map contains a mapping for the specified key. + * + * @param key The key to look up. + * @return True if this map contains a mapping for the specified key. + */ + public boolean containsKey(Object key) { + return getDelegate().containsKey(key); + } + + /** + * Returns true if this map maps one or more keys to the specified value. + * + * @param value The value to look up + * @return True if this map maps one or more keys to the specified value. + */ + public boolean containsValue(Object value) { + return getDelegate().containsValue(value); + } + + /** + * Returns a set view of the mappings contained in this map. + * + * @return A set view of the mappings contained in this map. + */ + public Set> entrySet() { + return getDelegate().entrySet(); + } + + /** + * Compares the specified object with this map for equality. + * + * @param o Object to be compared for equality with this map. + * @return True if the specified object is equal to this map. + */ + @Override + public boolean equals(Object o) { + return getDelegate().equals(o); + } + + /** + * Returns the value to which this map maps the specified key. + * + * @param key Key whose associated value is to be returned. + * @return The value to which this map maps the specified key, or null if the map contains no + * mapping for this key. + */ + public V get(Object key) { + return getDelegate().get(key); + } + + /** + * Returns the delegate list. + * + * @return The delegate list. + */ + protected Map getDelegate() { + return this.delegate; + } + + /** + * Returns the hash code value for this map. + * + * @return The hash code value for this map. + */ + @Override + public int hashCode() { + return getDelegate().hashCode(); + } + + /** + * Returns true if this map contains no key-value mappings. + * + * @return True if this map contains no key-value mappings. + */ + public boolean isEmpty() { + return getDelegate().isEmpty(); + } + + /** + * Returns a set view of the keys contained in this map. + * + * @return A set view of the keys contained in this map. + */ + public Set keySet() { + return getDelegate().keySet(); + } + + /** + * Associates the specified value with the specified key in this map (optional operation). + * + * @param key Key with which the specified value is to be associated. + * @param value Value to be associated with the specified key. + * @return The previous value associated with a specified key, or null if there was no mapping for + * key. A null return can also indicate that the map previously associated null with the + * specified key, if the implementation supports null values. + */ + public V put(K key, V value) { + return getDelegate().put(key, value); + } + + /** + * Copies all the mappings from the specified map to this map (optional operation). + * + * @param t Mappings to be stored in this map. + */ + public void putAll(Map t) { + getDelegate().putAll(t); + } + + /** + * Removes the mapping for this key from this map if it is present (optional operation). + * + * @param key Key whose mapping is to be removed from the map. + * @return The previous value associated with a specified key, or null if there was no mapping for + * key. + */ + public V remove(Object key) { + return getDelegate().remove(key); + } + + /** + * Returns the number of key-value mappings in this map. + * + * @return The number of key-value mappings in this map. + */ + public int size() { + return getDelegate().size(); + } + + /** + * Returns a collection view of the values contained in this map. + * + * @return A collection view of the values contained in this map. + */ + public Collection values() { + return getDelegate().values(); + } } diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java b/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java index ba704f6be8..9bfb2b3c0d 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java @@ -1,235 +1,238 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; -import org.restlet.data.*; -import org.restlet.representation.Representation; - import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.Date; import java.util.List; +import org.restlet.data.CharacterSet; +import org.restlet.data.Disposition; +import org.restlet.data.Encoding; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Reference; +import org.restlet.data.Tag; +import org.restlet.representation.Representation; /** - * Representation wrapper. Useful for application developer who need to enrich - * the representation with application related properties and behavior. - * - * @see The decorator (aka - * wrapper) pattern + * Representation wrapper. Useful for developers who need to enrich the representation + * with application-related properties and behavior. + * + * @see The decorator (aka wrapper) pattern * @author Jerome Louvel */ public class WrapperRepresentation extends Representation { - /** The wrapped representation. */ - private final Representation wrappedRepresentation; - - /** - * Constructor. - * - * @param wrappedRepresentation The wrapped representation. - */ - public WrapperRepresentation(Representation wrappedRepresentation) { - this.wrappedRepresentation = wrappedRepresentation; - } - - @Override - public long exhaust() throws IOException { - return getWrappedRepresentation().exhaust(); - } - - @Override - public long getAvailableSize() { - return getWrappedRepresentation().getAvailableSize(); - } - - @Override - public CharacterSet getCharacterSet() { - return getWrappedRepresentation().getCharacterSet(); - } - - @Override - public org.restlet.data.Digest getDigest() { - return getWrappedRepresentation().getDigest(); - } - - @Override - public Disposition getDisposition() { - return getWrappedRepresentation().getDisposition(); - } - - @Override - public List getEncodings() { - return getWrappedRepresentation().getEncodings(); - } - - @Override - public Date getExpirationDate() { - return getWrappedRepresentation().getExpirationDate(); - } - - @Override - public List getLanguages() { - return getWrappedRepresentation().getLanguages(); - } - - @Override - public Reference getLocationRef() { - return getWrappedRepresentation().getLocationRef(); - } - - @Override - public MediaType getMediaType() { - return getWrappedRepresentation().getMediaType(); - } - - @Override - public Date getModificationDate() { - return getWrappedRepresentation().getModificationDate(); - } - - @Override - public org.restlet.data.Range getRange() { - return getWrappedRepresentation().getRange(); - } - - @Override - public Reader getReader() throws IOException { - return getWrappedRepresentation().getReader(); - } - - @Override - public long getSize() { - return getWrappedRepresentation().getSize(); - } - - @Override - public InputStream getStream() throws IOException { - return getWrappedRepresentation().getStream(); - } - - @Override - public Tag getTag() { - return getWrappedRepresentation().getTag(); - } - - @Override - public String getText() throws IOException { - return getWrappedRepresentation().getText(); - } - - /** - * Returns the wrapped representation. - * - * @return The wrapped representation. - */ - public Representation getWrappedRepresentation() { - return this.wrappedRepresentation; - } - - @Override - public boolean isAvailable() { - return getWrappedRepresentation().isAvailable(); - } - - @Override - public boolean isTransient() { - return getWrappedRepresentation().isTransient(); - } - - @Override - public void release() { - getWrappedRepresentation().release(); - } - - @Override - public void setAvailable(boolean isAvailable) { - getWrappedRepresentation().setAvailable(isAvailable); - } - - @Override - public void setCharacterSet(CharacterSet characterSet) { - getWrappedRepresentation().setCharacterSet(characterSet); - } - - @Override - public void setDigest(org.restlet.data.Digest digest) { - getWrappedRepresentation().setDigest(digest); - } - - @Override - public void setDisposition(Disposition disposition) { - getWrappedRepresentation().setDisposition(disposition); - } - - @Override - public void setEncodings(List encodings) { - getWrappedRepresentation().setEncodings(encodings); - } - - @Override - public void setExpirationDate(Date expirationDate) { - getWrappedRepresentation().setExpirationDate(expirationDate); - } - - @Override - public void setLanguages(List languages) { - getWrappedRepresentation().setLanguages(languages); - } - - @Override - public void setLocationRef(Reference location) { - getWrappedRepresentation().setLocationRef(location); - } - - @Override - public void setLocationRef(String locationUri) { - getWrappedRepresentation().setLocationRef(locationUri); - } - - @Override - public void setMediaType(MediaType mediaType) { - getWrappedRepresentation().setMediaType(mediaType); - } - - @Override - public void setModificationDate(Date modificationDate) { - getWrappedRepresentation().setModificationDate(modificationDate); - } - - @Override - public void setRange(org.restlet.data.Range range) { - getWrappedRepresentation().setRange(range); - } - - @Override - public void setSize(long expectedSize) { - getWrappedRepresentation().setSize(expectedSize); - } - - @Override - public void setTag(Tag tag) { - getWrappedRepresentation().setTag(tag); - } - - @Override - public void setTransient(boolean isTransient) { - getWrappedRepresentation().setTransient(isTransient); - } - - @Override - public void write(java.io.OutputStream outputStream) throws IOException { - getWrappedRepresentation().write(outputStream); - } - - @Override - public void write(java.io.Writer writer) throws IOException { - getWrappedRepresentation().write(writer); - } + /** The wrapped representation. */ + private final Representation wrappedRepresentation; + + /** + * Constructor. + * + * @param wrappedRepresentation The wrapped representation. + */ + public WrapperRepresentation(Representation wrappedRepresentation) { + this.wrappedRepresentation = wrappedRepresentation; + } + + @Override + public long exhaust() throws IOException { + return getWrappedRepresentation().exhaust(); + } + + @Override + public long getAvailableSize() { + return getWrappedRepresentation().getAvailableSize(); + } + + @Override + public CharacterSet getCharacterSet() { + return getWrappedRepresentation().getCharacterSet(); + } + + @Override + public org.restlet.data.Digest getDigest() { + return getWrappedRepresentation().getDigest(); + } + + @Override + public Disposition getDisposition() { + return getWrappedRepresentation().getDisposition(); + } + + @Override + public List getEncodings() { + return getWrappedRepresentation().getEncodings(); + } + + @Override + public Date getExpirationDate() { + return getWrappedRepresentation().getExpirationDate(); + } + + @Override + public List getLanguages() { + return getWrappedRepresentation().getLanguages(); + } + + @Override + public Reference getLocationRef() { + return getWrappedRepresentation().getLocationRef(); + } + + @Override + public MediaType getMediaType() { + return getWrappedRepresentation().getMediaType(); + } + + @Override + public Date getModificationDate() { + return getWrappedRepresentation().getModificationDate(); + } + + @Override + public org.restlet.data.Range getRange() { + return getWrappedRepresentation().getRange(); + } + + @Override + public Reader getReader() throws IOException { + return getWrappedRepresentation().getReader(); + } + + @Override + public long getSize() { + return getWrappedRepresentation().getSize(); + } + + @Override + public InputStream getStream() throws IOException { + return getWrappedRepresentation().getStream(); + } + + @Override + public Tag getTag() { + return getWrappedRepresentation().getTag(); + } + + @Override + public String getText() throws IOException { + return getWrappedRepresentation().getText(); + } + + /** + * Returns the wrapped representation. + * + * @return The wrapped representation. + */ + public Representation getWrappedRepresentation() { + return this.wrappedRepresentation; + } + + @Override + public boolean isAvailable() { + return getWrappedRepresentation().isAvailable(); + } + + @Override + public boolean isTransient() { + return getWrappedRepresentation().isTransient(); + } + + @Override + public void release() { + getWrappedRepresentation().release(); + } + + @Override + public void setAvailable(boolean isAvailable) { + getWrappedRepresentation().setAvailable(isAvailable); + } + + @Override + public void setCharacterSet(CharacterSet characterSet) { + getWrappedRepresentation().setCharacterSet(characterSet); + } + + @Override + public void setDigest(org.restlet.data.Digest digest) { + getWrappedRepresentation().setDigest(digest); + } + + @Override + public void setDisposition(Disposition disposition) { + getWrappedRepresentation().setDisposition(disposition); + } + + @Override + public void setEncodings(List encodings) { + getWrappedRepresentation().setEncodings(encodings); + } + + @Override + public void setExpirationDate(Date expirationDate) { + getWrappedRepresentation().setExpirationDate(expirationDate); + } + + @Override + public void setLanguages(List languages) { + getWrappedRepresentation().setLanguages(languages); + } + + @Override + public void setLocationRef(Reference location) { + getWrappedRepresentation().setLocationRef(location); + } + + @Override + public void setLocationRef(String locationUri) { + getWrappedRepresentation().setLocationRef(locationUri); + } + + @Override + public void setMediaType(MediaType mediaType) { + getWrappedRepresentation().setMediaType(mediaType); + } + + @Override + public void setModificationDate(Date modificationDate) { + getWrappedRepresentation().setModificationDate(modificationDate); + } + + @Override + public void setRange(org.restlet.data.Range range) { + getWrappedRepresentation().setRange(range); + } + + @Override + public void setSize(long expectedSize) { + getWrappedRepresentation().setSize(expectedSize); + } + + @Override + public void setTag(Tag tag) { + getWrappedRepresentation().setTag(tag); + } + + @Override + public void setTransient(boolean isTransient) { + getWrappedRepresentation().setTransient(isTransient); + } + + @Override + public void write(java.io.OutputStream outputStream) throws IOException { + getWrappedRepresentation().write(outputStream); + } + + @Override + public void write(java.io.Writer writer) throws IOException { + getWrappedRepresentation().write(writer); + } } diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java index 7a6b49a640..4561c0f3be 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java @@ -1,495 +1,492 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; import org.restlet.Request; import org.restlet.Response; import org.restlet.Uniform; -import org.restlet.data.*; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ClientInfo; +import org.restlet.data.Conditions; +import org.restlet.data.Cookie; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Protocol; +import org.restlet.data.Range; +import org.restlet.data.Reference; import org.restlet.representation.Representation; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; - /** - * Request wrapper. Useful for application developer who need to enrich the - * request with application related properties and behavior. - * - * @see The decorator (aka - * wrapper) pattern + * Request wrapper. Useful for application developer who need to enrich the request with application + * related properties and behavior. + * + * @see The decorator (aka wrapper) pattern * @author Jerome Louvel */ public class WrapperRequest extends Request { - /** The wrapped request. */ - private final Request wrappedRequest; - - /** - * Constructor. - * - * @param wrappedRequest The wrapped request. - */ - public WrapperRequest(Request wrappedRequest) { - this.wrappedRequest = wrappedRequest; - } - - @Override - public boolean abort() { - return wrappedRequest.abort(); - } - - @Override - public void commit(Response response) { - wrappedRequest.commit(response); - } - - /** - * Returns a modifiable attributes map that can be used by developers to save - * information relative to the message. This is an easier alternative to the - * creation of a wrapper instance around the whole message.
- *
- * - * In addition, this map is a shared space between the developer and the - * connectors. In this case, it is used to exchange information that is not - * uniform across all protocols and couldn't therefore be directly included in - * the API. For this purpose, all attribute names starting with "org.restlet" - * are reserved. Currently the following attributes are used: - * - * - * - * - * - * - * - * - * - * - * - * - *
list of supported attributes
Attribute nameClass nameDescription
org.restlet.http.headersorg.restlet.data.FormServer HTTP connectors must provide all request headers and client HTTP - * connectors must provide all response headers, exactly as they were received. - * In addition, developers can also use this attribute to specify - * non-standard headers that should be added to the request or to the - * response.
- * Adding standard HTTP headers is forbidden because it could conflict with the - * connector's internal behavior, limit portability or prevent future - * optimizations. - * - * @return The modifiable attributes map. - */ - @Override - public ConcurrentMap getAttributes() { - return getWrappedRequest().getAttributes(); - } - - /** - * Returns the authentication response sent by a client to an origin server. - * - * @return The authentication response sent by a client to an origin server. - */ - @Override - public ChallengeResponse getChallengeResponse() { - return getWrappedRequest().getChallengeResponse(); - } - - /** - * Returns the client-specific information. - * - * @return The client-specific information. - */ - @Override - public ClientInfo getClientInfo() { - return getWrappedRequest().getClientInfo(); - } - - /** - * Returns the conditions applying to this call. - * - * @return The conditions applying to this call. - */ - @Override - public Conditions getConditions() { - return getWrappedRequest().getConditions(); - } - - /** - * Returns the cookies provided by the client. - * - * @return The cookies provided by the client. - */ - @Override - public Series getCookies() { - return getWrappedRequest().getCookies(); - } - - /** - * Returns the entity representation. - * - * @return The entity representation. - */ - @Override - public Representation getEntity() { - return getWrappedRequest().getEntity(); - } - - /** - * Returns the host reference. This may be different from the resourceRef's - * host, for example for URNs and other URIs that don't contain host - * information. - * - * @return The host reference. - */ - @Override - public Reference getHostRef() { - return getWrappedRequest().getHostRef(); - } - - @Override - public int getMaxForwards() { - return wrappedRequest.getMaxForwards(); - } - - /** - * Returns the method. - * - * @return The method. - */ - @Override - public Method getMethod() { - return getWrappedRequest().getMethod(); - } - - @Override - public Uniform getOnResponse() { - return wrappedRequest.getOnResponse(); - } - - @Override - public Reference getOriginalRef() { - return wrappedRequest.getOriginalRef(); - } - - /** - * Returns the protocol by first returning the baseRef.schemeProtocol property - * if it is set, or the resourceRef.schemeProtocol property otherwise. - * - * @return The protocol or null if not available. - */ - @Override - public Protocol getProtocol() { - return getWrappedRequest().getProtocol(); - } - - /** - * Returns the authentication response sent by a client to a proxy. - * - * @return The authentication response sent by a client to a proxy. - */ - @Override - public ChallengeResponse getProxyChallengeResponse() { - return getWrappedRequest().getProxyChallengeResponse(); - } - - @Override - public List getRanges() { - return wrappedRequest.getRanges(); - } - - /** - * Returns the referrer reference if available. - * - * @return The referrer reference. - */ - @Override - public Reference getReferrerRef() { - return getWrappedRequest().getReferrerRef(); - } - - /** - * Returns the reference of the target resource. - * - * @return The reference of the target resource. - */ - @Override - public Reference getResourceRef() { - return getWrappedRequest().getResourceRef(); - } - - /** - * Returns the application root reference. - * - * @return The application root reference. - */ - @Override - public Reference getRootRef() { - return getWrappedRequest().getRootRef(); - } - - /** - * Returns the wrapped request. - * - * @return The wrapped request. - */ - protected Request getWrappedRequest() { - return this.wrappedRequest; - } - - /** - * Returns the access control request headers of the target resource. - * - * @return The access control request headers of the target resource. - */ - @Override - public Set getAccessControlRequestHeaders() { - return wrappedRequest.getAccessControlRequestHeaders(); - } - - /** - * Returns the access control request method of the target resource. - * - * @return The access control request method of the target resource. - */ - @Override - public Method getAccessControlRequestMethod() { - return wrappedRequest.getAccessControlRequestMethod(); - } - - @Override - public boolean isAsynchronous() { - return wrappedRequest.isAsynchronous(); - } - - /** - * Indicates if the call came over a confidential channel such as an SSL-secured - * connection. - * - * @return True if the call came over a confidential channel. - */ - @Override - public boolean isConfidential() { - return getWrappedRequest().isConfidential(); - } - - /** - * Indicates if a content is available and can be sent. Several conditions must - * be met: the method must allow the sending of content, the content must exists - * and have some available data. - * - * @return True if a content is available and can be sent. - */ - @Override - public boolean isEntityAvailable() { - return getWrappedRequest().isEntityAvailable(); - } - - @Override - public boolean isExpectingResponse() { - return wrappedRequest.isExpectingResponse(); - } - - @Override - public boolean isSynchronous() { - return wrappedRequest.isSynchronous(); - } - - /** - * Sets the authentication response sent by a client to an origin server. - * - * @param response The authentication response sent by a client to an origin - * server. - */ - @Override - public void setChallengeResponse(ChallengeResponse response) { - getWrappedRequest().setChallengeResponse(response); - } - - @Override - public void setClientInfo(ClientInfo clientInfo) { - wrappedRequest.setClientInfo(clientInfo); - } - - @Override - public void setConditions(Conditions conditions) { - wrappedRequest.setConditions(conditions); - } - - @Override - public void setCookies(Series cookies) { - wrappedRequest.setCookies(cookies); - } - - /** - * Sets the entity representation. - * - * @param entity The entity representation. - */ - @Override - public void setEntity(Representation entity) { - getWrappedRequest().setEntity(entity); - } - - /** - * Sets a textual entity. - * - * @param value The represented string. - * @param mediaType The representation's media type. - */ - @Override - public void setEntity(String value, MediaType mediaType) { - getWrappedRequest().setEntity(value, mediaType); - } - - /** - * Sets the host reference. - * - * @param hostRef The host reference. - */ - @Override - public void setHostRef(Reference hostRef) { - getWrappedRequest().setHostRef(hostRef); - } - - /** - * Sets the host reference using an URI string. - * - * @param hostUri The host URI. - */ - @Override - public void setHostRef(String hostUri) { - getWrappedRequest().setHostRef(hostUri); - } - - @Override - public void setMaxForwards(int maxForwards) { - wrappedRequest.setMaxForwards(maxForwards); - } - - /** - * Sets the method called. - * - * @param method The method called. - */ - @Override - public void setMethod(Method method) { - getWrappedRequest().setMethod(method); - } - - @Override - public void setOnResponse(Uniform onResponseCallback) { - wrappedRequest.setOnResponse(onResponseCallback); - } - - @Override - public void setOriginalRef(Reference originalRef) { - wrappedRequest.setOriginalRef(originalRef); - } - - @Override - public void setProtocol(Protocol protocol) { - wrappedRequest.setProtocol(protocol); - } - - /** - * Sets the authentication response sent by a client to a proxy. - * - * @param response The authentication response sent by a client to a proxy. - */ - @Override - public void setProxyChallengeResponse(ChallengeResponse response) { - getWrappedRequest().setProxyChallengeResponse(response); - } - - @Override - public void setRanges(List ranges) { - wrappedRequest.setRanges(ranges); - } - - /** - * Sets the referrer reference if available. - * - * @param referrerRef The referrer reference. - */ - @Override - public void setReferrerRef(Reference referrerRef) { - getWrappedRequest().setReferrerRef(referrerRef); - } - - /** - * Sets the referrer reference if available using an URI string. - * - * @param referrerUri The referrer URI. - */ - @Override - public void setReferrerRef(String referrerUri) { - getWrappedRequest().setReferrerRef(referrerUri); - } - - /** - * Sets the target resource reference. If the reference is relative, it will be - * resolved as an absolute reference. Also, the context's base reference will be - * reset. Finally, the reference will be normalized to ensure a consistent - * handling of the call. - * - * @param resourceRef The resource reference. - */ - @Override - public void setResourceRef(Reference resourceRef) { - getWrappedRequest().setResourceRef(resourceRef); - } - - /** - * Sets the target resource reference using an URI string. Note that the URI can - * be either absolute or relative to the context's base reference. - * - * @param resourceUri The resource URI. - */ - @Override - public void setResourceRef(String resourceUri) { - getWrappedRequest().setResourceRef(resourceUri); - } - - /** - * Sets the application root reference. - * - * @param rootRef The application root reference. - */ - @Override - public void setRootRef(Reference rootRef) { - getWrappedRequest().setRootRef(rootRef); - } - - /** - * Sets the access control request headers of the target resource. - * - * @param accessControlRequestHeaders The access control request headers of the - * target resource. - */ - @Override - public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { - super.setAccessControlRequestHeaders(accessControlRequestHeaders); - } - - /** - * Sets the access control request method of the target resource. - * - * @param accessControlRequestMethod The access control request method of the - * target resource. - */ - @Override - public void setAccessControlRequestMethod(Method accessControlRequestMethod) { - super.setAccessControlRequestMethod(accessControlRequestMethod); - } - - @Override - public String toString() { - return wrappedRequest.toString(); - } - + /** The wrapped request. */ + private final Request wrappedRequest; + + /** + * Constructor. + * + * @param wrappedRequest The wrapped request. + */ + public WrapperRequest(Request wrappedRequest) { + this.wrappedRequest = wrappedRequest; + } + + @Override + public boolean abort() { + return wrappedRequest.abort(); + } + + @Override + public void commit(Response response) { + wrappedRequest.commit(response); + } + + /** + * Returns a modifiable attributes map that developers can use to save information + * relative to the message. This is an easier alternative to the creation of a wrapper instance + * around the whole message.
+ *
+ * In addition, this map is a shared space between the developer and the connectors. In this + * case, it is used to exchange information that is not uniform across all protocols and + * couldn't therefore be directly included in the API. For this purpose, all attribute names + * starting with "org.restlet" are reserved. Currently, the following attributes are used: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
list of supported attributes
Attribute nameClass nameDescription
org.restlet.http.headersorg.restlet.data.FormServer HTTP connectors must provide all request headers, and client HTTP + * connectors must provide all response headers, exactly as they were received. + * In addition, developers can also use this attribute to specify + * non-standard headers that should be added to the request or to the + * response.
+ * + * Adding standard HTTP headers is forbidden because it could conflict with the connector's + * internal behavior, limit portability, or prevent future optimizations. + * + * @return The modifiable attributes map. + */ + @Override + public ConcurrentMap getAttributes() { + return getWrappedRequest().getAttributes(); + } + + /** + * Returns the authentication response sent by a client to an origin server. + * + * @return The authentication response sent by a client to an origin server. + */ + @Override + public ChallengeResponse getChallengeResponse() { + return getWrappedRequest().getChallengeResponse(); + } + + /** + * Returns the client-specific information. + * + * @return The client-specific information. + */ + @Override + public ClientInfo getClientInfo() { + return getWrappedRequest().getClientInfo(); + } + + /** + * Returns the conditions applying to this call. + * + * @return The conditions applying to this call. + */ + @Override + public Conditions getConditions() { + return getWrappedRequest().getConditions(); + } + + /** + * Returns the cookies provided by the client. + * + * @return The cookies provided by the client. + */ + @Override + public Series getCookies() { + return getWrappedRequest().getCookies(); + } + + /** + * Returns the entity representation. + * + * @return The entity representation. + */ + @Override + public Representation getEntity() { + return getWrappedRequest().getEntity(); + } + + /** + * Returns the host reference. This may be different from the resourceRef's host, for example, + * for URNs and other URIs that don't contain host information. + * + * @return The host reference. + */ + @Override + public Reference getHostRef() { + return getWrappedRequest().getHostRef(); + } + + @Override + public int getMaxForwards() { + return wrappedRequest.getMaxForwards(); + } + + /** + * Returns the method. + * + * @return The method. + */ + @Override + public Method getMethod() { + return getWrappedRequest().getMethod(); + } + + @Override + public Uniform getOnResponse() { + return wrappedRequest.getOnResponse(); + } + + @Override + public Reference getOriginalRef() { + return wrappedRequest.getOriginalRef(); + } + + /** + * Returns the protocol by first returning the baseRef.schemeProtocol property if it is set, or + * the resourceRef.schemeProtocol property otherwise. + * + * @return The protocol or null if not available. + */ + @Override + public Protocol getProtocol() { + return getWrappedRequest().getProtocol(); + } + + /** + * Returns the authentication response sent by a client to a proxy. + * + * @return The authentication response sent by a client to a proxy. + */ + @Override + public ChallengeResponse getProxyChallengeResponse() { + return getWrappedRequest().getProxyChallengeResponse(); + } + + @Override + public List getRanges() { + return wrappedRequest.getRanges(); + } + + /** + * Returns the referrer reference if available. + * + * @return The referrer reference. + */ + @Override + public Reference getReferrerRef() { + return getWrappedRequest().getReferrerRef(); + } + + /** + * Returns the reference of the target resource. + * + * @return The reference of the target resource. + */ + @Override + public Reference getResourceRef() { + return getWrappedRequest().getResourceRef(); + } + + /** + * Returns the application root reference. + * + * @return The application root reference. + */ + @Override + public Reference getRootRef() { + return getWrappedRequest().getRootRef(); + } + + /** + * Returns the wrapped request. + * + * @return The wrapped request. + */ + protected Request getWrappedRequest() { + return this.wrappedRequest; + } + + /** + * Returns the access control request headers of the target resource. + * + * @return The access control request headers of the target resource. + */ + @Override + public Set getAccessControlRequestHeaders() { + return wrappedRequest.getAccessControlRequestHeaders(); + } + + /** + * Returns the access control request method of the target resource. + * + * @return The access control request method of the target resource. + */ + @Override + public Method getAccessControlRequestMethod() { + return wrappedRequest.getAccessControlRequestMethod(); + } + + @Override + public boolean isAsynchronous() { + return wrappedRequest.isAsynchronous(); + } + + /** + * Indicates if the call came over a confidential channel such as an SSL-secured connection. + * + * @return True if the call came over a confidential channel. + */ + @Override + public boolean isConfidential() { + return getWrappedRequest().isConfidential(); + } + + /** + * Indicates if a content is available and can be sent. Several conditions must be met: the + * method must allow the sending of content, the content must exist and have some available + * data. + * + * @return True if a content is available and can be sent. + */ + @Override + public boolean isEntityAvailable() { + return getWrappedRequest().isEntityAvailable(); + } + + @Override + public boolean isExpectingResponse() { + return wrappedRequest.isExpectingResponse(); + } + + @Override + public boolean isSynchronous() { + return wrappedRequest.isSynchronous(); + } + + /** + * Sets the authentication response sent by a client to an origin server. + * + * @param response The authentication response sent by a client to an origin server. + */ + @Override + public void setChallengeResponse(ChallengeResponse response) { + getWrappedRequest().setChallengeResponse(response); + } + + @Override + public void setClientInfo(ClientInfo clientInfo) { + wrappedRequest.setClientInfo(clientInfo); + } + + @Override + public void setConditions(Conditions conditions) { + wrappedRequest.setConditions(conditions); + } + + @Override + public void setCookies(Series cookies) { + wrappedRequest.setCookies(cookies); + } + + /** + * Sets the entity representation. + * + * @param entity The entity representation. + */ + @Override + public void setEntity(Representation entity) { + getWrappedRequest().setEntity(entity); + } + + /** + * Sets a textual entity. + * + * @param value The represented string. + * @param mediaType The representation's media-type. + */ + @Override + public void setEntity(String value, MediaType mediaType) { + getWrappedRequest().setEntity(value, mediaType); + } + + /** + * Sets the host reference. + * + * @param hostRef The host reference. + */ + @Override + public void setHostRef(Reference hostRef) { + getWrappedRequest().setHostRef(hostRef); + } + + /** + * Sets the host reference using a URI string. + * + * @param hostUri The host URI. + */ + @Override + public void setHostRef(String hostUri) { + getWrappedRequest().setHostRef(hostUri); + } + + @Override + public void setMaxForwards(int maxForwards) { + wrappedRequest.setMaxForwards(maxForwards); + } + + /** + * Sets the method called. + * + * @param method The method called. + */ + @Override + public void setMethod(Method method) { + getWrappedRequest().setMethod(method); + } + + @Override + public void setOnResponse(Uniform onResponseCallback) { + wrappedRequest.setOnResponse(onResponseCallback); + } + + @Override + public void setOriginalRef(Reference originalRef) { + wrappedRequest.setOriginalRef(originalRef); + } + + @Override + public void setProtocol(Protocol protocol) { + wrappedRequest.setProtocol(protocol); + } + + /** + * Sets the authentication response sent by a client to a proxy. + * + * @param response The authentication response sent by a client to a proxy. + */ + @Override + public void setProxyChallengeResponse(ChallengeResponse response) { + getWrappedRequest().setProxyChallengeResponse(response); + } + + @Override + public void setRanges(List ranges) { + wrappedRequest.setRanges(ranges); + } + + /** + * Sets the referrer reference if available. + * + * @param referrerRef The referrer reference. + */ + @Override + public void setReferrerRef(Reference referrerRef) { + getWrappedRequest().setReferrerRef(referrerRef); + } + + /** + * Sets the referrer reference if available using a URI string. + * + * @param referrerUri The referrer URI. + */ + @Override + public void setReferrerRef(String referrerUri) { + getWrappedRequest().setReferrerRef(referrerUri); + } + + /** + * Sets the target resource reference. If the reference is relative, it will be resolved as an + * absolute reference. Also, the context's base reference will be reset. Finally, the reference + * will be normalized to ensure a consistent handling of the call. + * + * @param resourceRef The resource reference. + */ + @Override + public void setResourceRef(Reference resourceRef) { + getWrappedRequest().setResourceRef(resourceRef); + } + + /** + * Sets the target resource reference using a URI string. Note that the URI can be either + * absolute or relative to the context's base reference. + * + * @param resourceUri The resource URI. + */ + @Override + public void setResourceRef(String resourceUri) { + getWrappedRequest().setResourceRef(resourceUri); + } + + /** + * Sets the application root reference. + * + * @param rootRef The application root reference. + */ + @Override + public void setRootRef(Reference rootRef) { + getWrappedRequest().setRootRef(rootRef); + } + + /** + * Sets the access control request headers of the target resource. + * + * @param accessControlRequestHeaders The access control request headers of the target resource. + */ + @Override + public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { + super.setAccessControlRequestHeaders(accessControlRequestHeaders); + } + + /** + * Sets the access control request method of the target resource. + * + * @param accessControlRequestMethod The access control request method of the target resource. + */ + @Override + public void setAccessControlRequestMethod(Method accessControlRequestMethod) { + super.setAccessControlRequestMethod(accessControlRequestMethod); + } + + @Override + public String toString() { + return wrappedRequest.toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java b/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java index e4486124bf..ff75756b5a 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java @@ -1,493 +1,484 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.*; -import org.restlet.representation.Representation; - import java.util.Date; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentMap; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.AuthenticationInfo; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.CookieSetting; +import org.restlet.data.Dimension; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Reference; +import org.restlet.data.ServerInfo; +import org.restlet.data.Status; +import org.restlet.representation.Representation; /** - * Request wrapper. Useful for application developer who need to enrich the - * request with application related properties and behavior. - * - * @see The decorator (aka - * wrapper) pattern + * Request wrapper. Useful for developers who need to enrich the request with application-related properties and behavior. + * + * @see The decorator (aka wrapper) pattern * @author Jerome Louvel */ public class WrapperResponse extends Response { - /** The wrapped response. */ - private final Response wrappedResponse; - - /** - * Constructor. - * - * @param wrappedResponse The wrapped response. - */ - public WrapperResponse(Response wrappedResponse) { - super(null); - this.wrappedResponse = wrappedResponse; - } - - @Override - public void abort() { - wrappedResponse.abort(); - } - - @Override - public void commit() { - wrappedResponse.commit(); - } - - @Override - public int getAge() { - return wrappedResponse.getAge(); - } - - /** - * Returns the set of methods allowed on the requested resource. This property - * only has to be updated when a status CLIENT_ERROR_METHOD_NOT_ALLOWED is set. - * - * @return The list of allowed methods. - */ - @Override - public Set getAllowedMethods() { - return getWrappedResponse().getAllowedMethods(); - } - - /** - * Returns a modifiable attributes map that can be used by developers to save - * information relative to the message. This is an easier alternative to the - * creation of a wrapper instance around the whole message.
- *
- * - * In addition, this map is a shared space between the developer and the - * connectors. In this case, it is used to exchange information that is not - * uniform across all protocols and couldn't therefore be directly included in - * the API. For this purpose, all attribute names starting with "org.restlet" - * are reserved. Currently the following attributes are used: - * - * - * - * - * - * - * - * - * - * - * - * - *
list of supported attributes
Attribute nameClass nameDescription
org.restlet.http.headersorg.restlet.data.FormServer HTTP connectors must provide all request headers and client HTTP - * connectors must provide all response headers, exactly as they were received. - * In addition, developers can also use this attribute to specify - * non-standard headers that should be added to the request or to the - * response.
- * Adding standard HTTP headers is forbidden because it could conflict with the - * connector's internal behavior, limit portability or prevent future - * optimizations. - * - * @return The modifiable attributes map. - */ - @Override - public ConcurrentMap getAttributes() { - return getWrappedResponse().getAttributes(); - } - - @Override - public AuthenticationInfo getAuthenticationInfo() { - return wrappedResponse.getAuthenticationInfo(); - } - - /** - * Returns the list of authentication requests sent by an origin server to a - * client. - * - * @return The list of authentication requests sent by an origin server to a - * client. - */ - @Override - public List getChallengeRequests() { - return getWrappedResponse().getChallengeRequests(); - } - - /** - * Returns the cookie settings provided by the server. - * - * @return The cookie settings provided by the server. - */ - @Override - public Series getCookieSettings() { - return getWrappedResponse().getCookieSettings(); - } - - /** - * Returns the set of selecting dimensions on which the response entity may - * vary. If some server-side content negotiation is done, this set should be - * properly updated, other it can be left empty. - * - * @return The set of dimensions on which the response entity may vary. - */ - @Override - public Set getDimensions() { - return getWrappedResponse().getDimensions(); - } - - /** - * Returns the entity representation. - * - * @return The entity representation. - */ - @Override - public Representation getEntity() { - return getWrappedResponse().getEntity(); - } - - /** - * Returns the reference that the client should follow for redirections or - * resource creations. - * - * @return The redirection reference. - */ - @Override - public Reference getLocationRef() { - return getWrappedResponse().getLocationRef(); - } - - /** - * Returns the list of authentication requests sent by a proxy to a client. - * - * @return The list of authentication requests sent by a proxy to a client. - */ - @Override - public List getProxyChallengeRequests() { - return getWrappedResponse().getProxyChallengeRequests(); - } - - /** - * Returns the associated request - * - * @return The associated request - */ - @Override - public Request getRequest() { - return getWrappedResponse().getRequest(); - } - - @Override - public Date getRetryAfter() { - return wrappedResponse.getRetryAfter(); - } - - /** - * Returns the server-specific information. - * - * @return The server-specific information. - */ - @Override - public ServerInfo getServerInfo() { - return getWrappedResponse().getServerInfo(); - } - - /** - * Returns the status. - * - * @return The status. - */ - @Override - public Status getStatus() { - return getWrappedResponse().getStatus(); - } - - /** - * Returns the wrapped response. - * - * @return The wrapped response. - */ - protected Response getWrappedResponse() { - return this.wrappedResponse; - } - - @Override - public boolean isAutoCommitting() { - return wrappedResponse.isAutoCommitting(); - } - - @Override - public boolean isCommitted() { - return wrappedResponse.isCommitted(); - } - - /** - * Indicates if the call came over a confidential channel such as an SSL-secured - * connection. - * - * @return True if the call came over a confidential channel. - */ - @Override - public boolean isConfidential() { - return getWrappedResponse().isConfidential(); - } - - /** - * Indicates if a content is available and can be sent. Several conditions must - * be met: the content must exists and have some available data. - * - * @return True if a content is available and can be sent. - */ - @Override - public boolean isEntityAvailable() { - return getWrappedResponse().isEntityAvailable(); - } - - /** - * Permanently redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetRef The target URI reference. - */ - @Override - public void redirectPermanent(Reference targetRef) { - getWrappedResponse().redirectPermanent(targetRef); - } - - /** - * Permanently redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetUri The target URI. - */ - @Override - public void redirectPermanent(String targetUri) { - getWrappedResponse().redirectPermanent(targetUri); - } - - /** - * Redirects the client to a different URI that SHOULD be retrieved using a GET - * method on that resource. This method exists primarily to allow the output of - * a POST-activated script to redirect the user agent to a selected resource. - * The new URI is not a substitute reference for the originally requested - * resource. - * - * @param targetRef The target reference. - */ - @Override - public void redirectSeeOther(Reference targetRef) { - getWrappedResponse().redirectSeeOther(targetRef); - } - - /** - * Redirects the client to a different URI that SHOULD be retrieved using a GET - * method on that resource. This method exists primarily to allow the output of - * a POST-activated script to redirect the user agent to a selected resource. - * The new URI is not a substitute reference for the originally requested - * resource. - * - * @param targetUri The target URI. - */ - @Override - public void redirectSeeOther(String targetUri) { - getWrappedResponse().redirectSeeOther(targetUri); - } - - /** - * Temporarily redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetRef The target reference. - */ - @Override - public void redirectTemporary(Reference targetRef) { - getWrappedResponse().redirectTemporary(targetRef); - } - - /** - * Temporarily redirects the client to a target URI. The client is expected to - * reuse the same method for the new request. - * - * @param targetUri The target URI. - */ - @Override - public void redirectTemporary(String targetUri) { - getWrappedResponse().redirectTemporary(targetUri); - } - - @Override - public void setAge(int age) { - wrappedResponse.setAge(age); - } - - @Override - public void setAllowedMethods(Set allowedMethods) { - wrappedResponse.setAllowedMethods(allowedMethods); - } - - @Override - public void setAuthenticationInfo(AuthenticationInfo authenticationInfo) { - wrappedResponse.setAuthenticationInfo(authenticationInfo); - } - - @Override - public void setAutoCommitting(boolean autoCommitting) { - wrappedResponse.setAutoCommitting(autoCommitting); - } - - /** - * Sets the list of authentication requests sent by an origin server to a - * client. - * - * @param requests The list of authentication requests sent by an origin server - * to a client. - */ - @Override - public void setChallengeRequests(List requests) { - getWrappedResponse().setChallengeRequests(requests); - } - - @Override - public void setCommitted(boolean committed) { - wrappedResponse.setCommitted(committed); - } - - @Override - public void setCookieSettings(Series cookieSettings) { - wrappedResponse.setCookieSettings(cookieSettings); - } - - @Override - public void setDimensions(Set dimensions) { - wrappedResponse.setDimensions(dimensions); - } - - /** - * Sets the entity representation. - * - * @param entity The entity representation. - */ - @Override - public void setEntity(Representation entity) { - getWrappedResponse().setEntity(entity); - } - - /** - * Sets a textual entity. - * - * @param value The represented string. - * @param mediaType The representation's media type. - */ - @Override - public void setEntity(String value, MediaType mediaType) { - getWrappedResponse().setEntity(value, mediaType); - } - - /** - * Sets the reference that the client should follow for redirections or resource - * creations. - * - * @param locationRef The reference to set. - */ - @Override - public void setLocationRef(Reference locationRef) { - getWrappedResponse().setLocationRef(locationRef); - } - - /** - * Sets the reference that the client should follow for redirections or resource - * creations. - * - * @param locationUri The URI to set. - */ - @Override - public void setLocationRef(String locationUri) { - getWrappedResponse().setLocationRef(locationUri); - } - - /** - * Sets the list of authentication requests sent by a proxy to a client. - * - * @param requests The list of authentication requests sent by a proxy to a - * client. - */ - @Override - public void setProxyChallengeRequests(List requests) { - getWrappedResponse().setProxyChallengeRequests(requests); - } - - /** - * Sets the associated request. - * - * @param request The associated request - */ - @Override - public void setRequest(Request request) { - getWrappedResponse().setRequest(request); - } - - /** - * Sets the associated request. - * - * @param request The associated request - */ - public void setRequest(WrapperRequest request) { - getWrappedResponse().setRequest(request); - } - - @Override - public void setRetryAfter(Date retryAfter) { - wrappedResponse.setRetryAfter(retryAfter); - } - - @Override - public void setServerInfo(ServerInfo serverInfo) { - wrappedResponse.setServerInfo(serverInfo); - } - - /** - * Sets the status. - * - * @param status The status to set. - */ - @Override - public void setStatus(Status status) { - getWrappedResponse().setStatus(status); - } - - /** - * Sets the status. - * - * @param status The status to set. - * @param message The status message. - */ - @Override - public void setStatus(Status status, String message) { - getWrappedResponse().setStatus(status, message); - } - - @Override - public void setStatus(Status status, Throwable throwable) { - wrappedResponse.setStatus(status, throwable); - } - - @Override - public void setStatus(Status status, Throwable throwable, String message) { - wrappedResponse.setStatus(status, throwable, message); - } - - @Override - public String toString() { - return wrappedResponse.toString(); - } - + /** The wrapped response. */ + private final Response wrappedResponse; + + /** + * Constructor. + * + * @param wrappedResponse The wrapped response. + */ + public WrapperResponse(Response wrappedResponse) { + super(null); + this.wrappedResponse = wrappedResponse; + } + + @Override + public void abort() { + wrappedResponse.abort(); + } + + @Override + public void commit() { + wrappedResponse.commit(); + } + + @Override + public int getAge() { + return wrappedResponse.getAge(); + } + + /** + * Returns the set of methods allowed on the requested resource. This property only has to be + * updated when a status CLIENT_ERROR_METHOD_NOT_ALLOWED is set. + * + * @return The list of allowed methods. + */ + @Override + public Set getAllowedMethods() { + return getWrappedResponse().getAllowedMethods(); + } + + /** + * Returns a modifiable attributes map that developers can use to save information + * relative to the message. This is an easier alternative to the creation of a wrapper instance + * around the whole message.
+ *
+ * In addition, this map is a shared space between the developer and the connectors. In this + * case, it is used to exchange information that is not uniform across all protocols and + * couldn't therefore be directly included in the API. For this purpose, all attribute names + * starting with "org.restlet" are reserved. Currently, the following attributes are used: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
list of supported attributes
Attribute nameClass nameDescription
org.restlet.http.headersorg.restlet.data.FormServer HTTP connectors must provide all request headers, and client HTTP + * connectors must provide all response headers, exactly as they were received. + * In addition, developers can also use this attribute to specify + * non-standard headers that should be added to the request or to the + * response.
+ * + * Adding standard HTTP headers is forbidden because it could conflict with the connector's + * internal behavior, limit portability, or prevent future optimizations. + * + * @return The modifiable attributes map. + */ + @Override + public ConcurrentMap getAttributes() { + return getWrappedResponse().getAttributes(); + } + + @Override + public AuthenticationInfo getAuthenticationInfo() { + return wrappedResponse.getAuthenticationInfo(); + } + + /** + * Returns the list of authentication requests sent by an origin server to a client. + * + * @return The list of authentication requests sent by an origin server to a client. + */ + @Override + public List getChallengeRequests() { + return getWrappedResponse().getChallengeRequests(); + } + + /** + * Returns the cookie settings provided by the server. + * + * @return The cookie settings provided by the server. + */ + @Override + public Series getCookieSettings() { + return getWrappedResponse().getCookieSettings(); + } + + /** + * Returns the set of selecting dimensions on which the response entity may vary. If some + * server-side content negotiation is done, this set should be properly updated, other it can be + * left empty. + * + * @return The set of dimensions on which the response entity may vary. + */ + @Override + public Set getDimensions() { + return getWrappedResponse().getDimensions(); + } + + /** + * Returns the entity representation. + * + * @return The entity representation. + */ + @Override + public Representation getEntity() { + return getWrappedResponse().getEntity(); + } + + /** + * Returns the reference that the client should follow for redirections or resource creations. + * + * @return The redirection reference. + */ + @Override + public Reference getLocationRef() { + return getWrappedResponse().getLocationRef(); + } + + /** + * Returns the list of authentication requests sent by a proxy to a client. + * + * @return The list of authentication requests sent by a proxy to a client. + */ + @Override + public List getProxyChallengeRequests() { + return getWrappedResponse().getProxyChallengeRequests(); + } + + /** + * Returns the associated request + * + * @return The associated request + */ + @Override + public Request getRequest() { + return getWrappedResponse().getRequest(); + } + + @Override + public Date getRetryAfter() { + return wrappedResponse.getRetryAfter(); + } + + /** + * Returns the server-specific information. + * + * @return The server-specific information. + */ + @Override + public ServerInfo getServerInfo() { + return getWrappedResponse().getServerInfo(); + } + + /** + * Returns the status. + * + * @return The status. + */ + @Override + public Status getStatus() { + return getWrappedResponse().getStatus(); + } + + /** + * Returns the wrapped response. + * + * @return The wrapped response. + */ + protected Response getWrappedResponse() { + return this.wrappedResponse; + } + + @Override + public boolean isAutoCommitting() { + return wrappedResponse.isAutoCommitting(); + } + + @Override + public boolean isCommitted() { + return wrappedResponse.isCommitted(); + } + + /** + * Indicates if the call came over a confidential channel such as an SSL-secured connection. + * + * @return True if the call came over a confidential channel. + */ + @Override + public boolean isConfidential() { + return getWrappedResponse().isConfidential(); + } + + /** + * Indicates if a content is available and can be sent. Several conditions must be met: the + * content must exists and have some available data. + * + * @return True if a content is available and can be sent. + */ + @Override + public boolean isEntityAvailable() { + return getWrappedResponse().isEntityAvailable(); + } + + /** + * Permanently redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetRef The target URI reference. + */ + @Override + public void redirectPermanent(Reference targetRef) { + getWrappedResponse().redirectPermanent(targetRef); + } + + /** + * Permanently redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetUri The target URI. + */ + @Override + public void redirectPermanent(String targetUri) { + getWrappedResponse().redirectPermanent(targetUri); + } + + /** + * Redirects the client to a different URI that SHOULD be retrieved using a GET method on that + * resource. This method exists primarily to allow the output of a POST-activated script to + * redirect the user agent to a selected resource. The new URI is not a substitute reference for + * the originally requested resource. + * + * @param targetRef The target reference. + */ + @Override + public void redirectSeeOther(Reference targetRef) { + getWrappedResponse().redirectSeeOther(targetRef); + } + + /** + * Redirects the client to a different URI that SHOULD be retrieved using a GET method on that + * resource. This method exists primarily to allow the output of a POST-activated script to + * redirect the user agent to a selected resource. The new URI is not a substitute reference for + * the originally requested resource. + * + * @param targetUri The target URI. + */ + @Override + public void redirectSeeOther(String targetUri) { + getWrappedResponse().redirectSeeOther(targetUri); + } + + /** + * Temporarily redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetRef The target reference. + */ + @Override + public void redirectTemporary(Reference targetRef) { + getWrappedResponse().redirectTemporary(targetRef); + } + + /** + * Temporarily redirects the client to a target URI. The client is expected to reuse the same + * method for the new request. + * + * @param targetUri The target URI. + */ + @Override + public void redirectTemporary(String targetUri) { + getWrappedResponse().redirectTemporary(targetUri); + } + + @Override + public void setAge(int age) { + wrappedResponse.setAge(age); + } + + @Override + public void setAllowedMethods(Set allowedMethods) { + wrappedResponse.setAllowedMethods(allowedMethods); + } + + @Override + public void setAuthenticationInfo(AuthenticationInfo authenticationInfo) { + wrappedResponse.setAuthenticationInfo(authenticationInfo); + } + + @Override + public void setAutoCommitting(boolean autoCommitting) { + wrappedResponse.setAutoCommitting(autoCommitting); + } + + /** + * Sets the list of authentication requests sent by an origin server to a client. + * + * @param requests The list of authentication requests sent by an origin server to a client. + */ + @Override + public void setChallengeRequests(List requests) { + getWrappedResponse().setChallengeRequests(requests); + } + + @Override + public void setCommitted(boolean committed) { + wrappedResponse.setCommitted(committed); + } + + @Override + public void setCookieSettings(Series cookieSettings) { + wrappedResponse.setCookieSettings(cookieSettings); + } + + @Override + public void setDimensions(Set dimensions) { + wrappedResponse.setDimensions(dimensions); + } + + /** + * Sets the entity representation. + * + * @param entity The entity representation. + */ + @Override + public void setEntity(Representation entity) { + getWrappedResponse().setEntity(entity); + } + + /** + * Sets a textual entity. + * + * @param value The represented string. + * @param mediaType The representation's media-type. + */ + @Override + public void setEntity(String value, MediaType mediaType) { + getWrappedResponse().setEntity(value, mediaType); + } + + /** + * Sets the reference that the client should follow for redirections or resource creations. + * + * @param locationRef The reference to set. + */ + @Override + public void setLocationRef(Reference locationRef) { + getWrappedResponse().setLocationRef(locationRef); + } + + /** + * Sets the reference that the client should follow for redirections or resource creations. + * + * @param locationUri The URI to set. + */ + @Override + public void setLocationRef(String locationUri) { + getWrappedResponse().setLocationRef(locationUri); + } + + /** + * Sets the list of authentication requests sent by a proxy to a client. + * + * @param requests The list of authentication requests sent by a proxy to a client. + */ + @Override + public void setProxyChallengeRequests(List requests) { + getWrappedResponse().setProxyChallengeRequests(requests); + } + + /** + * Sets the associated request. + * + * @param request The associated request + */ + @Override + public void setRequest(Request request) { + getWrappedResponse().setRequest(request); + } + + /** + * Sets the associated request. + * + * @param request The associated request + */ + public void setRequest(WrapperRequest request) { + getWrappedResponse().setRequest(request); + } + + @Override + public void setRetryAfter(Date retryAfter) { + wrappedResponse.setRetryAfter(retryAfter); + } + + @Override + public void setServerInfo(ServerInfo serverInfo) { + wrappedResponse.setServerInfo(serverInfo); + } + + /** + * Sets the status. + * + * @param status The status to set. + */ + @Override + public void setStatus(Status status) { + getWrappedResponse().setStatus(status); + } + + /** + * Sets the status. + * + * @param status The status to set. + * @param message The status message. + */ + @Override + public void setStatus(Status status, String message) { + getWrappedResponse().setStatus(status, message); + } + + @Override + public void setStatus(Status status, Throwable throwable) { + wrappedResponse.setStatus(status, throwable); + } + + @Override + public void setStatus(Status status, Throwable throwable, String message) { + wrappedResponse.setStatus(status, throwable, message); + } + + @Override + public String toString() { + return wrappedResponse.toString(); + } } diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java b/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java index bf977c9541..d927aba30a 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java @@ -1,127 +1,122 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.util; +import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; -import java.util.logging.Logger; - /** - * Restlet wrapper. Useful for application developer who need to wrap a Restlet - * instance. - * + * Restlet wrapper. Useful for developers who need to wrap a Restlet instance. + * * @author Thierry Boileau - * @see The decorator (aka - * wrapper) pattern + * @see The decorator (aka wrapper) pattern */ public class WrapperRestlet extends Restlet { - /** The wrapped Restlet instance. */ - private Restlet wrappedRestlet; - - /** - * Constructor. - * - * @param wrappedRestlet The wrapped Restlet instance. - */ - public WrapperRestlet(Restlet wrappedRestlet) { - super(); - this.wrappedRestlet = wrappedRestlet; - } - - @Override - public org.restlet.Application getApplication() { - return wrappedRestlet.getApplication(); - } - - @Override - public String getAuthor() { - return wrappedRestlet.getAuthor(); - } - - @Override - public Context getContext() { - return wrappedRestlet.getContext(); - } - - @Override - public String getDescription() { - return wrappedRestlet.getDescription(); - } - - @Override - public Logger getLogger() { - return wrappedRestlet.getLogger(); - } - - @Override - public String getName() { - return wrappedRestlet.getName(); - } - - @Override - public String getOwner() { - return wrappedRestlet.getOwner(); - } - - @Override - public void handle(Request request, Response response) { - wrappedRestlet.handle(request, response); - } - - @Override - public boolean isStarted() { - return wrappedRestlet.isStarted(); - } - - @Override - public boolean isStopped() { - return wrappedRestlet.isStopped(); - } - - @Override - public void setAuthor(String author) { - wrappedRestlet.setAuthor(author); - } - - @Override - public void setContext(Context context) { - wrappedRestlet.setContext(context); - } - - @Override - public void setDescription(String description) { - wrappedRestlet.setDescription(description); - } - - @Override - public void setName(String name) { - wrappedRestlet.setName(name); - } - - @Override - public void setOwner(String owner) { - wrappedRestlet.setOwner(owner); - } - - @Override - public synchronized void start() throws Exception { - wrappedRestlet.start(); - } - - @Override - public synchronized void stop() throws Exception { - wrappedRestlet.stop(); - } - + /** The wrapped Restlet instance. */ + private Restlet wrappedRestlet; + + /** + * Constructor. + * + * @param wrappedRestlet The wrapped Restlet instance. + */ + public WrapperRestlet(Restlet wrappedRestlet) { + super(); + this.wrappedRestlet = wrappedRestlet; + } + + @Override + public org.restlet.Application getApplication() { + return wrappedRestlet.getApplication(); + } + + @Override + public String getAuthor() { + return wrappedRestlet.getAuthor(); + } + + @Override + public Context getContext() { + return wrappedRestlet.getContext(); + } + + @Override + public String getDescription() { + return wrappedRestlet.getDescription(); + } + + @Override + public Logger getLogger() { + return wrappedRestlet.getLogger(); + } + + @Override + public String getName() { + return wrappedRestlet.getName(); + } + + @Override + public String getOwner() { + return wrappedRestlet.getOwner(); + } + + @Override + public void handle(Request request, Response response) { + wrappedRestlet.handle(request, response); + } + + @Override + public boolean isStarted() { + return wrappedRestlet.isStarted(); + } + + @Override + public boolean isStopped() { + return wrappedRestlet.isStopped(); + } + + @Override + public void setAuthor(String author) { + wrappedRestlet.setAuthor(author); + } + + @Override + public void setContext(Context context) { + wrappedRestlet.setContext(context); + } + + @Override + public void setDescription(String description) { + wrappedRestlet.setDescription(description); + } + + @Override + public void setName(String name) { + wrappedRestlet.setName(name); + } + + @Override + public void setOwner(String owner) { + wrappedRestlet.setOwner(owner); + } + + @Override + public synchronized void start() throws Exception { + wrappedRestlet.start(); + } + + @Override + public synchronized void stop() throws Exception { + wrappedRestlet.stop(); + } } diff --git a/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java b/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java index 225edaea82..42c93019a6 100644 --- a/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java +++ b/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -21,10 +22,9 @@ import org.restlet.resource.ServerResource; import org.restlet.routing.Router; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** - * Tests that when issuing internal calls, the application context is kept intact in the caller server resource. + * Tests that when issuing internal calls, the application context is kept intact in the caller + * server resource. */ public class ApplicationContextTestCase { @@ -82,7 +82,7 @@ protected void setUpEach() throws Exception { @AfterEach protected void tearDownEach() throws Exception { Engine.clearThreadLocalVariables(); - this.component.stop(); + this.component.stop(); } @Test @@ -90,7 +90,8 @@ public void testApplicationContext() throws Exception { ClientResource res = new ClientResource("http://localhost:" + testPort + "/api/test"); Representation rep = res.get(MediaType.TEXT_PLAIN); // following https://github.com/restlet/restlet-framework-java/issues/1317 fix, - // should return "InternalApplication" since the current Application thread variable has not been cleared + // should return "InternalApplication" since the current Application thread variable has not + // been cleared assertEquals("InternalApplication", rep.getText()); } } diff --git a/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java b/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java index 9f68b77711..f43ae5be83 100644 --- a/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java +++ b/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java @@ -1,19 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashSet; import java.util.List; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/org.restlet/src/test/java/org/restlet/CallTestCase.java b/org.restlet/src/test/java/org/restlet/CallTestCase.java index a0bf453bc0..d7d11b0cac 100644 --- a/org.restlet/src/test/java/org/restlet/CallTestCase.java +++ b/org.restlet/src/test/java/org/restlet/CallTestCase.java @@ -1,14 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.data.ClientInfo; import org.restlet.data.Method; @@ -16,20 +19,15 @@ import org.restlet.data.Status; import org.restlet.engine.adapter.Call; -import java.util.Arrays; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test {@link org.restlet.engine.adapter.Call}. - * + * * @author Lars Heuer (heuer[at]semagia.com) */ public class CallTestCase { /** * Returns a connector call. - * + * * @return A connector call instance. */ protected Call getCall() { @@ -44,15 +42,13 @@ protected boolean isClientKeepAlive() { protected boolean isServerKeepAlive() { return false; } - }; } /** * Returns a reference with the specified URI. - * - * @param uri - * The URI. + * + * @param uri The URI. * @return Reference instance. */ protected Reference getReference(String uri) { @@ -61,7 +57,7 @@ protected Reference getReference(String uri) { /** * Returns a request. - * + * * @return Request instance. */ protected Request getRequest() { @@ -70,18 +66,15 @@ protected Request getRequest() { /** * Returns a response. - * - * @param request - * The associated request. + * + * @param request The associated request. * @return Response instance. */ protected Response getResponse(Request request) { return new Response(request); } - /** - * Tests context's base reference getting/setting. - */ + /** Tests context's base reference getting/setting. */ @Test public void testBaseRef() { final Request request = getRequest(); @@ -89,13 +82,13 @@ public void testBaseRef() { final Reference resourceRef = getReference(resourceRefURI); request.setResourceRef(resourceRefURI); assertEquals(resourceRef, request.getResourceRef()); - + String uri = "http://restlet.org/path"; Reference reference = getReference(uri); request.getResourceRef().setBaseRef(uri); assertEquals(uri, request.getResourceRef().getBaseRef().toString()); assertEquals(reference, request.getResourceRef().getBaseRef()); - + uri = "http://restlet.org/path/to"; reference = getReference(uri); request.getResourceRef().setBaseRef(uri); @@ -103,9 +96,7 @@ public void testBaseRef() { assertEquals(reference, request.getResourceRef().getBaseRef()); } - /** - * Tests client address getting/setting. - */ + /** Tests client address getting/setting. */ @Test public void testClientAddress() { final ClientInfo client = getRequest().getClientInfo(); @@ -115,24 +106,20 @@ public void testClientAddress() { assertEquals(0, client.getForwardedAddresses().size()); } - /** - * Tests client agent getting/setting. - */ + /** Tests client agent getting/setting. */ @Test public void testClientAgent() { final ClientInfo client = getRequest().getClientInfo(); String name = "Restlet"; client.setAgent(name); assertEquals(name, client.getAgent()); - + name = "Restlet Client"; client.setAgent(name); assertEquals(name, client.getAgent()); } - /** - * Tests client addresses getting/setting. - */ + /** Tests client addresses getting/setting. */ @Test public void testClientForwardedAddresses() { final ClientInfo client = getRequest().getClientInfo(); @@ -141,28 +128,24 @@ public void testClientForwardedAddresses() { List addresses = Arrays.asList(firstAddress, secondAddress); client.getForwardedAddresses().addAll(addresses); assertEquals(addresses, client.getForwardedAddresses()); - + client.getForwardedAddresses().clear(); client.getForwardedAddresses().addAll(addresses); assertEquals(addresses, client.getForwardedAddresses()); } - /** - * Tests method getting/setting. - */ + /** Tests method getting/setting. */ @Test public void testMethod() { final Request request = getRequest(); request.setMethod(Method.GET); assertEquals(Method.GET, request.getMethod()); - + request.setMethod(Method.POST); assertEquals(Method.POST, request.getMethod()); } - /** - * Tests redirection reference getting/setting. - */ + /** Tests redirection reference getting/setting. */ @Test public void testRedirectionRef() { final Request request = getRequest(); @@ -171,16 +154,14 @@ public void testRedirectionRef() { Reference reference = getReference(uri); response.setLocationRef(uri); assertEquals(reference, response.getLocationRef()); - + uri = "http://restlet.org/something"; reference = getReference(uri); response.setLocationRef(reference); assertEquals(reference, response.getLocationRef()); } - /** - * Tests referrer reference getting/setting. - */ + /** Tests referrer reference getting/setting. */ @Test public void testReferrerRef() { final Request request = getRequest(); @@ -188,16 +169,14 @@ public void testReferrerRef() { Reference reference = getReference(uri); request.setReferrerRef(uri); assertEquals(reference, request.getReferrerRef()); - + uri = "http://restlet.org/something"; reference = getReference(uri); request.setReferrerRef(reference); assertEquals(reference, request.getReferrerRef()); } - /** - * Tests resource reference getting/setting. - */ + /** Tests resource reference getting/setting. */ @Test public void testResourceRef() { final Request request = getRequest(); @@ -211,9 +190,7 @@ public void testResourceRef() { assertEquals(reference, request.getResourceRef()); } - /** - * Tests server address getting/setting. - */ + /** Tests server address getting/setting. */ @Test public void testServerAddress() { final Request request = getRequest(); @@ -221,15 +198,13 @@ public void testServerAddress() { String address = "127.0.0.1"; response.getServerInfo().setAddress(address); assertEquals(address, response.getServerInfo().getAddress()); - + address = "192.168.99.10"; response.getServerInfo().setAddress(address); assertEquals(address, response.getServerInfo().getAddress()); } - /** - * Tests server agent getting/setting. - */ + /** Tests server agent getting/setting. */ @Test public void testServerAgent() { final Request request = getRequest(); @@ -237,24 +212,21 @@ public void testServerAgent() { String name = "Restlet"; response.getServerInfo().setAgent(name); assertEquals(name, response.getServerInfo().getAgent()); - + name = "Restlet Server"; response.getServerInfo().setAgent(name); assertEquals(name, response.getServerInfo().getAgent()); } - /** - * Tests status getting/setting. - */ + /** Tests status getting/setting. */ @Test public void testStatus() { final Request request = getRequest(); final Response response = getResponse(request); response.setStatus(Status.SUCCESS_OK); assertEquals(Status.SUCCESS_OK, response.getStatus()); - + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST); assertEquals(Status.CLIENT_ERROR_BAD_REQUEST, response.getStatus()); } - } diff --git a/org.restlet/src/test/java/org/restlet/RestartTestCase.java b/org.restlet/src/test/java/org/restlet/RestartTestCase.java index a2e47b8964..34cdaba794 100644 --- a/org.restlet/src/test/java/org/restlet/RestartTestCase.java +++ b/org.restlet/src/test/java/org/restlet/RestartTestCase.java @@ -1,22 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Duration; import org.junit.jupiter.api.Test; import org.restlet.data.Protocol; -import java.time.Duration; - /** * Test the ability of a connector to be restarted. * @@ -44,5 +42,4 @@ void testRestart() throws Exception { connector.stop(); assertFalse(connector.isStarted()); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java index 1a2350716a..fa6d919e02 100644 --- a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java @@ -1,47 +1,43 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.Test; -import org.restlet.engine.security.AuthenticatorUtils; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import org.junit.jupiter.api.Test; +import org.restlet.engine.security.AuthenticatorUtils; + /** * Test {@link org.restlet.data.Reference}. - * + * * @author Kelly McLaughlin (mclaughlin77[at]gmail.com) */ public class AuthenticationInfoTestCase { - /** - * Test parsing an Authorization-Info header string. - */ + /** Test parsing an Authorization-Info header string. */ @Test public void testAuthenticationInfoHeaderParse() { - AuthenticationInfo authInfo = new AuthenticationInfo("00000002", 1, - "MDAzMTAw1", "auth", null); + AuthenticationInfo authInfo = + new AuthenticationInfo("00000002", 1, "MDAzMTAw1", "auth", null); String authInfoHeader = "nc=00000001, qop=auth, cnonce=\"MDAzMTAw1\", nextnonce=00000002"; - AuthenticationInfo parsedAuthInfo = AuthenticatorUtils.parseAuthenticationInfo(authInfoHeader); + AuthenticationInfo parsedAuthInfo = + AuthenticatorUtils.parseAuthenticationInfo(authInfoHeader); assertEquals(authInfo, parsedAuthInfo); assertEquals(parsedAuthInfo, authInfo); } - /** - * Test cnonce getting/setting. - */ + /** Test cnonce getting/setting. */ @Test public void testCnonce() { - AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", - 1111111, "testcnonce", "auth", "FFFFFF"); + AuthenticationInfo authInfo = + new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getClientNonce(), "testcnonce"); String newCnonce = "newcnonce"; @@ -49,27 +45,23 @@ public void testCnonce() { assertEquals(authInfo.getClientNonce(), "newcnonce"); } - /** - * Equality tests. - */ + /** Equality tests. */ @Test public void testEquals() { - final AuthenticationInfo authInfo1 = new AuthenticationInfo( - "testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); - final AuthenticationInfo authInfo2 = new AuthenticationInfo( - "testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); + final AuthenticationInfo authInfo1 = + new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); + final AuthenticationInfo authInfo2 = + new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo1, authInfo2); assertEquals(authInfo1, authInfo2); } - /** - * Test nextnonce getting/setting. - */ + /** Test nextnonce getting/setting. */ @Test public void testNextNonce() { - AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", - 1111111, "testcnonce", "auth", "FFFFFF"); + AuthenticationInfo authInfo = + new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getNextServerNonce(), "testnonce"); String newNonce = "newnonce"; @@ -77,13 +69,11 @@ public void testNextNonce() { assertEquals(authInfo.getNextServerNonce(), "newnonce"); } - /** - * Test nonce-count getting/setting. - */ + /** Test nonce-count getting/setting. */ @Test public void testNonceCount() { - AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", - 1111111, "testcnonce", "auth", "FFFFFF"); + AuthenticationInfo authInfo = + new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getNonceCount(), 1111111); int newNonceCount = 2222222; @@ -91,13 +81,11 @@ public void testNonceCount() { assertEquals(authInfo.getNonceCount(), 2222222); } - /** - * Test message-qop getting/setting. - */ + /** Test message-qop getting/setting. */ @Test public void testQop() { - AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", - 1111111, "testcnonce", "auth", "FFFFFF"); + AuthenticationInfo authInfo = + new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getQuality(), "auth"); String newQop = "auth-int"; @@ -105,13 +93,11 @@ public void testQop() { assertEquals(authInfo.getQuality(), "auth-int"); } - /** - * Test response-auth getting/setting. - */ + /** Test response-auth getting/setting. */ @Test public void testResponseAuth() { - AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", - 1111111, "testcnonce", "auth", "FFFFFF"); + AuthenticationInfo authInfo = + new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getResponseDigest(), "FFFFFF"); String newResponseAuth = "000000"; @@ -121,10 +107,10 @@ public void testResponseAuth() { @Test public void testUnEquals() { - final AuthenticationInfo authInfo1 = new AuthenticationInfo( - "testnonce1", 1111111, "testcnonce1", "auth", "FFFFFF"); - final AuthenticationInfo authInfo2 = new AuthenticationInfo( - "testnonce2", 1111111, "testcnonce2", "auth", "FFFFFF"); + final AuthenticationInfo authInfo1 = + new AuthenticationInfo("testnonce1", 1111111, "testcnonce1", "auth", "FFFFFF"); + final AuthenticationInfo authInfo2 = + new AuthenticationInfo("testnonce2", 1111111, "testcnonce2", "auth", "FFFFFF"); assertNotEquals(authInfo1, authInfo2); assertNotEquals(null, authInfo1); diff --git a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java index 8f81d3914f..ab4b78f734 100644 --- a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java @@ -1,14 +1,23 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.restlet.data.Language.ENGLISH; +import static org.restlet.data.Language.ENGLISH_US; +import static org.restlet.data.Language.FRENCH; +import static org.restlet.data.Language.FRENCH_FRANCE; +import static org.restlet.data.MediaType.APPLICATION_XML; +import static org.restlet.data.MediaType.TEXT_PLAIN; +import static org.restlet.data.MediaType.TEXT_XML; + +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -17,12 +26,6 @@ import org.restlet.service.ConnegService; import org.restlet.service.MetadataService; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.restlet.data.Language.*; -import static org.restlet.data.MediaType.*; - /** * Test {@link org.restlet.data.ClientInfo} for content negotiation. * @@ -47,9 +50,10 @@ void setup() { @Test public void shouldReturnEnUsAndTextXml() { - List variants = List.of( - new Variant(TEXT_XML, ENGLISH_US), - new Variant(TEXT_XML, FRENCH_FRANCE)); + List variants = + List.of( + new Variant(TEXT_XML, ENGLISH_US), + new Variant(TEXT_XML, FRENCH_FRANCE)); Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); @@ -58,9 +62,8 @@ public void shouldReturnEnUsAndTextXml() { @Test public void shouldReturnEnAndTextXml() { - List variants = List.of( - new Variant(TEXT_XML, ENGLISH), - new Variant(TEXT_XML, FRENCH)); + List variants = + List.of(new Variant(TEXT_XML, ENGLISH), new Variant(TEXT_XML, FRENCH)); Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); @@ -70,9 +73,8 @@ public void shouldReturnEnAndTextXml() { // Testing quality priority over parent metadata @Test public void shouldReturnFrFrAndText() { - List variants = List.of( - new Variant(TEXT_PLAIN, ENGLISH), - new Variant(TEXT_XML, FRENCH_FRANCE)); + List variants = + List.of(new Variant(TEXT_PLAIN, ENGLISH), new Variant(TEXT_XML, FRENCH_FRANCE)); Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); @@ -82,9 +84,10 @@ public void shouldReturnFrFrAndText() { // Testing quality priority over parent metadata @Test public void shouldReturnFrFrAndXml() { - List variants = List.of( - new Variant(APPLICATION_XML, ENGLISH_US), - new Variant(TEXT_XML, FRENCH_FRANCE)); + List variants = + List.of( + new Variant(APPLICATION_XML, ENGLISH_US), + new Variant(TEXT_XML, FRENCH_FRANCE)); Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); @@ -94,9 +97,10 @@ public void shouldReturnFrFrAndXml() { // Leveraging parent media types @Test public void shouldPreferEnUsAndApplicationXml() { - List variants = List.of( - new Variant(APPLICATION_XML, ENGLISH_US), - new Variant(APPLICATION_XML, FRENCH_FRANCE)); + List variants = + List.of( + new Variant(APPLICATION_XML, ENGLISH_US), + new Variant(APPLICATION_XML, FRENCH_FRANCE)); Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(APPLICATION_XML, pv.getMediaType()); @@ -104,9 +108,7 @@ public void shouldPreferEnUsAndApplicationXml() { } } - /** - * Conneg tests for IE which accepts all media types. - */ + /** Conneg tests for IE which accepts all media types. */ @Test public void testConnegIe() { ClientInfo ci = new ClientInfo(); @@ -118,5 +120,4 @@ public void testConnegIe() { assertEquals(TEXT_XML, pmt); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java b/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java index 27eeb8ed3e..f91e6c22b2 100644 --- a/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java @@ -1,29 +1,26 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import org.junit.jupiter.api.Test; + /** * Test {@link org.restlet.data.Cookie}. - * + * * @author Jerome Louvel */ public class CookieTestCase { - /** - * Equality tests. - */ + /** Equality tests. */ @Test public void testEquals() { Cookie c1 = new Cookie(1, "name1", "value1", "path1", "domain1"); @@ -34,9 +31,7 @@ public void testEquals() { assertEquals(c1, c2); } - /** - * Inequality tests. - */ + /** Inequality tests. */ @Test public void testUnEquals() { Cookie c1 = new Cookie(1, "name1", "value1", "path1", "domain1"); @@ -51,5 +46,4 @@ public void testUnEquals() { assertNotEquals(c1, c2); assertNotEquals(c1.hashCode(), c2.hashCode()); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java b/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java index 273d81cf9d..9cf02b67e3 100644 --- a/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java @@ -1,28 +1,26 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; import org.junit.jupiter.api.Test; import org.restlet.engine.Engine; import org.restlet.representation.StringRepresentation; import org.restlet.resource.ClientResource; -import java.io.File; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - /** * Unit test case for the File client connector. - * + * * @author Jerome Louvel */ public class FileClientTestCase { diff --git a/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java b/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java index 4cf3e9348f..88c8513b3c 100644 --- a/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java @@ -1,23 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; /** * Unit test case for the File Reference parsing. - * + * * @author Jerome Louvel */ public class FileReferenceTestCase { diff --git a/org.restlet/src/test/java/org/restlet/data/FormTestCase.java b/org.restlet/src/test/java/org/restlet/data/FormTestCase.java index 24dc0a88d0..84a356cb39 100644 --- a/org.restlet/src/test/java/org/restlet/data/FormTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FormTestCase.java @@ -1,25 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.Test; -import org.restlet.engine.util.FormReader; - -import java.io.IOException; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.restlet.engine.util.FormReader; + /** * Unit tests for the {@link Form} class. - * + * * @author Jerome Louvel */ public class FormTestCase { @@ -60,5 +58,4 @@ public void testEmptyParameter() { assertNull(form.getFirstValue("nullParam")); assertNull(form.getFirstValue("unknownParam")); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java b/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java index 1eff0bc45f..8fa815d50b 100644 --- a/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java @@ -1,19 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; + /** * Test {@link org.restlet.data.Language}. * @@ -21,9 +20,7 @@ */ public class LanguageTestCase { - /** - * Testing {@link Language#valueOf(String)} - */ + /** Testing {@link Language#valueOf(String)} */ @Test public void testValueOf() { assertSame(Language.FRENCH_FRANCE, Language.valueOf("fr-fr")); @@ -32,6 +29,8 @@ public void testValueOf() { @Test public void testUnmodifiable() { - assertThrows(UnsupportedOperationException.class, () -> Language.FRENCH_FRANCE.getSubTags().add("foo")); + assertThrows( + UnsupportedOperationException.class, + () -> Language.FRENCH_FRANCE.getSubTags().add("foo")); } } diff --git a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java index a9ef47bd52..db6caf35f7 100644 --- a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java @@ -1,41 +1,41 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.Test; import org.restlet.util.Series; -import static org.junit.jupiter.api.Assertions.*; - /** * Test {@link org.restlet.data.MediaType}. - * + * * @author Jerome Louvel */ public class MediaTypeTestCase { /** - * Makes sure that a {@link MediaType} instance initialized on the specified - * name has the expected values. - * - * @param name - * type to analyze. - * @param main - * expected main type. - * @param sub - * expected subtype. - * @param concrete - * expected 'concrete' flag. + * Makes sure that a {@link MediaType} instance initialized on the specified name has the + * expected values. + * + * @param name type to analyze. + * @param main expected main type. + * @param sub expected subtype. + * @param concrete expected 'concrete' flag. */ - public void assertMediaType(String name, String main, String sub, - boolean concrete) { + public void assertMediaType(String name, String main, String sub, boolean concrete) { MediaType type; type = new MediaType(name); @@ -44,36 +44,27 @@ public void assertMediaType(String name, String main, String sub, assertEquals(concrete, type.isConcrete()); } - /** - * Makes sure concrete types are properly initialized. - */ + /** Makes sure concrete types are properly initialized. */ @Test public void testConcrete() { assertMediaType("application/xml", "application", "xml", true); assertMediaType("application/ xml ", "application", "xml", true); assertMediaType(" application /xml", "application", "xml", true); assertMediaType(" application / xml ", "application", "xml", true); - assertMediaType("application/atom+xml;type=entry", "application", - "atom+xml", true); + assertMediaType("application/atom+xml;type=entry", "application", "atom+xml", true); } - /** - * Makes sure concrete types are properly initialized. - */ + /** Makes sure concrete types are properly initialized. */ @Test public void testParameters() { MediaType mt = MediaType.valueOf("application/atom+xml;type=entry"); assertEquals("entry", mt.getParameters().getFirstValue("type")); - mt = MediaType - .valueOf("multipart/x-mixed-replace; boundary=\"My boundary\""); - assertEquals("\"My boundary\"", - mt.getParameters().getFirstValue("boundary")); + mt = MediaType.valueOf("multipart/x-mixed-replace; boundary=\"My boundary\""); + assertEquals("\"My boundary\"", mt.getParameters().getFirstValue("boundary")); } - /** - * Equality tests. - */ + /** Equality tests. */ @Test public void testEquals() { MediaType mt1 = new MediaType("application/xml"); @@ -105,9 +96,7 @@ public void testEquals() { assertEquals(mt1, mt2); } - /** - * Test inclusion. - */ + /** Test inclusion. */ @Test public void testIncludes() { MediaType mt1 = MediaType.APPLICATION_ALL; @@ -150,33 +139,28 @@ public void testIncludes() { Series singleParam = new Series<>(Parameter.class); singleParam.add(new Parameter("name1", "value1")); - MediaType typeWithSingleParam = new MediaType("application/sometype", - singleParam); + MediaType typeWithSingleParam = new MediaType("application/sometype", singleParam); - Series singleMatchingParam = new Series<>( - Parameter.class); + Series singleMatchingParam = new Series<>(Parameter.class); singleMatchingParam.add(new Parameter("name1", "value1")); - MediaType typeWithSingleMatchingParam = new MediaType( - "application/sometype", singleMatchingParam); + MediaType typeWithSingleMatchingParam = + new MediaType("application/sometype", singleMatchingParam); - Series singleNonMatchingParamValue = new Series<>( - Parameter.class); + Series singleNonMatchingParamValue = new Series<>(Parameter.class); singleNonMatchingParamValue.add(new Parameter("name1", "value2")); - MediaType typeWithSingleNonMatchingParamValue = new MediaType( - "application/sometype", singleNonMatchingParamValue); + MediaType typeWithSingleNonMatchingParamValue = + new MediaType("application/sometype", singleNonMatchingParamValue); - Series singleNonMatchingParamName = new Series<>( - Parameter.class); + Series singleNonMatchingParamName = new Series<>(Parameter.class); singleNonMatchingParamName.add(new Parameter("name2", "value2")); - MediaType typeWithSingleNonMatchingParamName = new MediaType( - "application/sometype", singleNonMatchingParamName); + MediaType typeWithSingleNonMatchingParamName = + new MediaType("application/sometype", singleNonMatchingParamName); - Series twoParamsOneMatches = new Series<>( - Parameter.class); + Series twoParamsOneMatches = new Series<>(Parameter.class); twoParamsOneMatches.add(new Parameter("name1", "value1")); twoParamsOneMatches.add(new Parameter("name2", "value2")); - MediaType typeWithTwoParamsOneMatches = new MediaType( - "application/sometype", twoParamsOneMatches); + MediaType typeWithTwoParamsOneMatches = + new MediaType("application/sometype", twoParamsOneMatches); // SCENARIO 1: test whether type with no params includes type with one // param @@ -194,64 +178,58 @@ public void testIncludes() { // matching single param. // Note that this is distinct from testing whether a type includes // itself, as there is a special check for that. - assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, - true)); - assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, - false)); + assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, true)); + assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, false)); // SCENARIO 4: test whether type with single param includes type with // single param having different name - assertTrue(typeWithSingleParam.includes( - typeWithSingleNonMatchingParamName, true)); - assertFalse(typeWithSingleParam.includes( - typeWithSingleNonMatchingParamName, false)); + assertTrue(typeWithSingleParam.includes(typeWithSingleNonMatchingParamName, true)); + assertFalse(typeWithSingleParam.includes(typeWithSingleNonMatchingParamName, false)); // SCENARIO 5: test whether type with single param includes type with // single param having same name but different value - assertTrue(typeWithSingleParam.includes( - typeWithSingleNonMatchingParamValue, true)); - assertFalse(typeWithSingleParam.includes( - typeWithSingleNonMatchingParamValue, false)); + assertTrue(typeWithSingleParam.includes(typeWithSingleNonMatchingParamValue, true)); + assertFalse(typeWithSingleParam.includes(typeWithSingleNonMatchingParamValue, false)); // SCENARIO 6: test whether type with single param includes type with // two params, one matching - assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, - true)); - assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, - false)); + assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, true)); + assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, false)); // SCENARIO 7: test whether type with two params includes type with // single matching param - assertTrue(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, - true)); - assertFalse(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, - false)); + assertTrue(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, true)); + assertFalse(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, false)); } @Test public void testMostSpecificMediaType() { - assertEquals(MediaType.TEXT_ALL, - MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_ALL)); - assertEquals(MediaType.TEXT_ALL, - MediaType.getMostSpecific(MediaType.TEXT_ALL, MediaType.ALL)); - - assertEquals(MediaType.TEXT_PLAIN, MediaType.getMostSpecific( - MediaType.ALL, MediaType.TEXT_ALL, MediaType.TEXT_PLAIN)); - assertEquals(MediaType.TEXT_PLAIN, MediaType.getMostSpecific( - MediaType.ALL, MediaType.TEXT_PLAIN, MediaType.TEXT_ALL)); - assertEquals(MediaType.TEXT_PLAIN, MediaType.getMostSpecific( - MediaType.TEXT_ALL, MediaType.ALL, MediaType.TEXT_PLAIN)); - assertEquals(MediaType.TEXT_PLAIN, MediaType.getMostSpecific( - MediaType.TEXT_ALL, MediaType.TEXT_PLAIN, MediaType.ALL)); - assertEquals(MediaType.TEXT_PLAIN, MediaType.getMostSpecific( - MediaType.TEXT_PLAIN, MediaType.ALL, MediaType.TEXT_ALL)); - assertEquals(MediaType.TEXT_PLAIN, MediaType.getMostSpecific( - MediaType.TEXT_PLAIN, MediaType.TEXT_ALL, MediaType.ALL)); + assertEquals( + MediaType.TEXT_ALL, MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_ALL)); + assertEquals( + MediaType.TEXT_ALL, MediaType.getMostSpecific(MediaType.TEXT_ALL, MediaType.ALL)); + + assertEquals( + MediaType.TEXT_PLAIN, + MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_ALL, MediaType.TEXT_PLAIN)); + assertEquals( + MediaType.TEXT_PLAIN, + MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_PLAIN, MediaType.TEXT_ALL)); + assertEquals( + MediaType.TEXT_PLAIN, + MediaType.getMostSpecific(MediaType.TEXT_ALL, MediaType.ALL, MediaType.TEXT_PLAIN)); + assertEquals( + MediaType.TEXT_PLAIN, + MediaType.getMostSpecific(MediaType.TEXT_ALL, MediaType.TEXT_PLAIN, MediaType.ALL)); + assertEquals( + MediaType.TEXT_PLAIN, + MediaType.getMostSpecific(MediaType.TEXT_PLAIN, MediaType.ALL, MediaType.TEXT_ALL)); + assertEquals( + MediaType.TEXT_PLAIN, + MediaType.getMostSpecific(MediaType.TEXT_PLAIN, MediaType.TEXT_ALL, MediaType.ALL)); } - /** - * Makes sure that 'abstract' types are properly initialised. - */ + /** Makes sure that 'abstract' types are properly initialised. */ @Test public void testNotConcrete() { // */* @@ -291,9 +269,7 @@ public void testNotConcrete() { assertMediaType(" application /*", "application", "*", false); } - /** - * Test references that are unequal. - */ + /** Test references that are unequal. */ @Test public void testUnEquals() { MediaType mt1 = new MediaType("application/xml"); @@ -316,17 +292,12 @@ public void testUnEquals() { assertNotEquals(mt1, mt2); } - /** - * Testing {@link MediaType#valueOf(String)} and - * {@link MediaType#register(String, String)} - */ + /** Testing {@link MediaType#valueOf(String)} and {@link MediaType#register(String, String)} */ @Test public void testValueOf() { - assertSame(MediaType.APPLICATION_XML, - MediaType.valueOf("application/xml")); + assertSame(MediaType.APPLICATION_XML, MediaType.valueOf("application/xml")); assertSame(MediaType.ALL, MediaType.valueOf("*/*")); - final MediaType newType = MediaType - .valueOf("application/x-restlet-test"); + final MediaType newType = MediaType.valueOf("application/x-restlet-test"); assertEquals("application", newType.getMainType()); assertEquals("x-restlet-test", newType.getSubType()); assertEquals("application/x-restlet-test", newType.getName()); @@ -334,21 +305,18 @@ public void testValueOf() { // Should not have got registered by call to valueOf() alone assertNotSame(newType, MediaType.valueOf("application/x-restlet-test")); - final MediaType registeredType = MediaType.register( - "application/x-restlet-test", "Restlet testcase"); + final MediaType registeredType = + MediaType.register("application/x-restlet-test", "Restlet testcase"); assertNotSame(newType, registeredType); // didn't touch old value assertEquals("application/x-restlet-test", registeredType.getName()); assertEquals("Restlet testcase", registeredType.getDescription()); // Later valueOf calls always returns the registered type - assertSame(registeredType, - MediaType.valueOf("application/x-restlet-test")); - assertSame(registeredType, - MediaType.valueOf("application/x-restlet-test")); + assertSame(registeredType, MediaType.valueOf("application/x-restlet-test")); + assertSame(registeredType, MediaType.valueOf("application/x-restlet-test")); // Test toString() equivalence - MediaType mediaType = MediaType - .valueOf("application/atom+xml; name=value"); + MediaType mediaType = MediaType.valueOf("application/atom+xml; name=value"); assertEquals("application/atom+xml; name=value", mediaType.toString()); assertEquals(MediaType.APPLICATION_ATOM, mediaType.getParent()); } @@ -359,8 +327,10 @@ public void testUnmodifiable() { final Form form = new Form(); form.add("name1", "value1"); - final Series unmodifiableForm = (Series) Series.unmodifiableSeries(form); + final Series unmodifiableForm = + (Series) Series.unmodifiableSeries(form); - assertThrows(UnsupportedOperationException.class, () -> unmodifiableForm.add("name2", "value2")); + assertThrows( + UnsupportedOperationException.class, () -> unmodifiableForm.add("name2", "value2")); } } diff --git a/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java b/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java index 913822cef0..bb40a3b55c 100644 --- a/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; import org.junit.jupiter.api.Assertions; @@ -14,24 +13,25 @@ /** * Test {@link org.restlet.data.Method}. - *

- * Note: this test purposefully does *not* extend RestletTestCase. - * The regression previously present in Restlet - * (described in https://github.com/restlet/restlet-framework-java/issues/1130) - * depends on class initialization order and - * vanishes when the Restlet/Engine class is initialized before the class Method. - * + * + *

Note: this test purposefully does *not* extend RestletTestCase. The regression previously + * present in Restlet (described in https://github.com/restlet/restlet-framework-java/issues/1130) + * depends on class initialization order and vanishes when the Restlet/Engine class is initialized + * before the class Method. + * * @author Andreas Wundsam */ public class MethodTestCase { /** - * validate that Method caching works, i.e., the value returned by - * Method.valueOf("GET") is the cached constant Method.GET. + * validate that Method caching works, i.e., the value returned by Method.valueOf("GET") is the + * cached constant Method.GET. */ @Test public void testCaching() { - Assertions.assertEquals(Method.GET, Method.valueOf("GET"), "Method.valueOf('GET') should return cached constant Method.GET "); + Assertions.assertEquals( + Method.GET, + Method.valueOf("GET"), + "Method.valueOf('GET') should return cached constant Method.GET "); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java index f452f89908..d16b458d27 100644 --- a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java @@ -1,14 +1,19 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -16,13 +21,6 @@ import org.restlet.engine.header.ProductReader; import org.restlet.engine.header.ProductWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - /** * Test {@link org.restlet.data.Product}. * @@ -32,7 +30,11 @@ public class ProductTokenTestCase { @ParameterizedTest(name = "{1} {2}") @MethodSource("mainProductTestCases") - public void testMainProduct(final String userAgent, final String productName, final String productVersion, final String productComment) { + public void testMainProduct( + final String userAgent, + final String productName, + final String productVersion, + final String productComment) { ClientInfo clientInfo = new ClientInfo(); clientInfo.setAgent(userAgent); Product product = clientInfo.getMainAgentProduct(); @@ -44,39 +46,63 @@ public void testMainProduct(final String userAgent, final String productName, fi private static Stream mainProductTestCases() { return Stream.of( - Arguments.of("Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)", - "MSIE", "6.0", null), - Arguments.of("Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:1.8) Gecko/20051107 Camino/1.0b1", - "Camino", "1.0b1", null), - Arguments.of("Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:0.9.2) Gecko/20020508 Netscape6/6.1", - "Netscape6", "6.1", null), - Arguments.of("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)", - "Iceweasel", "2.0", "Debian-2.0+dfsg-1"), - Arguments.of("Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.15-1.2054_FC5; X11; i686; en_US) KHTML/3.5.4 (like Gecko)", - "Konqueror", "3.5.4", "like Gecko"), - Arguments.of("Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)", - "MSIE", "5.5", null), - Arguments.of("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)", - "MSIE", "6.0", null), - Arguments.of("Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/521.25 (KHTML, like Gecko) Safari/521.24", - "Safari", "521.24", null), - Arguments.of("Opera/9.00 (Macintosh; PPC Mac OS X; U; en)", - "Opera", "9.00", null), - Arguments.of("Wget/1.9", - "Wget", "1.9", null), - Arguments.of("Restlet-Framework/2.2-SNAPSHOT", - "Restlet-Framework", "2.2-SNAPSHOT", null) - ); + Arguments.of( + "Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)", + "MSIE", + "6.0", + null), + Arguments.of( + "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:1.8) Gecko/20051107 Camino/1.0b1", + "Camino", + "1.0b1", + null), + Arguments.of( + "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:0.9.2) Gecko/20020508 Netscape6/6.1", + "Netscape6", + "6.1", + null), + Arguments.of( + "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)", + "Iceweasel", + "2.0", + "Debian-2.0+dfsg-1"), + Arguments.of( + "Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.15-1.2054_FC5; X11; i686; en_US) KHTML/3.5.4 (like Gecko)", + "Konqueror", + "3.5.4", + "like Gecko"), + Arguments.of( + "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)", "MSIE", "5.5", null), + Arguments.of( + "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)", + "MSIE", + "6.0", + null), + Arguments.of( + "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/521.25 (KHTML, like Gecko) Safari/521.24", + "Safari", + "521.24", + null), + Arguments.of("Opera/9.00 (Macintosh; PPC Mac OS X; U; en)", "Opera", "9.00", null), + Arguments.of("Wget/1.9", "Wget", "1.9", null), + Arguments.of( + "Restlet-Framework/2.2-SNAPSHOT", + "Restlet-Framework", + "2.2-SNAPSHOT", + null)); } @Test public void testProductTokens() { - final String userAgent1 = "Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)"; + final String userAgent1 = + "Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)"; final String userAgent2 = "Advanced Browser (http://www.avantbrowser.com)"; final String userAgent3 = "Mozilla/5.0"; final String userAgent4 = "Mozilla"; - final String userAgent5 = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:1.8) Gecko/20051107 Camino/1.0b1"; - final String userAgent6 = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)"; + final String userAgent5 = + "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:1.8) Gecko/20051107 Camino/1.0b1"; + final String userAgent6 = + "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)"; final String userAgent7 = "Restlet-Framework/2.2-SNAPSHOT"; List list = ProductReader.read(userAgent1); @@ -109,8 +135,7 @@ public void testProductTokens() { assertEquals(3, list.size()); assertEquals("Mozilla", list.get(0).getName()); assertEquals("5.0", list.get(0).getVersion()); - assertEquals("Macintosh; U; PPC Mac OS X; en-US; rv:1.8", list.get(0) - .getComment()); + assertEquals("Macintosh; U; PPC Mac OS X; en-US; rv:1.8", list.get(0).getComment()); assertEquals("Gecko", list.get(1).getName()); assertEquals("20051107", list.get(1).getVersion()); assertNull(list.get(1).getComment()); @@ -122,8 +147,7 @@ public void testProductTokens() { assertEquals(3, list.size()); assertEquals("Mozilla", list.get(0).getName()); assertEquals("5.0", list.get(0).getVersion()); - assertEquals("X11; U; Linux i686; en-US; rv:1.8.1", list.get(0) - .getComment()); + assertEquals("X11; U; Linux i686; en-US; rv:1.8.1", list.get(0).getComment()); assertEquals("Gecko", list.get(1).getName()); assertEquals("20061024", list.get(1).getVersion()); assertNull(list.get(1).getComment()); @@ -153,5 +177,4 @@ public void testWriteThenRead() { assertEquals("1.1m4", list.get(1).getVersion()); assertEquals("This is a comment", list.get(1).getComment()); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java b/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java index 5f1b8c39d3..1cf24cc345 100644 --- a/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java @@ -1,44 +1,50 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.*; -import org.restlet.*; -import org.restlet.engine.Engine; -import org.restlet.engine.io.IoUtils; -import org.restlet.engine.local.FileClientHelper; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.Directory; -import org.restlet.routing.Router; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.engine.Engine; +import org.restlet.engine.io.IoUtils; +import org.restlet.engine.local.FileClientHelper; +import org.restlet.representation.StringRepresentation; +import org.restlet.resource.Directory; +import org.restlet.routing.Router; /** * Test {@link org.restlet.data.Range}. - * + * * @author Jerome Louvel */ public class RangeTestCase { private static final Tag ENTITY_TAG = new Tag("TestRangeGetRestlet"); - /** - * Internal class used for test purpose. - * - */ + /** Internal class used for test purpose. */ private static class TestRangeApplication extends Application { public TestRangeApplication() { @@ -53,18 +59,16 @@ public Restlet createInboundRoot() { Router router = new Router(); router.attach("/testGet", new TestRangeGetRestlet()); - Directory directory = new Directory(getContext(), LocalReference.createFileReference(testDirPath.toFile())); + Directory directory = + new Directory( + getContext(), LocalReference.createFileReference(testDirPath.toFile())); directory.setModifiable(true); router.attach("/testPut/", directory); return router; } } - /** - * Internal class used for test purpose. It simply returns a string 10 - * characters long. - * - */ + /** Internal class used for test purpose. It simply returns a string 10 characters long. */ private static class TestRangeGetRestlet extends Restlet { @Override public void handle(Request request, Response response) { @@ -107,6 +111,7 @@ void noRange() throws IOException { assertEquals(10, response.getEntity().getAvailableSize()); assertNull(response.getEntity().getRange()); } + @Test public void fullRange() throws IOException { request.setRanges(List.of(new Range(0, 10))); @@ -118,6 +123,7 @@ public void fullRange() throws IOException { assertEquals(0, response.getEntity().getRange().getIndex()); assertEquals(10, response.getEntity().getRange().getSize()); } + @Test public void rangeFirst2Bytes() throws Exception { request.setRanges(List.of(new Range(Range.INDEX_FIRST, 2))); @@ -153,6 +159,7 @@ public void range2To9Bytes() throws Exception { assertEquals(2, response.getEntity().getRange().getIndex()); assertEquals(7, response.getEntity().getRange().getSize()); } + @Test public void rangeLast7Bytes() throws Exception { request.setRanges(List.of(new Range(Range.INDEX_LAST, 7))); @@ -283,7 +290,6 @@ public void testPut() throws IOException { assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); assertEquals("20000998", response.getEntity().getText()); - // Partial PUT on a file, with a non-bytes range, not taken into account request = new Request(Method.PUT, "/testPut/essai.txt"); request.setEntity(new StringRepresentation("1234567890")); @@ -321,7 +327,6 @@ void putNewFileWithRangeWithoutSize() throws IOException { } } - @Test public void testMultipleRanges() { Request request = new Request(Method.GET, "/testGet"); @@ -330,5 +335,4 @@ public void testMultipleRanges() { Response response = testRangeApplication.handle(request); assertEquals(Status.SERVER_ERROR_NOT_IMPLEMENTED, response.getStatus()); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java index 11e8acf2e5..44272ff247 100644 --- a/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java @@ -1,42 +1,46 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.engine.header.HeaderConstants; import org.restlet.engine.header.RecipientInfoReader; import org.restlet.engine.header.RecipientInfoWriter; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - /** * Test {@link org.restlet.data.RecipientInfo}. - * + * * @author Jerome Louvel */ public class RecipientInfoTestCase { @Test public void testVia() { - Header via1a = new Header(HeaderConstants.HEADER_VIA, - "1.0 fred, 1.1 nowhere.com (Apache/1.1)"); - Header via1b = new Header(HeaderConstants.HEADER_VIA, - "HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)"); - Header via1c = new Header(HeaderConstants.HEADER_VIA, - "HTTP/1.0 fred (Apache/1.1), HTTP/1.1 nowhere.com"); - Header via1d = new Header(HeaderConstants.HEADER_VIA, - "HTTP/1.0 fred (Apache/1.1), HTTP/1.1 nowhere.com:8111"); + Header via1a = + new Header(HeaderConstants.HEADER_VIA, "1.0 fred, 1.1 nowhere.com (Apache/1.1)"); + Header via1b = + new Header( + HeaderConstants.HEADER_VIA, + "HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)"); + Header via1c = + new Header( + HeaderConstants.HEADER_VIA, + "HTTP/1.0 fred (Apache/1.1), HTTP/1.1 nowhere.com"); + Header via1d = + new Header( + HeaderConstants.HEADER_VIA, + "HTTP/1.0 fred (Apache/1.1), HTTP/1.1 nowhere.com:8111"); List recipients = new ArrayList<>(); RecipientInfoReader.addValues(via1a, recipients); diff --git a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java index d2020cd094..e40d455e71 100644 --- a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java @@ -1,39 +1,40 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.engine.header.HeaderConstants; import org.restlet.engine.util.ReferenceUtils; import org.restlet.util.Series; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - /** * Test {@link org.restlet.data.Reference}. - * + * * @author Jerome Louvel - * @author Lars Heuer (heuer[at]semagia.com) Semagia + * @author Lars Heuer (heuer[at]semagia.com) Semagia */ public class ReferenceTestCase { - protected final static String DEFAULT_SCHEME = "http"; + protected static final String DEFAULT_SCHEME = "http"; - protected final static String DEFAULT_SCHEMEPART = "//"; + protected static final String DEFAULT_SCHEMEPART = "//"; /** * Returns a reference that is initialized with http://restlet.org. - * + * * @return Reference instance. */ protected Reference getDefaultReference() { @@ -44,7 +45,7 @@ protected Reference getDefaultReference() { /** * Returns a reference with uri == http:// - * + * * @return Reference instance. */ protected Reference getReference() { @@ -54,9 +55,7 @@ protected Reference getReference() { return ref; } - /** - * Test addition methods. - */ + /** Test addition methods. */ @Test public void testAdditions() throws Exception { final Reference ref = new Reference("http://restlet.org"); @@ -118,9 +117,7 @@ public void testEmptyRef() { reference.setSegments(segments); // must not produce NPE } - /** - * Equality tests. - */ + /** Equality tests. */ @Test public void testEquals() { final Reference ref1 = getDefaultReference(); @@ -129,30 +126,28 @@ public void testEquals() { assertEquals(ref1, ref2); } - @Test + @Test public void testGetLastSegment() { Reference reference = new Reference("http://hostname"); assertNull(reference.getLastSegment()); - + reference = new Reference("http://hostname/"); assertNull(reference.getLastSegment()); - + reference = new Reference("http://hostname/abc"); assertEquals("abc", reference.getLastSegment()); - + reference = new Reference("http://hostname/abc/"); assertEquals("abc", reference.getLastSegment()); - + reference = new Reference("http://hostname/123/abc/"); assertEquals("abc", reference.getLastSegment()); - + reference = new Reference("http://hostname/123/abc"); assertEquals("abc", reference.getLastSegment()); } - /** - * Test hostname getting/setting. - */ + /** Test hostname getting/setting. */ @Test public void testHostName() { final Reference ref = getReference(); @@ -168,12 +163,10 @@ public void testHostName() { @Test public void testMatrix() { - final Reference ref1 = new Reference( - "http://domain.tld/whatever/a=1;b=2;c=4?x=a&y=b"); - final Reference ref2 = new Reference( - "http://domain.tld/whatever/a=1/foo;b=2;c=4;d?x=a&y=b"); - final Reference ref3 = new Reference( - "http://domain.tld/whatever/a=1;b=2;c=4/foo?x=a&y=b"); + final Reference ref1 = new Reference("http://domain.tld/whatever/a=1;b=2;c=4?x=a&y=b"); + final Reference ref2 = + new Reference("http://domain.tld/whatever/a=1/foo;b=2;c=4;d?x=a&y=b"); + final Reference ref3 = new Reference("http://domain.tld/whatever/a=1;b=2;c=4/foo?x=a&y=b"); assertTrue(ref1.hasMatrix()); assertTrue(ref2.hasMatrix()); @@ -210,10 +203,7 @@ public void testOriginalRef() { assertEquals(originalRef.getHostPort(), 123); } - /** - * Test the computation of parent references, for absolute and relative - * URIs. - */ + /** Test the computation of parent references, for absolute and relative URIs. */ @Test public void testParentRef() { Reference baseRef = new Reference("http://test.com/foo/bar"); @@ -225,9 +215,7 @@ public void testParentRef() { assertEquals("/foo/", parentRef.toString()); } - /** - * Tests the URI parsing. - */ + /** Tests the URI parsing. */ @Test public void testParsing() { final String base = "http://a/b/c/d;p?q"; @@ -321,27 +309,30 @@ public void testParsing() { final Reference fullsub = new Reference("http://host.com/dir/sub"); // Test the parsing of references into its components - testRef0("foo://example.com:8042/over/there?name=ferret#nose", "foo", - "example.com:8042", "/over/there", "name=ferret", "nose"); - testRef0("urn:example:animal:ferret:nose", "urn", null, - "example:animal:ferret:nose", null, null); - testRef0("mailto:fred@example.com", "mailto", null, "fred@example.com", - null, null); - testRef0("foo://info.example.com?fred", "foo", "info.example.com", - null, "fred", null); - testRef0("*", null, null, "*", null, null); - testRef0("http://localhost?query", "http", "localhost", null, "query", - null); - testRef0("http://localhost#?query", "http", "localhost", null, null, - "?query"); - testRef0("http://localhost/?query", "http", "localhost", "/", "query", + testRef0( + "foo://example.com:8042/over/there?name=ferret#nose", + "foo", + "example.com:8042", + "/over/there", + "name=ferret", + "nose"); + testRef0( + "urn:example:animal:ferret:nose", + "urn", + null, + "example:animal:ferret:nose", + null, null); - testRef0("http://localhost/#?query", "http", "localhost", "/", null, - "?query"); - testRef0("http://localhost/path#frag/ment", "http", "localhost", - "/path", null, "frag/ment"); - testRef0("http://localhost/path?qu/ery", "http", "localhost", "/path", - "qu/ery", null); + testRef0("mailto:fred@example.com", "mailto", null, "fred@example.com", null, null); + testRef0("foo://info.example.com?fred", "foo", "info.example.com", null, "fred", null); + testRef0("*", null, null, "*", null, null); + testRef0("http://localhost?query", "http", "localhost", null, "query", null); + testRef0("http://localhost#?query", "http", "localhost", null, null, "?query"); + testRef0("http://localhost/?query", "http", "localhost", "/", "query", null); + testRef0("http://localhost/#?query", "http", "localhost", "/", null, "?query"); + testRef0( + "http://localhost/path#frag/ment", "http", "localhost", "/path", null, "frag/ment"); + testRef0("http://localhost/path?qu/ery", "http", "localhost", "/path", "qu/ery", null); // Test the resolution of relative references testRef1(base, uri01, uri101); @@ -402,71 +393,112 @@ public void testParsing() { testRef2(uri104, uri118, uri21); // Test the toString method with or without query/fragment - testRef3("http://localhost/path#fragment", true, true, - "http://localhost/path#fragment"); - testRef3("http://localhost/path#fragment", true, false, - "http://localhost/path"); - testRef3("http://localhost/path#fragment", false, true, - "http://localhost/path#fragment"); - testRef3("http://localhost/path#fragment", false, false, - "http://localhost/path"); - - testRef3("http://localhost/path?query", true, true, - "http://localhost/path?query"); - testRef3("http://localhost/path?query", true, false, - "http://localhost/path?query"); - testRef3("http://localhost/path?query", false, true, - "http://localhost/path"); - testRef3("http://localhost/path?query", false, false, - "http://localhost/path"); - - testRef3("http://localhost/path?query#fragment", true, true, + testRef3("http://localhost/path#fragment", true, true, "http://localhost/path#fragment"); + testRef3("http://localhost/path#fragment", true, false, "http://localhost/path"); + testRef3("http://localhost/path#fragment", false, true, "http://localhost/path#fragment"); + testRef3("http://localhost/path#fragment", false, false, "http://localhost/path"); + + testRef3("http://localhost/path?query", true, true, "http://localhost/path?query"); + testRef3("http://localhost/path?query", true, false, "http://localhost/path?query"); + testRef3("http://localhost/path?query", false, true, "http://localhost/path"); + testRef3("http://localhost/path?query", false, false, "http://localhost/path"); + + testRef3( + "http://localhost/path?query#fragment", + true, + true, "http://localhost/path?query#fragment"); - testRef3("http://localhost/path?query#fragment", true, false, - "http://localhost/path?query"); - testRef3("http://localhost/path?query#fragment", false, true, + testRef3( + "http://localhost/path?query#fragment", true, false, "http://localhost/path?query"); + testRef3( + "http://localhost/path?query#fragment", + false, + true, "http://localhost/path#fragment"); - testRef3("http://localhost/path?query#fragment", false, false, - "http://localhost/path"); + testRef3("http://localhost/path?query#fragment", false, false, "http://localhost/path"); - testRef3("http://localhost/path#fragment?query", true, true, + testRef3( + "http://localhost/path#fragment?query", + true, + true, "http://localhost/path#fragment?query"); - testRef3("http://localhost/path#fragment?query", true, false, - "http://localhost/path"); - testRef3("http://localhost/path#fragment?query", false, true, + testRef3("http://localhost/path#fragment?query", true, false, "http://localhost/path"); + testRef3( + "http://localhost/path#fragment?query", + false, + true, "http://localhost/path#fragment?query"); - testRef3("http://localhost/path#fragment?query", false, false, - "http://localhost/path"); - - testRef4(host, "http", "host.com", null, "http://host.com", - "http://host.com", "http://host.com", null, null); - testRef4(slashdir, null, null, "/dir", null, "/dir", - "http://host.com/dir", null, "/dir"); - testRef4(dir, null, null, "dir", null, "dir", "http://host.com/dir", - null, "dir"); - testRef4(dirslash, null, null, "dir/", null, "dir/", - "http://host.com/dir/", null, "dir/"); - testRef4(fulldir, "http", "host.com", "/dir", "http://host.com/dir", - "http://host.com/dir", "http://host.com/dir", null, null); - - testRef4(fulldirsub, null, null, "sub", null, "sub", - "http://host.com/sub", null, "sub"); - testRef4(fulldirslashsub, null, null, "/sub", null, "/sub", - "http://host.com/sub", null, "/sub"); - testRef4(slashdirsub, null, null, "sub", null, "sub", - "http://host.com/sub", null, "sub"); - testRef4(slashdirslashsub, null, null, "/sub", null, "/sub", - "http://host.com/sub", null, "/sub"); - testRef4(dirslashsub, null, null, "sub", null, "sub", - "http://host.com/dir/sub", null, "sub"); - testRef4(fullsub, "http", "host.com", "/dir/sub", - "http://host.com/dir/sub", "http://host.com/dir/sub", - "http://host.com/dir/sub", null, null); + testRef3("http://localhost/path#fragment?query", false, false, "http://localhost/path"); + + testRef4( + host, + "http", + "host.com", + null, + "http://host.com", + "http://host.com", + "http://host.com", + null, + null); + testRef4(slashdir, null, null, "/dir", null, "/dir", "http://host.com/dir", null, "/dir"); + testRef4(dir, null, null, "dir", null, "dir", "http://host.com/dir", null, "dir"); + testRef4(dirslash, null, null, "dir/", null, "dir/", "http://host.com/dir/", null, "dir/"); + testRef4( + fulldir, + "http", + "host.com", + "/dir", + "http://host.com/dir", + "http://host.com/dir", + "http://host.com/dir", + null, + null); + + testRef4(fulldirsub, null, null, "sub", null, "sub", "http://host.com/sub", null, "sub"); + testRef4( + fulldirslashsub, + null, + null, + "/sub", + null, + "/sub", + "http://host.com/sub", + null, + "/sub"); + testRef4(slashdirsub, null, null, "sub", null, "sub", "http://host.com/sub", null, "sub"); + testRef4( + slashdirslashsub, + null, + null, + "/sub", + null, + "/sub", + "http://host.com/sub", + null, + "/sub"); + testRef4( + dirslashsub, + null, + null, + "sub", + null, + "sub", + "http://host.com/dir/sub", + null, + "sub"); + testRef4( + fullsub, + "http", + "host.com", + "/dir/sub", + "http://host.com/dir/sub", + "http://host.com/dir/sub", + "http://host.com/dir/sub", + null, + null); } - /** - * Test port getting/setting. - */ + /** Test port getting/setting. */ @Test public void testPort() { Reference ref = getDefaultReference(); @@ -482,10 +514,10 @@ public void testPort() { @Test public void testProtocolConstructors() { - assertEquals("http://restlet.org", new Reference(Protocol.HTTP, - "restlet.org").toString()); - assertEquals("https://restlet.org:8443", new Reference(Protocol.HTTPS, - "restlet.org", 8443).toString()); + assertEquals("http://restlet.org", new Reference(Protocol.HTTP, "restlet.org").toString()); + assertEquals( + "https://restlet.org:8443", + new Reference(Protocol.HTTPS, "restlet.org", 8443).toString()); final Reference ref = new Reference(Protocol.HTTP, "restlet.org"); ref.addQueryParameter("abc", "123"); @@ -495,36 +527,34 @@ public void testProtocolConstructors() { @Test public void testQuery() { - Reference ref1 = new Reference( - "http://localhost/search?q=anythingelse%"); + Reference ref1 = new Reference("http://localhost/search?q=anythingelse%"); String query = ref1.getQuery(); assertEquals("q=anythingelse%25", query); Form queryForm = ref1.getQueryAsForm(); assertEquals("anythingelse%", queryForm.getFirstValue("q")); - Form extJsQuery = new Form( - "&_dc=1244741620627&callback=stcCallback1001"); + Form extJsQuery = new Form("&_dc=1244741620627&callback=stcCallback1001"); assertEquals("1244741620627", extJsQuery.getFirstValue("_dc")); assertEquals("stcCallback1001", extJsQuery.getFirstValue("callback")); Reference ref = new Reference("http://localhost/v1/projects/13404"); ref.addQueryParameter("dyn", "true"); - assertEquals("http://localhost/v1/projects/13404?dyn=true", - ref.toString()); + assertEquals("http://localhost/v1/projects/13404?dyn=true", ref.toString()); } @Test public void testQueryWithUri() { - Reference ref = new Reference(new Reference("http://localhost:8111/"), - "http://localhost:8111/contrats/123?srvgwt=localhost:9997"); - assertEquals("contrats/123?srvgwt=localhost:9997", ref.getRelativeRef() - .toString()); + Reference ref = + new Reference( + new Reference("http://localhost:8111/"), + "http://localhost:8111/contrats/123?srvgwt=localhost:9997"); + assertEquals("contrats/123?srvgwt=localhost:9997", ref.getRelativeRef().toString()); } /** * Tests the parsing of a reference into its components - * + * * @param reference * @param scheme * @param authority @@ -532,8 +562,13 @@ public void testQueryWithUri() { * @param query * @param fragment */ - private void testRef0(String reference, String scheme, String authority, - String path, String query, String fragment) { + private void testRef0( + String reference, + String scheme, + String authority, + String path, + String query, + String fragment) { final Reference ref = new Reference(reference); assertEquals(scheme, ref.getScheme()); assertEquals(authority, ref.getAuthority()); @@ -544,13 +579,12 @@ private void testRef0(String reference, String scheme, String authority, /** * Test the resolution of relative references. - * + * * @param baseUri * @param relativeUri * @param expectedAbsoluteUri */ - private void testRef1(String baseUri, String relativeUri, - String expectedAbsoluteUri) { + private void testRef1(String baseUri, String relativeUri, String expectedAbsoluteUri) { final Reference baseRef = new Reference(baseUri); final Reference relativeRef = new Reference(baseRef, relativeUri); final Reference absoluteRef = relativeRef.getTargetRef(); @@ -559,13 +593,12 @@ private void testRef1(String baseUri, String relativeUri, /** * Test the relativization of absolute references - * + * * @param baseUri * @param absoluteUri * @param expectedRelativeUri */ - private void testRef2(String baseUri, String absoluteUri, - String expectedRelativeUri) { + private void testRef2(String baseUri, String absoluteUri, String expectedRelativeUri) { final Reference baseRef = new Reference(baseUri); final Reference absoluteRef = new Reference(absoluteUri); final Reference relativeRef = absoluteRef.getRelativeRef(baseRef); @@ -574,24 +607,28 @@ private void testRef2(String baseUri, String absoluteUri, /** * Test the toString method with or without query/fragment - * + * * @param reference * @param query * @param fragment * @param toString */ - private void testRef3(String reference, boolean query, boolean fragment, - String toString) { + private void testRef3(String reference, boolean query, boolean fragment, String toString) { final Reference ref = new Reference(reference); assertEquals(ref.toString(query, fragment), toString); } - /** - * Test the behaviour of several getters upon a Reference object. - */ - private void testRef4(Reference reference, String scheme, String authority, - String path, String remainingPart, String toString, - String targetRef, String query, String relativePart) { + /** Test the behaviour of several getters upon a Reference object. */ + private void testRef4( + Reference reference, + String scheme, + String authority, + String path, + String remainingPart, + String toString, + String targetRef, + String query, + String relativePart) { assertEquals(reference.getScheme(), scheme); assertEquals(reference.getAuthority(), authority); assertEquals(reference.getPath(), path); @@ -606,13 +643,10 @@ private void testRef4(Reference reference, String scheme, String authority, public void testRiap() { Reference baseRef = new Reference("riap://component/exist/db/"); Reference ref = new Reference(baseRef, "something.xq"); - assertEquals("riap://component/exist/db/something.xq", ref - .getTargetRef().toString()); + assertEquals("riap://component/exist/db/something.xq", ref.getTargetRef().toString()); } - /** - * Test scheme getting/setting. - */ + /** Test scheme getting/setting. */ @Test public void testScheme() { final Reference ref = getDefaultReference(); @@ -624,9 +658,7 @@ public void testScheme() { assertEquals(DEFAULT_SCHEME, ref.getScheme()); } - /** - * Test scheme specific part getting/setting. - */ + /** Test scheme-specific part getting/setting. */ @Test public void testSchemeSpecificPart() { final Reference ref = getDefaultReference(); @@ -637,9 +669,7 @@ public void testSchemeSpecificPart() { assertEquals(part, ref.getSchemeSpecificPart()); } - /** - * Test setting of the last segment. - */ + /** Test setting of the last segment. */ @Test public void testSetLastSegment() { Reference ref = new Reference("http://localhost:1234"); @@ -662,20 +692,21 @@ public void testSetLastSegment() { @Test public void testTargetRef() { - Reference ref = new Reference( - "http://twitter.com?status=RT @gamasutra: Devil May Cry : Born Again http://www.gamasutra.com/view/feature/177267/"); - Reference targetRef = new Reference( + Reference ref = new Reference( - "http://www.gamasutra.com/view/feature/177267/devil_may_cry_born_again.php"), - ref).getTargetRef(); + "http://twitter.com?status=RT @gamasutra: Devil May Cry : Born Again http://www.gamasutra.com/view/feature/177267/"); + Reference targetRef = + new Reference( + new Reference( + "http://www.gamasutra.com/view/feature/177267/devil_may_cry_born_again.php"), + ref) + .getTargetRef(); assertEquals( "http://twitter.com?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http:?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http://www.gamasutra.com/view/feature/177267/", targetRef.toString()); } - /** - * Test references that are unequal. - */ + /** Test references that are unequal. */ @Test public void testUnEquals() throws Exception { final String uri1 = "http://restlet.org/"; @@ -703,15 +734,13 @@ public void testUserinfo() { assertEquals("login:password", reference.getUserInfo()); reference.setHostDomain("www.example.com"); - assertEquals("login:password@www.example.com:81", - reference.getAuthority()); + assertEquals("login:password@www.example.com:81", reference.getAuthority()); assertEquals("www.example.com", reference.getHostDomain()); assertEquals(81, reference.getHostPort()); assertEquals("login:password", reference.getUserInfo()); reference.setHostPort(82); - assertEquals("login:password@www.example.com:82", - reference.getAuthority()); + assertEquals("login:password@www.example.com:82", reference.getAuthority()); assertEquals("www.example.com", reference.getHostDomain()); assertEquals(82, reference.getHostPort()); assertEquals("login:password", reference.getUserInfo()); diff --git a/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java b/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java index 4e71bb98dd..63cdc851e9 100644 --- a/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java @@ -1,38 +1,40 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.restlet.*; +import org.restlet.Application; +import org.restlet.Component; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.engine.Engine; import org.restlet.representation.Representation; import org.restlet.resource.ClientResource; import org.restlet.routing.Router; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Unit test case for the RIAP Internal routing protocol. - */ +/** Unit test case for the RIAP Internal routing protocol. */ public class RiapConnectorsTestCase { - /** - * Test the RIAP client and server connectors. - */ + /** Test the RIAP client and server connectors. */ @ParameterizedTest - @ValueSource(strings = { "riap://component/app/test", "riap://component/app/redirectToInternalResource" }) + @ValueSource( + strings = { + "riap://component/app/test", + "riap://component/app/redirectToInternalResource" + }) public void testRiapConnectors(final String url) throws IOException { ClientResource res = new ClientResource(url); Representation rep = res.get(); @@ -51,27 +53,32 @@ static void setUp() throws Exception { component.getServers().add(Protocol.RIAP); component.getClients().add(Protocol.RIAP); - Application app = new Application() { - @Override - public Restlet createInboundRoot() { - Router router = new Router(getContext()); - router.attach("/test", new Restlet(getContext()) { - + Application app = + new Application() { @Override - public void handle(Request request, Response response) { - response.setEntity("hello, world", MediaType.TEXT_PLAIN); + public Restlet createInboundRoot() { + Router router = new Router(getContext()); + router.attach( + "/test", + new Restlet(getContext()) { + + @Override + public void handle(Request request, Response response) { + response.setEntity("hello, world", MediaType.TEXT_PLAIN); + } + }); + router.attach( + "/redirectToInternalResource", + new Restlet(getContext()) { + public void handle(Request request, Response response) { + ClientResource resource = + new ClientResource("riap://component/app/test"); + response.setEntity(resource.get()); + } + }); + return router; } - - }); - router.attach("/redirectToInternalResource", new Restlet(getContext()) { - public void handle(Request request, Response response) { - ClientResource resource = new ClientResource("riap://component/app/test"); - response.setEntity(resource.get()); - } - }); - return router; - } - }; + }; // Attach the private application component.getInternalRouter().attach("/app", app); @@ -85,5 +92,4 @@ static void tearDown() throws Exception { component.stop(); component = null; } - } diff --git a/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java b/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java index 70c9a7bdb7..afe5e57cb7 100644 --- a/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java @@ -1,28 +1,33 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.fail; +import static org.restlet.data.LocalReference.RIAP_APPLICATION; +import static org.restlet.data.LocalReference.createRiapReference; + +import java.io.Serializable; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.restlet.*; +import org.restlet.Application; +import org.restlet.Component; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.representation.ObjectRepresentation; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import org.restlet.resource.ClientResource; -import java.io.Serializable; - -import static org.junit.jupiter.api.Assertions.*; -import static org.restlet.data.LocalReference.RIAP_APPLICATION; -import static org.restlet.data.LocalReference.createRiapReference; - /** * Unit test case for the RIAP Internal routing protocol. * @@ -51,39 +56,46 @@ private static String buildAggregate(String echoMessage, String echoCopy) { @BeforeAll public static void setUp() { final Component comp = new Component(); - final Application localOnly = new Application() { - @Override - public Restlet createInboundRoot() { - return new Restlet(getContext()) { + final Application localOnly = + new Application() { @Override - public void handle(Request request, Response response) { - final Reference ref = request.getResourceRef(); - final String remainder = ref.getRemainingPart(); - - Representation result = new StringRepresentation(DEFAULT_MSG); - - if (remainder.startsWith("/echo/")) { - result = new StringRepresentation(remainder.substring(6)); - } else if (remainder.equals("/object")) { - result = new ObjectRepresentation<>(JUST_SOME_OBJ); - } else if (remainder.equals("/null")) { - result = new ObjectRepresentation<>((Serializable) null); - } else if (remainder.equals("/self-aggregated")) { - final String echoMessage = ECHO_TEST_MSG; - final Reference echoRef = createRiapReference(RIAP_APPLICATION, "/echo/" + echoMessage); - try { - String echoCopy = new ClientResource(echoRef).get().getText(); - assertEquals(echoMessage, echoCopy, "expected echoMessage back"); - result = new StringRepresentation(buildAggregate( echoMessage, echoCopy)); - } catch (Exception e) { - fail("Error getting internal reference to " + echoRef, e); + public Restlet createInboundRoot() { + return new Restlet(getContext()) { + @Override + public void handle(Request request, Response response) { + final Reference ref = request.getResourceRef(); + final String remainder = ref.getRemainingPart(); + + Representation result = new StringRepresentation(DEFAULT_MSG); + + if (remainder.startsWith("/echo/")) { + result = new StringRepresentation(remainder.substring(6)); + } else if (remainder.equals("/object")) { + result = new ObjectRepresentation<>(JUST_SOME_OBJ); + } else if (remainder.equals("/null")) { + result = new ObjectRepresentation<>((Serializable) null); + } else if (remainder.equals("/self-aggregated")) { + final String echoMessage = ECHO_TEST_MSG; + final Reference echoRef = + createRiapReference( + RIAP_APPLICATION, "/echo/" + echoMessage); + try { + String echoCopy = + new ClientResource(echoRef).get().getText(); + assertEquals( + echoMessage, echoCopy, "expected echoMessage back"); + result = + new StringRepresentation( + buildAggregate(echoMessage, echoCopy)); + } catch (Exception e) { + fail("Error getting internal reference to " + echoRef, e); + } + } + response.setEntity(result); } - } - response.setEntity(result); + }; } }; - } - }; comp.getInternalRouter().attach("/local", localOnly); @@ -101,9 +113,10 @@ public void testEcho() throws Exception { @Test public void testObject() throws Exception { final Representation objRep = sendLocalRequestAndGetRepresentation("/object"); - assertSame(JUST_SOME_OBJ, ((ObjectRepresentation) objRep).getObject(), - "expected specific test-object" - ); + assertSame( + JUST_SOME_OBJ, + ((ObjectRepresentation) objRep).getObject(), + "expected specific test-object"); } @Test @@ -126,6 +139,6 @@ public void testSelfAggregated() throws Exception { } private static Representation sendLocalRequestAndGetRepresentation(final String url) { - return dispatcher.handle(new Request(Method.GET, localBase+ url)).getEntity(); + return dispatcher.handle(new Request(Method.GET, localBase + url)).getEntity(); } } diff --git a/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java b/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java index 6ba5009254..7584c6e4a2 100644 --- a/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java @@ -1,21 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * Test {@link org.restlet.data.Status}. - * + * * @author Jerome Louvel */ public class StatusTestCase { @@ -27,9 +28,7 @@ public void testCustomDescription() { assertEquals(customDescription, s.getDescription()); } - /** - * Equality tests. - */ + /** Equality tests. */ @Test public void testEquals() { final Status s1 = new Status(201); @@ -40,9 +39,7 @@ public void testEquals() { assertEquals(s1, s2); } - /** - * Tests for status classes. - */ + /** Tests for status classes. */ @Test public void testStatusClasses() { final Status s1 = new Status(287); @@ -53,9 +50,7 @@ public void testStatusClasses() { assertTrue(s2.isError()); } - /** - * Unequality tests. - */ + /** Unequality tests. */ @Test public void testUnEquals() { final Status s1 = new Status(200); @@ -66,5 +61,4 @@ public void testUnEquals() { assertNotEquals(null, s1); assertNotEquals(null, s2); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/TagTestCase.java b/org.restlet/src/test/java/org/restlet/data/TagTestCase.java index 05c4a189b5..ba84f34eed 100644 --- a/org.restlet/src/test/java/org/restlet/data/TagTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/TagTestCase.java @@ -1,21 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; -import org.junit.jupiter.api.Test; -import org.restlet.engine.header.TagReader; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.restlet.engine.header.TagReader; /** * Test {@link org.restlet.data.Tag}. @@ -70,5 +71,4 @@ public void testListOfTagsWithInvalidTag() { assertEquals(Tag.ALL.getName(), tags.get(2).getName()); assertEquals(3, tags.size()); } - } diff --git a/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java b/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java index aaf09f59db..afcb155f34 100644 --- a/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java @@ -1,14 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -18,14 +24,6 @@ import org.restlet.resource.ClientResource; import org.restlet.resource.ResourceException; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - /** * Unit test case for the Zip client connector. * @@ -89,13 +87,17 @@ void testFileClient() throws IOException { assertEquals(dirEntryClientResource.getStatus(), Status.SUCCESS_OK); // Add a file inside the directory - ClientResource testFileInDirEntryCLientResource = new ClientResource(test3FileInDirEntryReference); + ClientResource testFileInDirEntryCLientResource = + new ClientResource(test3FileInDirEntryReference); testFileInDirEntryCLientResource.put(new StringRepresentation(text)); assertEquals(testFileInDirEntryCLientResource.getStatus(), Status.SUCCESS_OK); // Check that the second entry is still there test2FileEntryClientResource.get(); - assertEquals(test2FileEntryClientResource.getStatus(), Status.SUCCESS_OK, "Could not get " + test2FileEntryReference); + assertEquals( + test2FileEntryClientResource.getStatus(), + Status.SUCCESS_OK, + "Could not get " + test2FileEntryReference); assertEquals(test2FileEntryClientResource.getResponseEntity().getText(), text2); // Check that content negotiation does not work diff --git a/org.restlet/src/test/java/org/restlet/engine/EngineTest.java b/org.restlet/src/test/java/org/restlet/engine/EngineTest.java index fe0e1f4038..7bb6f97ca8 100644 --- a/org.restlet/src/test/java/org/restlet/engine/EngineTest.java +++ b/org.restlet/src/test/java/org/restlet/engine/EngineTest.java @@ -1,22 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.InputStream; import java.util.Properties; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class EngineTest { @@ -24,13 +22,15 @@ public class EngineTest { public void engineVersionShouldBeEqualToMavenProjectVersion() { // When I retrieve the Maven project's version as stated in the pom file. Properties properties = new Properties(); - try (InputStream resourceAsStream = EngineTest.class.getClassLoader().getResourceAsStream("maven-version.properties")) { + try (InputStream resourceAsStream = + EngineTest.class.getClassLoader().getResourceAsStream("maven-version.properties")) { properties.load(resourceAsStream); } catch (IOException e) { - Assertions.fail("Can't load the properties file that contain the Maven's project version"); + Assertions.fail( + "Can't load the properties file that contain the Maven's project version"); } // Then the Maven project's version should be equal to the Engine's version. assertEquals(Engine.VERSION, properties.getProperty("maven.version")); } -} \ No newline at end of file +} diff --git a/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java index 25e3512a58..c73943935c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java @@ -1,14 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collection; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; @@ -20,11 +25,7 @@ import org.restlet.resource.Options; import org.restlet.resource.ServerResource; -import java.util.Collection; - -import static org.junit.jupiter.api.Assertions.*; - - /** +/** * @author Manuel Boillod */ public class CorsResponseFilterTestCase { @@ -33,9 +34,10 @@ public class CorsResponseFilterTestCase { public static class DummyServerResource extends ServerResource { @Options - public void doOption(){} + public void doOption() {} + @Get - public void doGet(){} + public void doGet() {} } @BeforeEach @@ -111,7 +113,9 @@ public void testOption_requestGet() { assertEquals("*", response.getAccessControlAllowOrigin()); assertNull(response.getAccessControlAllowCredentials()); assertIsEmpty(response.getAccessControlAllowHeaders()); - MatcherAssert.assertThat(response.getAccessControlAllowMethods(), Matchers.contains(Method.GET, Method.OPTIONS)); + MatcherAssert.assertThat( + response.getAccessControlAllowMethods(), + Matchers.contains(Method.GET, Method.OPTIONS)); assertIsEmpty(response.getAccessControlExposeHeaders()); } @@ -127,7 +131,10 @@ public void testOption_requestGet_skippingResource() { assertEquals("*", response.getAccessControlAllowOrigin()); assertNull(response.getAccessControlAllowCredentials()); assertIsEmpty(response.getAccessControlAllowHeaders()); - MatcherAssert.assertThat(response.getAccessControlAllowMethods(), Matchers.containsInAnyOrder(Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); + MatcherAssert.assertThat( + response.getAccessControlAllowMethods(), + Matchers.containsInAnyOrder( + Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); assertIsEmpty(response.getAccessControlExposeHeaders()); } @@ -143,7 +150,10 @@ public void testOption_requestPost_skippingResource() { assertEquals("*", response.getAccessControlAllowOrigin()); assertNull(response.getAccessControlAllowCredentials()); assertIsEmpty(response.getAccessControlAllowHeaders()); - MatcherAssert.assertThat(response.getAccessControlAllowMethods(), Matchers.containsInAnyOrder(Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); + MatcherAssert.assertThat( + response.getAccessControlAllowMethods(), + Matchers.containsInAnyOrder( + Method.GET, Method.POST, Method.PUT, Method.DELETE, Method.PATCH)); assertIsEmpty(response.getAccessControlExposeHeaders()); } @@ -159,7 +169,9 @@ public void testOption_requestGet_withAuthenticationAllowed() { assertEquals("localhost", response.getAccessControlAllowOrigin()); assertEquals(Boolean.TRUE, response.getAccessControlAllowCredentials()); assertIsEmpty(response.getAccessControlAllowHeaders()); - MatcherAssert.assertThat(response.getAccessControlAllowMethods(), Matchers.contains(Method.GET, Method.OPTIONS)); + MatcherAssert.assertThat( + response.getAccessControlAllowMethods(), + Matchers.contains(Method.GET, Method.OPTIONS)); assertIsEmpty(response.getAccessControlExposeHeaders()); } @@ -175,5 +187,4 @@ public void assertNoCorsHeaders(Response response) { assertIsEmpty(response.getAccessControlAllowMethods()); assertIsEmpty(response.getAccessControlExposeHeaders()); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java index 20552ee461..c2c6d7c223 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java @@ -1,24 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + import org.junit.jupiter.api.Test; import org.restlet.data.Range; import org.restlet.representation.StringRepresentation; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - /** * Unit test case for the {@link RangeRepresentation} class. - * + * * @author Jerome Louvel */ public class RangeRepresentationTestCase { @@ -46,5 +45,4 @@ public void testSize() throws Exception { assertEquals("1234567890", sr.getText()); assertEquals("67890", rr.getText()); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java index bde324499b..e649aea819 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java @@ -1,14 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.application; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,20 +23,19 @@ import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; -import org.restlet.data.*; +import org.restlet.data.CharacterSet; +import org.restlet.data.Encoding; +import org.restlet.data.Header; +import org.restlet.data.Language; +import org.restlet.data.MediaType; +import org.restlet.data.Metadata; +import org.restlet.data.Method; +import org.restlet.data.Preference; +import org.restlet.data.Reference; import org.restlet.engine.header.HeaderConstants; import org.restlet.util.Series; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Tests cases for the tunnel filter. - */ +/** Tests cases for the tunnel filter. */ public class TunnelFilterTestCase { /** . */ @@ -39,7 +45,8 @@ public class TunnelFilterTestCase { private static final String QUERY = "http://example.org/?start=2013-11-26T03%3A45%2B1300"; /** . */ - private static final String QUERY_PREF = "http://example.org/?start=2013-11-26T03%3A45%2B1300&media=txt"; + private static final String QUERY_PREF = + "http://example.org/?start=2013-11-26T03%3A45%2B1300&media=txt"; /** . */ private static final String START_REF_FOR_PATH_TEST = "http://www.example.com/abc/def/"; @@ -74,7 +81,8 @@ void assertEncodings(Encoding... encodings) { } @SafeVarargs - final void assertEqualSet(List> actual, A... expected) { + final void assertEqualSet( + List> actual, A... expected) { if (actual.size() != expected.length) { System.out.println("Is: " + actual); System.out.println("Should: " + Arrays.asList(expected)); @@ -89,8 +97,7 @@ final void assertEqualSet(List> act } } if (!contained) { - final String message = exp - + " should be in, but is missing in " + actual; + final String message = exp + " should be in, but is missing in " + actual; fail(message); } } @@ -127,19 +134,14 @@ private void check(String expectedCut, String expectedExtensions) { } /** - * - * @param expectedSubPathCut - * if null, the same as subPathOrig - * @param expectedExtension - * if null, then same as "" for this test + * @param expectedSubPathCut if null, the same as subPathOrig + * @param expectedExtension if null, then same as "" for this test */ - private void checkFromPath(String expectedSubPathCut, - String expectedExtension) { + private void checkFromPath(String expectedSubPathCut, String expectedExtension) { if (expectedSubPathCut == null) { check(this.lastCreatedReference, expectedExtension); } else { - check(START_REF_FOR_PATH_TEST + expectedSubPathCut, - expectedExtension); + check(START_REF_FOR_PATH_TEST + expectedSubPathCut, expectedExtension); } } @@ -152,7 +154,6 @@ void createGet(String reference) { } /** - * * @param subPathToCheck * @see #createGet(String) * @see #createRequest(Method, String) @@ -161,17 +162,15 @@ private void createGetFromPath(String subPathToCheck) { createGet(START_REF_FOR_PATH_TEST + subPathToCheck); } - /** - * - */ + /** */ void createPost(String reference) { createRequest(Method.POST, reference); } /** * Creates a {@link Request} and put it into {@link #request}.
- * To use the methods provided by the test case class use ever the provided - * create methods to create a request. + * To use the methods provided by the test case class use ever the provided create methods to + * create a request. * * @param method * @param reference @@ -193,20 +192,16 @@ private void extensionTunnelOff() { application.getTunnelService().setExtensionsTunnel(false); } - /** - * Call this method to filter the current request - */ + /** Call this method to filter the current request */ private void filter() { this.tunnelFilter.beforeHandle(this.request, this.response); setPrefs(); } private void setPrefs() { - this.accMediaTypes = this.request.getClientInfo() - .getAcceptedMediaTypes(); + this.accMediaTypes = this.request.getClientInfo().getAcceptedMediaTypes(); this.accLanguages = this.request.getClientInfo().getAcceptedLanguages(); - this.accCharsets = this.request.getClientInfo() - .getAcceptedCharacterSets(); + this.accCharsets = this.request.getClientInfo().getAcceptedCharacterSets(); this.accEncodings = this.request.getClientInfo().getAcceptedEncodings(); } @@ -215,8 +210,7 @@ public void setUpEach() throws Exception { Application app = new Application(new Context()); Application.setCurrent(app); this.tunnelFilter = new TunnelFilter(app.getContext()); - this.tunnelFilter.getApplication().getTunnelService() - .setExtensionsTunnel(true); + this.tunnelFilter.getApplication().getTunnelService().setExtensionsTunnel(true); } @AfterEach @@ -230,10 +224,8 @@ protected void tearDownEach() throws Exception { public void testExtMappingOff1() { extensionTunnelOff(); createGet(UNEFFECTED); - this.accLanguages - .add(new Preference<>(Language.valueOf("ajh"))); - this.accMediaTypes.add(new Preference<>( - MediaType.APPLICATION_STUFFIT)); + this.accLanguages.add(new Preference<>(Language.valueOf("ajh"))); + this.accMediaTypes.add(new Preference<>(MediaType.APPLICATION_STUFFIT)); filter(); assertEquals(UNEFFECTED, this.request.getResourceRef().toString()); assertLanguages(Language.valueOf("ajh")); @@ -246,10 +238,8 @@ public void testExtMappingOff1() { public void testExtMappingOff2() { extensionTunnelOff(); createGet(EFFECTED); - this.accLanguages - .add(new Preference<>(Language.valueOf("ajh"))); - this.accMediaTypes.add(new Preference<>( - MediaType.APPLICATION_STUFFIT)); + this.accLanguages.add(new Preference<>(Language.valueOf("ajh"))); + this.accMediaTypes.add(new Preference<>(MediaType.APPLICATION_STUFFIT)); filter(); assertEquals(EFFECTED, this.request.getResourceRef().toString()); assertLanguages(Language.valueOf("ajh")); @@ -349,8 +339,7 @@ public void testMethodTunnelingViaHeader() { tunnelFilter.getTunnelService().setMethodTunnel(true); Map attributesHeader = new HashMap<>(); Series

headers = new Series<>(Header.class); - headers.add(HeaderConstants.HEADER_X_HTTP_METHOD_OVERRIDE, - Method.GET.getName()); + headers.add(HeaderConstants.HEADER_X_HTTP_METHOD_OVERRIDE, Method.GET.getName()); headers.add(HeaderConstants.HEADER_X_FORWARDED_FOR, "TEST"); attributesHeader.put(HeaderConstants.ATTRIBUTE_HEADERS, headers); @@ -364,20 +353,17 @@ public void testMethodTunnelingViaHeader() { assertMethod(Method.POST); createPost(UNEFFECTED); - tunnelFilter.getTunnelService().setMethodHeader( - HeaderConstants.HEADER_X_FORWARDED_FOR); + tunnelFilter.getTunnelService().setMethodHeader(HeaderConstants.HEADER_X_FORWARDED_FOR); this.request.setAttributes(attributesHeader); filter(); assertNotSameMethod(Method.PUT); createPost(UNEFFECTED); - tunnelFilter.getTunnelService().setMethodHeader( - HeaderConstants.HEADER_X_FORWARDED_FOR); + tunnelFilter.getTunnelService().setMethodHeader(HeaderConstants.HEADER_X_FORWARDED_FOR); tunnelFilter.getTunnelService().setHeadersTunnel(false); this.request.setAttributes(attributesHeader); filter(); assertMethod(Method.POST); - } @Test @@ -401,22 +387,24 @@ public void testMethodTunnelingViaUserAgent() { tunnelFilter.getTunnelService().setUserAgentTunnel(true); createGet(UNEFFECTED); - this.accMediaTypes.add(new Preference<>( - MediaType.APPLICATION_ZIP)); + this.accMediaTypes.add(new Preference<>(MediaType.APPLICATION_ZIP)); filter(); assertEquals(UNEFFECTED, this.request.getResourceRef().toString()); assertMediaTypes(MediaType.APPLICATION_ZIP); assertCharSets(); assertEncodings(); - this.userAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)"; + this.userAgent = + "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)"; createGet(UNEFFECTED); - this.accMediaTypes.add(new Preference<>( - MediaType.APPLICATION_ZIP)); + this.accMediaTypes.add(new Preference<>(MediaType.APPLICATION_ZIP)); filter(); assertEquals(UNEFFECTED, this.request.getResourceRef().toString()); - assertMediaTypes(MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML, - MediaType.APPLICATION_XML, MediaType.ALL); + assertMediaTypes( + MediaType.TEXT_HTML, + MediaType.APPLICATION_XHTML, + MediaType.APPLICATION_XML, + MediaType.ALL); } @Test diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java index 765a2a5222..4ee2f908ed 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,7 +14,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.CountDownLatch; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -62,25 +60,24 @@ private boolean responseEntityExpected(Method method) { private void testCall(Context context, int count, Method method) throws Exception { final CountDownLatch latch = new CountDownLatch(count); - final Uniform responseHandler = (request, response) -> { - String item = request.getResourceRef().getQueryAsForm() - .getFirstValue("item"); - - try { - assertEquals(item, Integer.toString(response.getAge())); - if (responseEntityExpected(request.getMethod())) { - assertEquals(Status.SUCCESS_OK, response.getStatus()); - assertTrue(response.isEntityAvailable()); - assertNotNull(response.getEntityAsText()); - } else { - assertEquals(Status.SUCCESS_NO_CONTENT, - response.getStatus()); - assertFalse(response.isEntityAvailable()); - } - } finally { - latch.countDown(); - } - }; + final Uniform responseHandler = + (request, response) -> { + String item = request.getResourceRef().getQueryAsForm().getFirstValue("item"); + + try { + assertEquals(item, Integer.toString(response.getAge())); + if (responseEntityExpected(request.getMethod())) { + assertEquals(Status.SUCCESS_OK, response.getStatus()); + assertTrue(response.isEntityAvailable()); + assertNotNull(response.getEntityAsText()); + } else { + assertEquals(Status.SUCCESS_NO_CONTENT, response.getStatus()); + assertFalse(response.isEntityAvailable()); + } + } finally { + latch.countDown(); + } + }; Restlet client = context.getClientDispatcher(); for (int i = 0; i < count; ++i) { @@ -108,43 +105,51 @@ protected void setUpEach() throws Exception { originComponent = new Component(); // Create a new Restlet that will display some path information. - final Restlet trace = new Restlet(originComponent.getContext() - .createChildContext()) { - @Override - public void handle(Request request, Response response) { - // let's set the item number as age ;-) - response.setAge(Integer.parseInt(request.getResourceRef() - .getQueryAsForm().getFirstValue("item"))); - - if (responseEntityExpected(request.getMethod())) { - // Print the requested URI path - String message = "Resource URI: " - + request.getResourceRef() + '\n' - + "Base URI: " - + request.getResourceRef().getBaseRef() + '\n' - + "Remaining part: " - + request.getResourceRef().getRemainingPart() - + '\n' + "Method name: " + request.getMethod() - + '\n'; - - if (requestEntityExpected(request.getMethod())) { - message += request.getEntityAsText(); - request.getEntity().release(); + final Restlet trace = + new Restlet(originComponent.getContext().createChildContext()) { + @Override + public void handle(Request request, Response response) { + // let's set the item number as age ;-) + response.setAge( + Integer.parseInt( + request.getResourceRef() + .getQueryAsForm() + .getFirstValue("item"))); + + if (responseEntityExpected(request.getMethod())) { + // Print the requested URI path + String message = + "Resource URI: " + + request.getResourceRef() + + '\n' + + "Base URI: " + + request.getResourceRef().getBaseRef() + + '\n' + + "Remaining part: " + + request.getResourceRef().getRemainingPart() + + '\n' + + "Method name: " + + request.getMethod() + + '\n'; + + if (requestEntityExpected(request.getMethod())) { + message += request.getEntityAsText(); + request.getEntity().release(); + } + + response.setEntity( + new StringRepresentation(message, MediaType.TEXT_PLAIN)); + + response.setStatus(Status.SUCCESS_OK); + } else { + // consume entity + if (requestEntityExpected(request.getMethod())) + request.getEntityAsText(); + + response.setStatus(Status.SUCCESS_NO_CONTENT); + } } - - response.setEntity(new StringRepresentation(message, - MediaType.TEXT_PLAIN)); - - response.setStatus(Status.SUCCESS_OK); - } else { - // consume entity - if (requestEntityExpected(request.getMethod())) - request.getEntityAsText(); - - response.setStatus(Status.SUCCESS_NO_CONTENT); - } - } - }; + }; originComponent.getDefaultHost().attach("", trace); diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java index 9a40c8753b..cd15fad0f6 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static org.junit.jupiter.api.DynamicTest.dynamicTest; @@ -14,7 +13,6 @@ import java.util.List; import java.util.logging.Level; import java.util.stream.Stream; - import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.restlet.Application; @@ -25,8 +23,8 @@ import org.restlet.engine.adapter.HttpServerHelper; /** - * Base test case that will call an abstract method for several client/server - * connectors configurations. + * Base test case that will call an abstract method for several client/server connectors + * configurations. * * @author Kevin Conaway * @author Jerome Louvel @@ -64,19 +62,20 @@ protected void configureServer(final Server server) { protected abstract Application createApplication(); protected List listTestCases() { - return List.of( - new ConnectorsPair(HttpServer.JETTY_HTTP, HttpClient.JETTY)); + return List.of(new ConnectorsPair(HttpServer.JETTY_HTTP, HttpClient.JETTY)); } @TestFactory Stream testsFactory() { - return listTestCases().stream().map(testCase -> dynamicTest( - testCase.getTestLabel(), - () -> runTest(testCase.httpServer, testCase.httpClient))); + return listTestCases().stream() + .map( + testCase -> + dynamicTest( + testCase.getTestLabel(), + () -> runTest(testCase.httpServer, testCase.httpClient))); } - private void runTest(final HttpServer server, final HttpClient client) - throws Exception { + private void runTest(final HttpServer server, final HttpClient client) throws Exception { if (shouldDebug()) { System.setProperty("org.eclipse.jetty.LEVEL", "TRACE"); } @@ -147,5 +146,4 @@ public enum HttpClient { this.clientHelper = clientHelper; } } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java index 6300d90c83..1482cf3dd4 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java @@ -1,15 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; -import org.restlet.*; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.MediaType; import org.restlet.data.Method; import org.restlet.data.Protocol; @@ -19,15 +26,11 @@ import org.restlet.resource.ServerResource; import org.restlet.routing.Router; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * This tests the ability of the connectors to handle chunked encoding. * - * The test uses each connector to PUT an entity that will be sent chunked and - * also to receive a chunked response. + *

The test uses each connector to PUT an entity that will be sent chunked and also to receive a + * chunked response. */ public class ChunkedEncodingPutTestCase extends BaseConnectorsTestCase { private static final int LOOP_NUMBER = 20; @@ -55,10 +58,7 @@ public Restlet createInboundRoot() { }; } - /** - * Test resource that answers to PUT requests by sending back the received - * entity. - */ + /** Test resource that answers to PUT requests by sending back the received entity. */ public static class PutTestResource extends ServerResource { public PutTestResource() { getVariants().add(new Variant(MediaType.TEXT_PLAIN)); @@ -81,12 +81,26 @@ private void sendPut(int testIndex, final String uri, final int size) throws Exc System.out.println(response.getStatus()); } - assertNotNull(response.getEntity(), format("test #%d - size %d: response's entity is null", testIndex, size)); + assertNotNull( + response.getEntity(), + format("test #%d - size %d: response's entity is null", testIndex, size)); final String responseEntity = response.getEntity().getText(); - assertNotNull(responseEntity, format("test #%d - size %d: response's entity content is null", testIndex, size)); - assertEquals(size, responseEntity.length(), format("test #%d - size %d: length of response's entity is wrong", testIndex, size)); + assertNotNull( + responseEntity, + format( + "test #%d - size %d: response's entity content is null", + testIndex, size)); + assertEquals( + size, + responseEntity.length(), + format( + "test #%d - size %d: length of response's entity is wrong", + testIndex, size)); final String expectedResponseEntity = createChunkedRepresentation(size).getText(); - assertEquals(expectedResponseEntity, responseEntity, format("test #%d - size %d: response's entity is wrong", testIndex, size)); + assertEquals( + expectedResponseEntity, + responseEntity, + format("test #%d - size %d: response's entity is wrong", testIndex, size)); } finally { response.release(); client.stop(); @@ -96,8 +110,7 @@ private void sendPut(int testIndex, final String uri, final int size) throws Exc /** * Returns a StringRepresentation which size depends on the given argument. * - * @param size - * the size of the representation + * @param size the size of the representation * @return A DomRepresentation. */ private Representation createChunkedRepresentation(int size) { @@ -105,5 +118,4 @@ private Representation createChunkedRepresentation(int size) { rep.setSize(Representation.UNKNOWN_SIZE); // force chunked encoding return rep; } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java index 0521750dfb..248225c22c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.lang.String.format; @@ -15,7 +14,6 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; - import org.restlet.Application; import org.restlet.Client; import org.restlet.Message; @@ -38,8 +36,8 @@ /** * This tests the ability of the connectors to handle chunked encoding. * - * The test uses each connector to PUT an entity that will be sent chunked and - * also to receive a chunked response. + *

The test uses each connector to PUT an entity that will be sent chunked and also to receive a + * chunked response. */ public class ChunkedEncodingTestCase extends BaseConnectorsTestCase { @@ -73,7 +71,10 @@ private void sendGet(int testIndex, String uri) throws Exception { final Response response = client.handle(request); try { - assertEquals(Status.SUCCESS_OK, response.getStatus(), format("test #%d: response's status is wrong", testIndex)); + assertEquals( + Status.SUCCESS_OK, + response.getStatus(), + format("test #%d: response's status is wrong", testIndex)); assertXML(testIndex, response.getEntity()); } finally { response.release(); @@ -88,13 +89,15 @@ private void sendPut(int testIndex, String uri) throws Exception { try { assertChunkedHeader(response); - assertEquals(Status.SUCCESS_OK, response.getStatus(), format("test #%d: response's status is wrong", testIndex)); + assertEquals( + Status.SUCCESS_OK, + response.getStatus(), + format("test #%d: response's status is wrong", testIndex)); assertXML(testIndex, response.getEntity()); } finally { response.release(); client.stop(); } - } public static class PutTestResource extends ServerResource { @@ -119,27 +122,29 @@ public Representation put(Representation entity) { private void assertXML(int testIndex, Representation entity) { try { - String expected = ""; + String expected = + ""; String text = entity.getText(); - assertEquals(expected, text, format("test #%d: xml representation is wrong", testIndex)); + assertEquals( + expected, text, format("test #%d: xml representation is wrong", testIndex)); } catch (IOException ex) { fail(ex.getMessage()); } } private static void assertChunkedHeader(Message message) { - final Header transferEncoding = message.getHeaders() - .getFirst(HeaderConstants.HEADER_TRANSFER_ENCODING, true); + final Header transferEncoding = + message.getHeaders().getFirst(HeaderConstants.HEADER_TRANSFER_ENCODING, true); assertNotNull(transferEncoding); assertEquals("chunked", transferEncoding.getValue()); } private static Representation createTestXml() { - String xmlRepresentationAsString = ""; + String xmlRepresentationAsString = + ""; Representation rep = new StringRepresentation(xmlRepresentationAsString); rep.setSize(Representation.UNKNOWN_SIZE); // force chunked encoding return rep; } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ConnectorsPair.java b/org.restlet/src/test/java/org/restlet/engine/connector/ConnectorsPair.java index 0970bb5b2a..1312e79fcf 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ConnectorsPair.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ConnectorsPair.java @@ -1,3 +1,11 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ package org.restlet.engine.connector; import static java.lang.String.format; @@ -6,7 +14,9 @@ public class ConnectorsPair { final BaseConnectorsTestCase.HttpServer httpServer; final BaseConnectorsTestCase.HttpClient httpClient; - public ConnectorsPair(BaseConnectorsTestCase.HttpServer httpServer, BaseConnectorsTestCase.HttpClient httpClient) { + public ConnectorsPair( + BaseConnectorsTestCase.HttpServer httpServer, + BaseConnectorsTestCase.HttpClient httpClient) { this.httpServer = httpServer; this.httpClient = httpClient; } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java index fc12b16097..bcec722f35 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.lang.String.format; @@ -49,7 +48,8 @@ protected void doTest(final int serverPort) throws Exception { final Response response = client.handle(request); try { - assertEquals(Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); + assertEquals( + Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); assertChunkedHeader(response); assertEquals(text, response.getEntity().getText()); } finally { @@ -78,17 +78,16 @@ public GetChunkedTestResource() { @Override public Representation get(Variant variant) { - final Representation rep = new StringRepresentation( text, MediaType.APPLICATION_XML); + final Representation rep = new StringRepresentation(text, MediaType.APPLICATION_XML); rep.setSize(Representation.UNKNOWN_SIZE); // force chunked encoding return rep; } } private static void assertChunkedHeader(Message message) { - final Header transferEncoding = message.getHeaders() - .getFirst(HeaderConstants.HEADER_TRANSFER_ENCODING, true); + final Header transferEncoding = + message.getHeaders().getFirst(HeaderConstants.HEADER_TRANSFER_ENCODING, true); assertNotNull(transferEncoding); assertEquals("chunked", transferEncoding.getValue()); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java index 8de0c66801..8dcf022f40 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java @@ -1,15 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; -import org.restlet.*; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.Form; import org.restlet.data.Method; import org.restlet.data.Protocol; @@ -18,16 +27,9 @@ import org.restlet.resource.ServerResource; import org.restlet.routing.Router; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test that a simple get with query parameters works for all the connectors. - * + * * @author Kevin Conaway */ public class GetQueryParamTestCase extends BaseConnectorsTestCase { @@ -41,7 +43,8 @@ protected void doTest(final int serverPort) throws Exception { final Response response = client.handle(request); try { - assertEquals(Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); + assertEquals( + Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); assertEquals("{q1=a, q2=b}", response.getEntity().getText()); } finally { client.stop(); @@ -50,7 +53,7 @@ protected void doTest(final int serverPort) throws Exception { @Override protected Application createApplication() { - return new Application() { + return new Application() { @Override public Restlet createInboundRoot() { final Router router = new Router(getContext()); @@ -69,5 +72,4 @@ public String toString() { return sortedMap.toString(); } } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java index 7ca389199d..55ca0ada9e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java @@ -1,15 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; -import org.restlet.*; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.Method; import org.restlet.data.Protocol; import org.restlet.data.Status; @@ -17,9 +23,6 @@ import org.restlet.resource.ServerResource; import org.restlet.routing.Router; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test that a simple get works for all the connectors. * @@ -37,9 +40,7 @@ protected void doTest(final int serverPort) throws Exception { try { assertEquals( - Status.SUCCESS_OK, response.getStatus(), - response.getStatus().getDescription() - ); + Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); assertEquals("Hello world", response.getEntity().getText()); } finally { response.release(); @@ -65,5 +66,4 @@ public String toString() { return "Hello world"; } } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java index c38802a01d..24b3f87814 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.lang.String.format; @@ -25,7 +24,6 @@ import java.nio.file.Path; import java.util.List; import java.util.stream.Stream; - import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Nested; @@ -47,9 +45,7 @@ import org.restlet.engine.ssl.DefaultSslContextFactory; import org.restlet.util.Series; -/** - * Test the support of HTTP2 and HTTP3 transport protocols. - */ +/** Test the support of HTTP2 and HTTP3 transport protocols. */ public class HttpTransportProtocolsTestCase { protected static final String KEYSTORE_FILE_NAME = "dummy.p12"; @@ -59,8 +55,10 @@ public class HttpTransportProtocolsTestCase { @BeforeAll public static void setup() throws IOException { - Path keystorePath = Files.createTempFile("HttpTransportProtocolsTestCase", KEYSTORE_FILE_NAME); - final InputStream resourceAsStream = SslBaseConnectorsTestCase.class.getResourceAsStream(KEYSTORE_FILE_NAME); + Path keystorePath = + Files.createTempFile("HttpTransportProtocolsTestCase", KEYSTORE_FILE_NAME); + final InputStream resourceAsStream = + SslBaseConnectorsTestCase.class.getResourceAsStream(KEYSTORE_FILE_NAME); assert resourceAsStream != null; Files.copy(resourceAsStream, keystorePath, REPLACE_EXISTING); testKeystoreFile = keystorePath.toFile(); @@ -77,7 +75,8 @@ class HttpServerTestCase extends HttpTransportProtocolTest { public static void setup() { Engine.clearThreadLocalVariables(); Engine nre = Engine.register(false); - nre.getRegisteredServers().add(0, new org.restlet.engine.connector.HttpServerHelper(null)); + nre.getRegisteredServers() + .add(0, new org.restlet.engine.connector.HttpServerHelper(null)); nre.getRegisteredClients().add(0, new HttpClientHelper(null)); } @@ -105,9 +104,15 @@ List expectedProtocols() { } static Stream validTestCases() { - return Stream.of(Arguments.of(null, newJdkHttpClient(HttpClient.Version.HTTP_1_1)), // server default to - // HTTP1_1 - Arguments.of(null, newJdkHttpClient(HttpClient.Version.HTTP_2)), // server default to HTTP1_1 + return Stream.of( + Arguments.of( + null, + newJdkHttpClient(HttpClient.Version.HTTP_1_1)), // server default to + // HTTP1_1 + Arguments.of( + null, + newJdkHttpClient( + HttpClient.Version.HTTP_2)), // server default to HTTP1_1 Arguments.of("HTTP1_1", newJdkHttpClient(HttpClient.Version.HTTP_1_1)), Arguments.of("HTTP1_1", newJdkHttpClient(HttpClient.Version.HTTP_2)), Arguments.of("HTTP2", newJdkHttpClient(HttpClient.Version.HTTP_1_1)), @@ -122,7 +127,8 @@ static Stream validTestCases() { } static Stream invalidTestCases() { - return Stream.of(Arguments.of(null, newJettyHttpClient("HTTP2")), // server default to HTTP1_1 + return Stream.of( + Arguments.of(null, newJettyHttpClient("HTTP2")), // server default to HTTP1_1 Arguments.of("HTTP1_1", newJettyHttpClient("HTTP2"))); } } @@ -134,9 +140,9 @@ class HttpsServerTestCase extends HttpTransportProtocolTest { public static void setup() { Engine.clearThreadLocalVariables(); Engine nre = Engine.register(false); - nre.getRegisteredServers().add(0, new org.restlet.engine.connector.HttpsServerHelper(null)); + nre.getRegisteredServers() + .add(0, new org.restlet.engine.connector.HttpsServerHelper(null)); nre.getRegisteredClients().add(0, new HttpClientHelper(null)); - } @AfterAll @@ -152,9 +158,15 @@ List expectedProtocols() { } static Stream validTestCases() { - return Stream.of(Arguments.of(null, newJdkHttpsClient(HttpClient.Version.HTTP_1_1)), // server default to - // HTTP1_1 - Arguments.of(null, newJdkHttpsClient(HttpClient.Version.HTTP_2)), // server default to HTTP1_1 + return Stream.of( + Arguments.of( + null, + newJdkHttpsClient(HttpClient.Version.HTTP_1_1)), // server default to + // HTTP1_1 + Arguments.of( + null, + newJdkHttpsClient( + HttpClient.Version.HTTP_2)), // server default to HTTP1_1 Arguments.of("HTTP1_1", newJdkHttpsClient(HttpClient.Version.HTTP_1_1)), Arguments.of("HTTP1_1", newJdkHttpsClient(HttpClient.Version.HTTP_2)), Arguments.of("HTTP2", newJdkHttpsClient(HttpClient.Version.HTTP_2)), @@ -176,7 +188,8 @@ static Stream validTestCases() { } static Stream invalidTestCases() { - return Stream.of(Arguments.of("HTTP2", newJdkHttpsClient(HttpClient.Version.HTTP_1_1)), + return Stream.of( + Arguments.of("HTTP2", newJdkHttpsClient(HttpClient.Version.HTTP_1_1)), Arguments.of(null, newJettyHttpsClient("HTTP2")), // server default to http1 Arguments.of("HTTP1_1", newJettyHttpsClient("HTTP2")), Arguments.of("HTTP2", newJettyHttpsClient("HTTP1_1")), @@ -214,7 +227,8 @@ abstract static class HttpTransportProtocolTest { @ParameterizedTest(name = "server: {0} / client: {1}") @MethodSource("validTestCases") - public void clientCompliesWithServer(final String httpTransportProtocol, final TestHttpClient testHttpClient) + public void clientCompliesWithServer( + final String httpTransportProtocol, final TestHttpClient testHttpClient) throws Exception { final Server server = newServer(httpTransportProtocol); server.start(); @@ -224,36 +238,40 @@ public void clientCompliesWithServer(final String httpTransportProtocol, final T @ParameterizedTest(name = "server: {0} / client: {1}") @MethodSource("invalidTestCases") - public void clientDoesNotComplyWithServer(final String httpTransportProtocol, - final TestHttpClient testHttpClient) throws Exception { + public void clientDoesNotComplyWithServer( + final String httpTransportProtocol, final TestHttpClient testHttpClient) + throws Exception { final Server server = newServer(httpTransportProtocol); server.start(); - RuntimeException runtimeException = assertThrows(RuntimeException.class, - () -> testHttpClient.sendRequest(server.getActualPort())); + RuntimeException runtimeException = + assertThrows( + RuntimeException.class, + () -> testHttpClient.sendRequest(server.getActualPort())); assertEquals("Error while sending request", runtimeException.getMessage()); } @ParameterizedTest(name = "server: {0}") - @ValueSource(strings = { - "", "invalid", "http3" }) + @ValueSource(strings = {"", "invalid", "http3"}) public void invalidServerConfiguration(final String httpTransportProtocol) { final Server server = newServer(httpTransportProtocol); final Exception exception = assertThrows(IllegalArgumentException.class, server::start); assertEquals( - format("'%s' is not one of the supported values: %s", httpTransportProtocol, expectedProtocols()), + format( + "'%s' is not one of the supported values: %s", + httpTransportProtocol, expectedProtocols()), exception.getMessage()); } - } - private static final Restlet HELLO_WORLD_RESTLET = new Restlet() { - @Override - public void handle(Request request, Response response) { - response.setEntity("hello, world", MediaType.TEXT_PLAIN); - } - }; + private static final Restlet HELLO_WORLD_RESTLET = + new Restlet() { + @Override + public void handle(Request request, Response response) { + response.setEntity("hello, world", MediaType.TEXT_PLAIN); + } + }; interface TestHttpClient { String sendRequest(final int port) throws Exception; @@ -295,8 +313,11 @@ private JdkTestHttpClient(final Protocol protocol, final HttpClient.Version http sslContextFactory.setTrustStorePath(testKeystoreFile.getPath()); sslContextFactory.setTrustStorePassword(KEYSTORE_PASSWORD); sslContextFactory.setTrustStoreType(KEYSTORE_TYPE); - httpClient = HttpClient.newBuilder().sslContext(sslContextFactory.createSslContext()) - .version(httpVersion).build(); + httpClient = + HttpClient.newBuilder() + .sslContext(sslContextFactory.createSslContext()) + .version(httpVersion) + .build(); } catch (Exception e) { throw new RuntimeException(e); } @@ -305,8 +326,10 @@ private JdkTestHttpClient(final Protocol protocol, final HttpClient.Version http @Override public String sendRequest(int port) { - final HttpRequest requestGet = HttpRequest.newBuilder() - .uri(URI.create(protocol.getSchemeName() + "://localhost:" + port)).build(); + final HttpRequest requestGet = + HttpRequest.newBuilder() + .uri(URI.create(protocol.getSchemeName() + "://localhost:" + port)) + .build(); try { return httpClient.send(requestGet, HttpResponse.BodyHandlers.ofString()).body(); } catch (Exception e) { @@ -332,10 +355,14 @@ private JettyTestHttpClient(final Protocol protocol, final String httpClientTran @Override public String sendRequest(int port) { - final Client client = new Client(newClientContext(), List.of(protocol), - HttpClientHelper.class.getCanonicalName()); - - final Request request = new Request(Method.GET, protocol.getSchemeName() + "://localhost:" + port); + final Client client = + new Client( + newClientContext(), + List.of(protocol), + HttpClientHelper.class.getCanonicalName()); + + final Request request = + new Request(Method.GET, protocol.getSchemeName() + "://localhost:" + port); final Response response = client.handle(request); if (response.getStatus().isError()) { diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/Lock.java b/org.restlet/src/test/java/org/restlet/engine/connector/Lock.java index 9ae6e934f3..8be610e371 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/Lock.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/Lock.java @@ -1,12 +1,20 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ package org.restlet.engine.connector; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + import java.time.Duration; import java.time.Instant; import java.util.concurrent.CountDownLatch; import java.util.logging.Logger; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - public class Lock { private static final Logger LOGGER = Logger.getLogger("Lock"); @@ -34,5 +42,4 @@ public boolean awaitForUnlockingFor(final Duration waitTime) throws InterruptedE private static void log(final String message) { LOGGER.fine(Instant.now().toString() + " " + message); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java index c2c6a679ae..d5161ba71e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -16,7 +15,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; - import org.eclipse.jetty.client.StringRequestContent; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.MultiPart; @@ -28,7 +26,7 @@ /** * Test case for the {@link MultiPartRepresentation} class in multipart mode. - * + * * @author Jerome Louvel */ public class MultiPartRepresentationTestCase { @@ -36,22 +34,22 @@ public class MultiPartRepresentationTestCase { @Test public void testWriteFromParts() throws IOException { Path textFilePath = Files.createTempFile("multiPart", ""); - Files.write(textFilePath, "this is the content of the file" - .getBytes(StandardCharsets.UTF_8)); - MultiPart.PathPart filePart = new MultiPart.PathPart("icon", "text.txt", - HttpFields.EMPTY, textFilePath); + Files.write( + textFilePath, "this is the content of the file".getBytes(StandardCharsets.UTF_8)); + MultiPart.PathPart filePart = + new MultiPart.PathPart("icon", "text.txt", HttpFields.EMPTY, textFilePath); - MultiPart.ContentSourcePart contentSourcePart = new MultiPart.ContentSourcePart( - "field", null, HttpFields.EMPTY, - new StringRequestContent("foo")); + MultiPart.ContentSourcePart contentSourcePart = + new MultiPart.ContentSourcePart( + "field", null, HttpFields.EMPTY, new StringRequestContent("foo")); final String boundary = "-----------------------------1294919323195"; - MultiPartRepresentation rep = new MultiPartRepresentation( - contentSourcePart, filePart); + MultiPartRepresentation rep = new MultiPartRepresentation(contentSourcePart, filePart); rep.setBoundary(boundary); - final String expected = """ + final String expected = + """ --%s\r Content-Disposition: form-data; name="field"\r \r @@ -62,29 +60,28 @@ public void testWriteFromParts() throws IOException { this is the content of the file\r --%s--\r """ - .replace("%s", boundary); + .replace("%s", boundary); assertEquals(expected, rep.getText()); } - /** - * Tests the multipart content-type. - */ + /** Tests the multipart content-type. */ @Test public void testContentType() { - MultiPart.ContentSourcePart contentSourcePart = new MultiPart.ContentSourcePart( - "field", null, HttpFields.EMPTY, - new StringRequestContent("foo")); - MultiPartRepresentation rep = new MultiPartRepresentation( - "myInitialBoundary", contentSourcePart); + MultiPart.ContentSourcePart contentSourcePart = + new MultiPart.ContentSourcePart( + "field", null, HttpFields.EMPTY, new StringRequestContent("foo")); + MultiPartRepresentation rep = + new MultiPartRepresentation("myInitialBoundary", contentSourcePart); rep.setBoundary("myActualBoundary"); - assertEquals("multipart/form-data; boundary=myActualBoundary", - rep.getMediaType().toString()); + assertEquals( + "multipart/form-data; boundary=myActualBoundary", rep.getMediaType().toString()); } @Test public void testParseIntoParts() throws IOException { final String boundary = "-----------------------------1294919323195"; - final String multipartEntityContent = """ + final String multipartEntityContent = + """ --%s\r Content-Disposition: form-data; name="field"\r \r @@ -95,16 +92,13 @@ public void testParseIntoParts() throws IOException { this is the content of the file\r --%s--\r """ - .replace("%s", boundary); + .replace("%s", boundary); - StringRepresentation multipartEntity = new StringRepresentation( - multipartEntityContent); + StringRepresentation multipartEntity = new StringRepresentation(multipartEntityContent); multipartEntity.setMediaType( MediaType.valueOf("multipart/form-data; boundary=" + boundary)); - Path tempDir = Files - .createTempDirectory("multipartRepresentationTestCase"); - MultiPartRepresentation rep = new MultiPartRepresentation( - multipartEntity, tempDir); + Path tempDir = Files.createTempDirectory("multipartRepresentationTestCase"); + MultiPartRepresentation rep = new MultiPartRepresentation(multipartEntity, tempDir); Part part1 = rep.getParts().get(0); assertEquals("field", part1.getName()); @@ -116,9 +110,6 @@ public void testParseIntoParts() throws IOException { assertEquals("icon", part2.getName()); assertEquals("text.txt", part2.getFileName()); assertEquals(31, part2.getLength()); - assertEquals("this is the content of the file", - part2.getContentAsString(null)); - + assertEquals("this is the content of the file", part2.getContentAsString(null)); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java index b75f5d3ae2..fa63595684 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.lang.String.format; @@ -25,7 +24,7 @@ /** * Unit tests for POST and PUT requests. - * + * * @author Jerome Louvel */ public class PostPutTestCase extends BaseConnectorsTestCase { @@ -81,5 +80,4 @@ private void testCall(Client client, Method method, String uri) { response.release(); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java index 670e8c5755..17c4aecf06 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java @@ -1,15 +1,26 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; -import org.restlet.*; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.MediaType; import org.restlet.data.Method; import org.restlet.data.Protocol; @@ -20,18 +31,9 @@ import org.restlet.resource.ServerResource; import org.restlet.routing.Router; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; - -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - /** * Test that the client address is available for all the connectors - * + * * @author Kevin Conaway */ public class RemoteClientAddressTestCase extends BaseConnectorsTestCase { @@ -75,14 +77,16 @@ public Representation get(Variant variant) { boolean localAddress = false; try { - Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + Enumeration networkInterfaces = + NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); Enumeration inetAddresses = networkInterface.getInetAddresses(); while (inetAddresses.hasMoreElements()) { final InetAddress inetAddress = inetAddresses.nextElement(); - if (inetAddress.getHostAddress().equals( - getRequest().getClientInfo().getAddress())) { + if (inetAddress + .getHostAddress() + .equals(getRequest().getClientInfo().getAddress())) { localAddress = true; } } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java index f31ddbf75a..494c63e8eb 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.lang.String.format; @@ -20,7 +19,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.logging.Logger; - import org.restlet.Application; import org.restlet.Client; import org.restlet.Context; @@ -34,14 +32,13 @@ import org.restlet.data.Status; import org.restlet.util.Series; -/** - * This tests the ability of the server to accept a fixed number of incoming connections. - */ +/** This tests the ability of the server to accept a fixed number of incoming connections. */ public class ServerMaxConnectionsTestCase extends BaseConnectorsTestCase { - private static final Logger LOGGER = Logger.getLogger(ServerMaxConnectionsTestCase.class.getCanonicalName()); - private final static int CONNECTIONS_NUMBER = 1; - private final static int CONCURRENT_REQUESTS = 2; - private final static Duration SERVER_RESOURCE_FREEZE_DURATION = Duration.ofMillis(100); + private static final Logger LOGGER = + Logger.getLogger(ServerMaxConnectionsTestCase.class.getCanonicalName()); + private static final int CONNECTIONS_NUMBER = 1; + private static final int CONCURRENT_REQUESTS = 2; + private static final Duration SERVER_RESOURCE_FREEZE_DURATION = Duration.ofMillis(100); @Override protected void configureServer(final Server server) { @@ -49,7 +46,9 @@ protected void configureServer(final Server server) { final Series parameters = server.getContext().getParameters(); parameters.add("server.maxConnections", Integer.toString(CONNECTIONS_NUMBER)); - parameters.add("connector.acceptors", Integer.toString(CONCURRENT_REQUESTS)); // server can accept all requests + parameters.add( + "connector.acceptors", + Integer.toString(CONCURRENT_REQUESTS)); // server can accept all requests } @Override @@ -61,11 +60,12 @@ protected void doTest(final int serverPort) throws Exception { final List testResults = new ArrayList<>(); final Executor executor = Executors.newFixedThreadPool(CONCURRENT_REQUESTS); - final Runnable runnable = () -> { - testResults.add(sendGet(uri).isSuccess()); - countDownLatch.countDown(); - log("client countDownLatch.countDown done " + Thread.currentThread().getName()); - }; + final Runnable runnable = + () -> { + testResults.add(sendGet(uri).isSuccess()); + countDownLatch.countDown(); + log("client countDownLatch.countDown done " + Thread.currentThread().getName()); + }; for (int i = 0; i < CONCURRENT_REQUESTS; i++) { executor.execute(runnable); @@ -89,7 +89,8 @@ private Status sendGet(final String uri) { log("client send get " + Thread.currentThread().getName()); try { - final Request request = new Request(Method.GET, uri + "/" + Thread.currentThread().getName()); + final Request request = + new Request(Method.GET, uri + "/" + Thread.currentThread().getName()); final Client client = new Client(new Context(), Protocol.HTTP); final Response response = client.handle(request); log("client get sent " + Thread.currentThread().getName()); @@ -125,5 +126,4 @@ public void handle(Request request, Response response) { } }; } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java index 4806cf8331..a5bdcbe47f 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java @@ -1,27 +1,11 @@ /** - * Copyright 2005-2014 Restlet - * - * The contents of this file are subject to the terms of one of the following - * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can - * select the license that you prefer but you may not use this file except in - * compliance with one of these Licenses. - * - * You can obtain a copy of the Apache 2.0 license at - * http://www.opensource.org/licenses/apache-2.0 - * - * You can obtain a copy of the EPL 1.0 license at - * http://www.opensource.org/licenses/eclipse-1.0 - * - * See the Licenses for the specific language governing permissions and - * limitations under the Licenses. - * - * Alternatively, you can obtain a royalty free commercial license with less - * limitations, transferable or non-transferable, directly at - * http://restlet.com/products/restlet-framework - * - * Restlet is a registered trademark of Restlet S.A.S. + * Copyright 2005-2014 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.util.Collections.singletonList; @@ -32,9 +16,7 @@ import java.time.Instant; import java.util.logging.Level; import java.util.logging.Logger; - import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.restlet.Context; @@ -65,15 +47,14 @@ public void tearDown() { Engine.register(true); } - /** - * Validates that the server stops immediately when no requests are currently handled. - */ + /** Validates that the server stops immediately when no requests are currently handled. */ @Test public void whenServerIsNotHandlingRequestThenItStopsImmediately() throws Exception { // Given a server resource that takes 1 min to send a response final Restlet hangingRestlet = newHangingRestlet(Duration.ofMinutes(1)); // Given a server with a 3-seconds graceful shutdown - final Server server = startServerWithGracefulShutdown(Duration.ofSeconds(3), hangingRestlet); + final Server server = + startServerWithGracefulShutdown(Duration.ofSeconds(3), hangingRestlet); // When the server stops final Instant serverAskedToStopInstant = stopServer(server); @@ -85,7 +66,8 @@ public void whenServerIsNotHandlingRequestThenItStopsImmediately() throws Except /** * Validates that hanging requests are aborted immediately when graceful shutdown is OFF. * - * This is done by making a request froze, then stopping the server, and checking that it didn't wait. + *

This is done by making a request froze, then stopping the server, and checking that it + * didn't wait. */ @Test public void whenServerIsHandlingBlockingRequestThenItStopsImmediately() throws Exception { @@ -106,27 +88,33 @@ public void whenServerIsHandlingBlockingRequestThenItStopsImmediately() throws E log("before stopping server while request is pending"); final Instant serverAskedToStopInstant = stopServer(server); log("before client unlock"); - final boolean isClientResourceUnlocked = testClient.lock.awaitForUnlockingFor(Duration.ofSeconds(4)); + final boolean isClientResourceUnlocked = + testClient.lock.awaitForUnlockingFor(Duration.ofSeconds(4)); log("after client unlock"); // Then assertTrue(isResourceUnlocked, "The resource didn't receive the request"); assertTrue(isClientResourceUnlocked, "The client didn't achieve the request"); assertTrue(testClient.cr.getStatus().isError(), "The request should have ended in error"); - assertIntervalBetweenDatesEquals(Duration.ZERO, serverAskedToStopInstant, testClient.stoppedAt); + assertIntervalBetweenDatesEquals( + Duration.ZERO, serverAskedToStopInstant, testClient.stoppedAt); assertIntervalBetweenDatesEquals(Duration.ZERO, serverAskedToStopInstant, Instant.now()); } /** - * Validates that hanging requests are aborted after the server has waited for the timeout when graceful shutdown is ON. + * Validates that hanging requests are aborted after the server has waited for the timeout when + * graceful shutdown is ON. * - * This is done by making a request froze, then stopping the server, and checking that it waited the expected amount of time before shutting down. + *

This is done by making a request froze, then stopping the server, and checking that it + * waited the expected amount of time before shutting down. */ @Test - public void whenServerIsHandlingBlockingRequestThenItGracefullyWaitsFor1SecondBeforeStopping() throws Exception { + public void whenServerIsHandlingBlockingRequestThenItGracefullyWaitsFor1SecondBeforeStopping() + throws Exception { // Given a server resource that takes 1 min to send a response final Lock serverLock = new Lock("Server"); - final Restlet hangingRestlet = newHangingAndLockedRestlet(Duration.ofMinutes(1), serverLock); + final Restlet hangingRestlet = + newHangingAndLockedRestlet(Duration.ofMinutes(1), serverLock); // Given a server with a 1-second graceful shutdown final Duration shutdownTimeout = Duration.ofSeconds(1); @@ -137,25 +125,32 @@ public void whenServerIsHandlingBlockingRequestThenItGracefullyWaitsFor1SecondBe new Thread(hangingClient).start(); // When we stop the server while there is a pending request - final boolean isResourceUnlocked = serverLock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); + final boolean isResourceUnlocked = + serverLock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); log("Before ask server to stop"); final Instant serverAskedToStopInstant = stopServer(server); log("After ask server to stop"); - final boolean isClientResourceUnlocked = hangingClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); + final boolean isClientResourceUnlocked = + hangingClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); // Then assertTrue(isResourceUnlocked, "The resource didn't receive the request"); assertTrue(isClientResourceUnlocked, "The client didn't achieved the request"); - assertTrue(hangingClient.cr.getStatus().isError(), "The request should have ended in error"); + assertTrue( + hangingClient.cr.getStatus().isError(), "The request should have ended in error"); - assertIntervalBetweenDatesEquals(shutdownTimeout, serverAskedToStopInstant, hangingClient.stoppedAt); - assertIntervalBetweenDatesEquals(toJettyEffectiveTimeout(shutdownTimeout), serverAskedToStopInstant, Instant.now()); + assertIntervalBetweenDatesEquals( + shutdownTimeout, serverAskedToStopInstant, hangingClient.stoppedAt); + assertIntervalBetweenDatesEquals( + toJettyEffectiveTimeout(shutdownTimeout), serverAskedToStopInstant, Instant.now()); } /** - * Validates that incoming requests are refused after the server is stopping when graceful shutdown is ON. + * Validates that incoming requests are refused after the server is stopping when graceful + * shutdown is ON. * - * This is done by making a request froze, then stopping the server, and checking that a new request is not taken into account. + *

This is done by making a request froze, then stopping the server, and checking that a new + * request is not taken into account. */ @Test public void whenServerIsHandlingBlockingRequestThenItRefusesNewRequest() throws Exception { @@ -172,30 +167,43 @@ public void whenServerIsHandlingBlockingRequestThenItRefusesNewRequest() throws new Thread(firstTestClient).start(); // When - final boolean isResourceUnlocked = lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); + final boolean isResourceUnlocked = + lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); final Instant serverAskedToStopInstant = stopServer(server); final TestClient blockedTestClient = new TestClient(server); blockedTestClient.run(); - final boolean isFirstClientResourceUnlocked = firstTestClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); - final boolean isBlockedClientResourceUnlocked = blockedTestClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); + final boolean isFirstClientResourceUnlocked = + firstTestClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); + final boolean isBlockedClientResourceUnlocked = + blockedTestClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); // Then assertTrue(isResourceUnlocked, "The resource didn't receive the request"); assertTrue(isFirstClientResourceUnlocked, "The first client didn't achieved the request"); - assertTrue(isBlockedClientResourceUnlocked, "The \"blocked\" client didn't achieved the request"); - assertTrue(firstTestClient.cr.getStatus().isError(), "The request should have ended in error"); - assertIntervalBetweenDatesEquals(shutdownTimeout, serverAskedToStopInstant, firstTestClient.stoppedAt); - assertTrue(blockedTestClient.cr.getStatus().isConnectorError(), "Any new client is blocked and fails with a connection error"); - assertIntervalBetweenDatesEquals(toJettyEffectiveTimeout(shutdownTimeout), serverAskedToStopInstant, Instant.now()); + assertTrue( + isBlockedClientResourceUnlocked, + "The \"blocked\" client didn't achieved the request"); + assertTrue( + firstTestClient.cr.getStatus().isError(), "The request should have ended in error"); + assertIntervalBetweenDatesEquals( + shutdownTimeout, serverAskedToStopInstant, firstTestClient.stoppedAt); + assertTrue( + blockedTestClient.cr.getStatus().isConnectorError(), + "Any new client is blocked and fails with a connection error"); + assertIntervalBetweenDatesEquals( + toJettyEffectiveTimeout(shutdownTimeout), serverAskedToStopInstant, Instant.now()); } /** - * Validates that hanging requests for a short amount of time are handled before the server has waited for the timeout when graceful shutdown is ON. + * Validates that hanging requests for a short amount of time are handled before the server has + * waited for the timeout when graceful shutdown is ON. * - * This is done by making a request froze for a short amount of time, then stopping the server, and checking that the request has been handled. + *

This is done by making a request froze for a short amount of time, then stopping the + * server, and checking that the request has been handled. */ @Test - public void whenServerIsHandlingLongRequestThenRequestIsHandledCorrectlyBeforeStopping() throws Exception { + public void whenServerIsHandlingLongRequestThenRequestIsHandledCorrectlyBeforeStopping() + throws Exception { // Given a server resource that takes 1 sec to send a response final Lock lock = new Lock("Server"); Duration requestHangingTime = Duration.ofSeconds(1); @@ -210,56 +218,80 @@ public void whenServerIsHandlingLongRequestThenRequestIsHandledCorrectlyBeforeSt new Thread(testClient).start(); // When - final boolean isResourceUnlocked = lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); + final boolean isResourceUnlocked = + lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); final Instant serverAskedToStopInstant = stopServer(server); log("Client resource wait lock"); - final boolean isClientResourceUnlocked = testClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); + final boolean isClientResourceUnlocked = + testClient.lock.awaitForUnlockingFor(shutdownTimeout.multipliedBy(2)); log("Client resource unlocked"); // Then assertTrue(isResourceUnlocked, "The resource didn't receive the request"); assertTrue(isClientResourceUnlocked, "The client didn't achieve the request"); assertEquals(Status.SUCCESS_OK, testClient.cr.getStatus()); - assertIntervalBetweenDatesIsLessThan(requestHangingTime, serverAskedToStopInstant, Instant.now()); + assertIntervalBetweenDatesIsLessThan( + requestHangingTime, serverAskedToStopInstant, Instant.now()); assertEquals("hello, world", testClient.responseText); } - private static void assertIntervalBetweenDatesEquals(final Duration expectedDuration, final Instant firstInstant, final Instant secondInstant) { - final Duration dateDifference = Duration.between(firstInstant, secondInstant) - .abs(); + private static void assertIntervalBetweenDatesEquals( + final Duration expectedDuration, + final Instant firstInstant, + final Instant secondInstant) { + final Duration dateDifference = Duration.between(firstInstant, secondInstant).abs(); final Duration tolerance = Duration.ofMillis(200); // let's consider that +/- 200ms is fine - final boolean isDateDifferenceNearlyEqualToExpectedDuration = dateDifference.minus(expectedDuration) - .abs() - .minus(tolerance) - .isNegative(); - assertTrue(isDateDifferenceNearlyEqualToExpectedDuration, String.format("Expected delay: %d second(s) versus %d second(s)\n", expectedDuration.toMillis(), dateDifference.toMillis())); + final boolean isDateDifferenceNearlyEqualToExpectedDuration = + dateDifference.minus(expectedDuration).abs().minus(tolerance).isNegative(); + assertTrue( + isDateDifferenceNearlyEqualToExpectedDuration, + String.format( + "Expected delay: %d second(s) versus %d second(s)\n", + expectedDuration.toMillis(), dateDifference.toMillis())); } - private static void assertIntervalBetweenDatesIsLessThan(final Duration expectedDuration, final Instant firstInstant, final Instant secondInstant) { + private static void assertIntervalBetweenDatesIsLessThan( + final Duration expectedDuration, + final Instant firstInstant, + final Instant secondInstant) { final Duration dateDifference = Duration.between(firstInstant, secondInstant).abs(); final Duration tolerance = Duration.ofMillis(200); // let's consider that +/- 200ms is fine - final boolean dateDifferenceIsLessThanExpectedDuration = dateDifference.minus(expectedDuration) - .abs() - .minus(tolerance) - .isNegative(); - assertTrue(dateDifferenceIsLessThanExpectedDuration, String.format("difference between dates [%d second(s)] is higher than expected: %d second(s)\n", dateDifference.getSeconds(), expectedDuration.getSeconds())); + final boolean dateDifferenceIsLessThanExpectedDuration = + dateDifference.minus(expectedDuration).abs().minus(tolerance).isNegative(); + assertTrue( + dateDifferenceIsLessThanExpectedDuration, + String.format( + "difference between dates [%d second(s)] is higher than expected: %d second(s)\n", + dateDifference.getSeconds(), expectedDuration.getSeconds())); } private Server startServerWithoutGracefulShutdown(final Restlet restlet) throws Exception { return startServer(false, Duration.ZERO, restlet); } - private Server startServerWithGracefulShutdown(final Duration timeout, final Restlet restlet) throws Exception { + private Server startServerWithGracefulShutdown(final Duration timeout, final Restlet restlet) + throws Exception { return startServer(true, timeout, restlet); } - private Server startServer(final boolean graceful, final Duration timeout, final Restlet restlet) throws Exception { + private Server startServer( + final boolean graceful, final Duration timeout, final Restlet restlet) + throws Exception { - Engine.getInstance().getRegisteredServers().add(0, new HttpServerHelper(null)); // Creates a Jetty server helper manually + Engine.getInstance() + .getRegisteredServers() + .add(0, new HttpServerHelper(null)); // Creates a Jetty server helper manually // 0 port means it will be computed when the server starts - Server server = new Server(new Context(), singletonList(Protocol.HTTP), null, 0, restlet, HttpServerHelper.class.getCanonicalName()); + Server server = + new Server( + new Context(), + singletonList(Protocol.HTTP), + null, + 0, + restlet, + HttpServerHelper.class.getCanonicalName()); if (shouldDebug) { server.getContext().getParameters().add("tracing", "true"); @@ -269,21 +301,26 @@ private Server startServer(final boolean graceful, final Duration timeout, final if (graceful) { // Don't let the lowResource monitor mess with the current test - server.getContext().getParameters().add("lowResource.idleTimeout", Long.toString(timeout.toMillis() * 10)); + server.getContext() + .getParameters() + .add("lowResource.idleTimeout", Long.toString(timeout.toMillis() * 10)); } server.getContext().getParameters().add("shutdown.gracefully", Boolean.toString(graceful)); - server.getContext().getParameters().add("shutdown.timeout", Long.toString(timeout.toMillis())); + server.getContext() + .getParameters() + .add("shutdown.timeout", Long.toString(timeout.toMillis())); server.start(); - log( "Server started on port " + server.getEphemeralPort()); + log("Server started on port " + server.getEphemeralPort()); return server; } /** - * Creates a resource that hangs for a specific amount of time before answering and acknowledges incoming requests - * by unlocking the given lock. + * Creates a resource that hangs for a specific amount of time before answering and acknowledges + * incoming requests by unlocking the given lock. */ - private static Restlet newHangingAndLockedRestlet(final Duration requestHangingTime, final Lock lock) { + private static Restlet newHangingAndLockedRestlet( + final Duration requestHangingTime, final Lock lock) { return new Restlet() { @Override public void handle(final Request request, final Response response) { @@ -293,7 +330,8 @@ public void handle(final Request request, final Response response) { try { Thread.sleep(requestHangingTime.toMillis()); } catch (Exception e) { - // silently stops, especially when Jetty server will abruptly quit after time out + // silently stops, especially when Jetty server will abruptly quit after time + // out LOGGER.log(Level.FINE, "Restlet error", e); } log("Restlet woke up, answering"); @@ -302,9 +340,7 @@ public void handle(final Request request, final Response response) { }; } - /** - * Creates a resource that hangs for a specific amount of time before answering. - */ + /** Creates a resource that hangs for a specific amount of time before answering. */ private static Restlet newHangingRestlet(final Duration requestHangingTime) { return new Restlet() { @Override @@ -312,7 +348,8 @@ public void handle(final Request request, final Response response) { try { Thread.sleep(requestHangingTime.toMillis()); } catch (Exception e) { - // silently stops, especially when Jetty server will abruptly quit after time out + // silently stops, especially when Jetty server will abruptly quit after time + // out LOGGER.log(Level.FINE, "Restlet error", e); } LOGGER.log(Level.FINE, "Restlet woke up, answering"); @@ -352,7 +389,9 @@ private synchronized Instant stopServer(final Server server) { log("Server stopping"); Instant serverAskedToStopInstant = Instant.now(); try { - final HttpServerHelper serverHelper = (HttpServerHelper) server.getContext().getAttributes().get("org.restlet.engine.helper"); + final HttpServerHelper serverHelper = + (HttpServerHelper) + server.getContext().getAttributes().get("org.restlet.engine.helper"); serverHelper.getWrappedServer().stop(); log("Server stopped"); } catch (Exception e) { @@ -363,7 +402,8 @@ private synchronized Instant stopServer(final Server server) { } /** - * Returns the effective Jetty timeout since there is an extra half-timeout in the {@link org.eclipse.jetty.util.thread.QueuedThreadPool}. + * Returns the effective Jetty timeout since there is an extra half-timeout in the {@link + * org.eclipse.jetty.util.thread.QueuedThreadPool}. */ private Duration toJettyEffectiveTimeout(final Duration timeout) { return timeout.plusMillis(500); // FIXME: needs improvements @@ -376,5 +416,4 @@ private static void log(final String message) { private static void log(final String message, final Exception exception) { LOGGER.log(Level.INFO, Instant.now().toString() + " " + message, exception); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java index a369c415e4..9508ff7428 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import java.io.File; @@ -16,7 +15,6 @@ import java.io.OutputStream; import java.nio.file.Files; import java.util.List; - import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.restlet.Client; @@ -29,9 +27,9 @@ import org.restlet.util.Series; /** - * Base test case that will call an abstract method for several client/server - * connectors configurations. (Modified for SSL support.) - * + * Base test case that will call an abstract method for several client/server connectors + * configurations. (Modified for SSL support.) + * * @author Kevin Conaway * @author Bruno Harbulot * @author Jerome Louvel @@ -48,13 +46,12 @@ public abstract class SslBaseConnectorsTestCase extends BaseConnectorsTestCase { public static void globalSetUp() throws IOException { Engine.clearThreadLocalVariables(); Engine.register(); - testKeystoreFile = Files - .createTempFile("sslBaseConnectorsTest", KEYSTORE_FILE_NAME) - .toFile(); + testKeystoreFile = + Files.createTempFile("sslBaseConnectorsTest", KEYSTORE_FILE_NAME).toFile(); testKeystoreFile.delete(); - InputStream resourceAsStream = SslBaseConnectorsTestCase.class - .getResourceAsStream(KEYSTORE_FILE_NAME); + InputStream resourceAsStream = + SslBaseConnectorsTestCase.class.getResourceAsStream(KEYSTORE_FILE_NAME); if (resourceAsStream != null) { OutputStream outputStream = new FileOutputStream(testKeystoreFile); IoUtils.copy(resourceAsStream, outputStream); @@ -63,13 +60,11 @@ public static void globalSetUp() throws IOException { } else { throw new RuntimeException("Can't find the key store"); } - } @Override protected List listTestCases() { - return List.of(new ConnectorsPair(HttpServer.JETTY_HTTPS, - HttpClient.JETTY)); + return List.of(new ConnectorsPair(HttpServer.JETTY_HTTPS, HttpClient.JETTY)); } @Override @@ -120,5 +115,4 @@ protected static void tearDown() { Engine.clearThreadLocalVariables(); org.restlet.engine.Engine.register(); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java index ade864cfd0..3da8479d42 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.lang.String.format; @@ -49,8 +48,8 @@ protected void doTest(final int serverPort) throws Exception { final Request request = new Request(Method.GET, uri); final Response response = client.handle(request); - assertEquals(Status.SUCCESS_OK, response.getStatus(), - response.getStatus().getDescription()); + assertEquals( + Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); assertEquals("Hello world", response.getEntity().getText()); client.stop(); } @@ -86,9 +85,7 @@ public GetTestResource() { @Override public Representation get(Variant variant) { - return new StringRepresentation("Hello world", - MediaType.TEXT_PLAIN); + return new StringRepresentation("Hello world", MediaType.TEXT_PLAIN); } } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java index af38cacb29..e83c2eeff7 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.connector; import static java.lang.String.format; @@ -30,7 +29,7 @@ /** * Test that a simple get using SSL works for all the connectors. - * + * * @author Kevin Conaway * @author Bruno Harbulot */ @@ -45,7 +44,8 @@ protected void doTest(final int serverPort) throws Exception { final Request request = new Request(Method.GET, format("https://localhost:%d", serverPort)); final Response response = client.handle(request); - assertEquals(Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); + assertEquals( + Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); assertEquals("Hello world", response.getEntity().getText()); client.stop(); @@ -74,5 +74,4 @@ public Representation get(Variant variant) { return new StringRepresentation("Hello world", MediaType.TEXT_PLAIN); } } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java index cc06ff3476..0f3a913ce7 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java @@ -1,22 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; + /** * Test {@link ContentType} - * + * * @author Jerome Louvel */ public class ContentTypeTestCase { @@ -37,12 +36,11 @@ public void testParsing() { ContentType ct2 = new ContentType(h2); assertEquals(h1, ct1.getMediaType().getName()); - assertEquals("my version 1.0", ct1.getMediaType().getParameters() - .getFirstValue("version")); + assertEquals("my version 1.0", ct1.getMediaType().getParameters().getFirstValue("version")); assertEquals(h2, ct2.getMediaType().getName()); - assertEquals("'my%20version%201.0'", ct2.getMediaType().getParameters() - .getFirstValue("version")); + assertEquals( + "'my%20version%201.0'", + ct2.getMediaType().getParameters().getFirstValue("version")); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java index 31a2c0ce7a..e92d041f10 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java @@ -1,38 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.junit.jupiter.api.Test; -import org.restlet.data.Cookie; -import org.restlet.data.CookieSetting; -import org.restlet.engine.util.DateUtils; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.restlet.data.Cookie; +import org.restlet.data.CookieSetting; +import org.restlet.engine.util.DateUtils; /** * Unit tests for the Cookie related classes. - * + * * @author Jerome Louvel */ public class CookieReaderTestCase { /** * Test one cookie header. - * - * @param headerValue - * The cookie header value. + * + * @param headerValue The cookie header value. * @throws IOException */ private void testCookie(String headerValue) throws IOException { @@ -54,9 +51,8 @@ private void testCookie(String headerValue) throws IOException { /** * Test one cookie header. - * - * @param headerValue - * The cookie header value. + * + * @param headerValue The cookie header value. * @throws IOException */ private void testCookieValues(String headerValue) throws IOException { @@ -90,16 +86,14 @@ private void testCookieValues(String headerValue) throws IOException { /** * Test a cookie date value. - * - * @param dateValue - * The cookie date value. + * + * @param dateValue The cookie date value. */ private void testCookieDate(String dateValue) { final Date date = DateUtils.parse(dateValue, DateUtils.FORMAT_RFC_1036); // Rewrite the date - final String newDateValue = DateUtils.format(date, - DateUtils.FORMAT_RFC_1036.get(0)); + final String newDateValue = DateUtils.format(date, DateUtils.FORMAT_RFC_1036.get(0)); // Compare initial and new headers assertEquals(dateValue, newDateValue); @@ -107,16 +101,12 @@ private void testCookieDate(String dateValue) { /** * Test one set cookie header. - * - * @param headerValue - * The set cookie header value. - * @param compare - * Indicates if the new header should be compared with the old - * one. + * + * @param headerValue The set cookie header value. + * @param compare Indicates if the new header should be compared with the old one. * @throws IOException */ - private void testCookieSetting(String headerValue, boolean compare) - throws IOException { + private void testCookieSetting(String headerValue, boolean compare) throws IOException { CookieSettingReader cr = new CookieSettingReader(headerValue); CookieSetting cookie = cr.readValue(); @@ -125,15 +115,12 @@ private void testCookieSetting(String headerValue, boolean compare) // Compare initial and new headers if (compare) { - boolean result = newHeaderValue.toLowerCase().startsWith( - headerValue.toLowerCase()); + boolean result = newHeaderValue.toLowerCase().startsWith(headerValue.toLowerCase()); assertTrue(result); } } - /** - * Tests the parsing of cookies. - */ + /** Tests the parsing of cookies. */ @Test public void testParsing() throws IOException { // Netscape specification @@ -151,38 +138,36 @@ public void testParsing() throws IOException { // RFC 2109 testCookie("$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\""); - testCookie("$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""); - testCookie("$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; Shipping=\"FedEx\"; $Path=\"/acme\""); - testCookie("$Version=\"1\"; Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""); - + testCookie( + "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""); + testCookie( + "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; Shipping=\"FedEx\"; $Path=\"/acme\""); + testCookie( + "$Version=\"1\"; Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""); + + testCookieSetting("Customer=\"WILE_E_COYOTE\"; Version=\"1\"; Path=\"/acme\"", true); testCookieSetting( - "Customer=\"WILE_E_COYOTE\"; Version=\"1\"; Path=\"/acme\"", - true); + "Part_Number=\"Rocket_Launcher_0001\"; Version=\"1\"; Path=\"/acme\"", true); + testCookieSetting("Shipping=\"FedEx\"; Version=\"1\"; Path=\"/acme\"", true); testCookieSetting( - "Part_Number=\"Rocket_Launcher_0001\"; Version=\"1\"; Path=\"/acme\"", - true); - testCookieSetting("Shipping=\"FedEx\"; Version=\"1\"; Path=\"/acme\"", - true); + "Part_Number=\"Rocket_Launcher_0001\"; Version=\"1\"; Path=\"/acme\"", true); testCookieSetting( - "Part_Number=\"Rocket_Launcher_0001\"; Version=\"1\"; Path=\"/acme\"", - true); - testCookieSetting( - "Part_Number=\"Riding_Rocket_0023\"; Version=\"1\"; Path=\"/acme/ammo\"", - true); + "Part_Number=\"Riding_Rocket_0023\"; Version=\"1\"; Path=\"/acme/ammo\"", true); // Bug #49 testCookieSetting( "RMS_ADMETA_VISITOR_RMS=27756847%3A240105; expires=Thu, 02 Mar 2006 21:09:00 GMT; path=/; domain=.admeta.com", false); - testCookieValues("Cookie 1=One; Cookie 2=Two; Cookie 3=Three; Cookie 4=Four; Cookie 5=\"Five\"; Cookie 6=\"Six\""); + testCookieValues( + "Cookie 1=One; Cookie 2=Two; Cookie 3=Three; Cookie 4=Four; Cookie 5=\"Five\"; Cookie 6=\"Six\""); } @Test public void testParsingTooLongMaxAgeShouldBeCapedToIntegerMAX_VALUE() throws IOException { - CookieSettingReader cr = new CookieSettingReader( - "RMS_ADMETA_VISITOR_RMS=27756847%3A240105; max-age=31536000000; path=/; domain=.admeta.com"); + CookieSettingReader cr = + new CookieSettingReader( + "RMS_ADMETA_VISITOR_RMS=27756847%3A240105; max-age=31536000000; path=/; domain=.admeta.com"); CookieSetting cookie = cr.readValue(); assertEquals(cookie.getMaxAge(), Integer.MAX_VALUE); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java index 412852cf04..8ff00dd1c6 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java @@ -1,63 +1,54 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.junit.jupiter.api.Test; -import org.restlet.data.Disposition; - -import java.io.IOException; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.restlet.data.Disposition; + /** * Unit tests for the HTTP calls. - * + * * @author Kevin Conaway */ public class DispositionReaderTestCase { @Test public void testParseContentDisposition() throws IOException { - Disposition disposition = new DispositionReader( - "attachment; fileName=\"file.txt\"").readValue(); - assertEquals("file.txt", - disposition.getParameters().getFirstValue("fileName")); - - disposition = new DispositionReader("attachment; fileName=file.txt") - .readValue(); - assertEquals("file.txt", - disposition.getParameters().getFirstValue("fileName")); - - disposition = new DispositionReader( - "attachment; filename=\"file with space.txt\"").readValue(); - assertEquals("file with space.txt", disposition.getParameters() - .getFirstValue("filename")); - - disposition = new DispositionReader("attachment; filename=\"\"") - .readValue(); + Disposition disposition = + new DispositionReader("attachment; fileName=\"file.txt\"").readValue(); + assertEquals("file.txt", disposition.getParameters().getFirstValue("fileName")); + + disposition = new DispositionReader("attachment; fileName=file.txt").readValue(); + assertEquals("file.txt", disposition.getParameters().getFirstValue("fileName")); + + disposition = + new DispositionReader("attachment; filename=\"file with space.txt\"").readValue(); + assertEquals("file with space.txt", disposition.getParameters().getFirstValue("filename")); + + disposition = new DispositionReader("attachment; filename=\"\"").readValue(); assertEquals("", disposition.getParameters().getFirstValue("filename")); - disposition = new DispositionReader("attachment; filename=") - .readValue(); + disposition = new DispositionReader("attachment; filename=").readValue(); assertNull(disposition.getParameters().getFirstValue("filename")); disposition = new DispositionReader("attachment; filenam").readValue(); assertNull(disposition.getParameters().getFirstValue("filename")); - disposition = new DispositionReader( - "attachment; modification-date=\"Wed, 11 Nov 09 22:11:12 GMT\"") - .readValue(); - String str = disposition.getParameters().getFirstValue( - "modification-date"); + disposition = + new DispositionReader( + "attachment; modification-date=\"Wed, 11 Nov 09 22:11:12 GMT\"") + .readValue(); + String str = disposition.getParameters().getFirstValue("modification-date"); assertEquals("Wed, 11 Nov 09 22:11:12 GMT", str); - } } diff --git a/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java index 986baf3867..3f395e47e2 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java @@ -1,27 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.junit.jupiter.api.Test; -import org.restlet.data.Disposition; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.restlet.data.Disposition; /** * Unit tests for the HTTP calls. - * + * * @author Kevin Conaway */ public class DispositionWriterTestCase { @@ -34,18 +32,17 @@ public void testFormatContentDisposition() { disposition = new Disposition(Disposition.TYPE_ATTACHMENT); assertEquals("attachment", DispositionWriter.write(disposition)); disposition.setFilename(""); - assertEquals("attachment; filename=", - DispositionWriter.write(disposition)); + assertEquals("attachment; filename=", DispositionWriter.write(disposition)); disposition.setFilename("test.txt"); - assertEquals("attachment; filename=test.txt", - DispositionWriter.write(disposition)); + assertEquals("attachment; filename=test.txt", DispositionWriter.write(disposition)); disposition.setFilename("file with space.txt"); - assertEquals("attachment; filename=\"file with space.txt\"", + assertEquals( + "attachment; filename=\"file with space.txt\"", DispositionWriter.write(disposition)); disposition.setType(Disposition.TYPE_INLINE); - assertEquals("inline; filename=\"file with space.txt\"", - DispositionWriter.write(disposition)); + assertEquals( + "inline; filename=\"file with space.txt\"", DispositionWriter.write(disposition)); disposition.getParameters().clear(); Calendar c = new GregorianCalendar(Locale.ENGLISH); @@ -59,8 +56,8 @@ public void testFormatContentDisposition() { c.set(Calendar.MILLISECOND, 13); c.setTimeZone(TimeZone.getTimeZone("GMT")); disposition.setCreationDate(c.getTime()); - assertEquals("inline; creation-date=\"Wed, 11 Nov 09 10:11:12 GMT\"", + assertEquals( + "inline; creation-date=\"Wed, 11 Nov 09 10:11:12 GMT\"", DispositionWriter.write(disposition)); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java index c116b96517..919b2165a9 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java @@ -1,14 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.data.ClientInfo; import org.restlet.data.Encoding; @@ -16,22 +25,13 @@ import org.restlet.data.MediaType; import org.restlet.engine.util.DateUtils; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - /** * Unit tests for the header. * * @author Jerome Louvel */ public class HeaderTestCase { - /** - * Test the {@link HeaderReader#addValues(java.util.Collection)} method. - */ + /** Test the {@link HeaderReader#addValues(java.util.Collection)} method. */ @Test public void testAddValues() { List list = new ArrayList<>(); @@ -79,17 +79,14 @@ public void testAddValues() { @Test public void testInvalidDate() { final String headerValue = "-1"; - final Date date = DateUtils.parse(headerValue, - DateUtils.FORMAT_RFC_1123); + final Date date = DateUtils.parse(headerValue, DateUtils.FORMAT_RFC_1123); assertNull(date); final Date unmodifiableDate = DateUtils.unmodifiable(date); assertNull(unmodifiableDate); } - /** - * Tests the parsing. - */ + /** Tests the parsing. */ @Test public void testParsing() { String header1 = "Accept-Encoding,User-Agent"; @@ -97,7 +94,7 @@ public void testParsing() { final String header3 = "Accept-Encoding,\r\tUser-Agent"; final String header4 = "Accept-Encoding,\r User-Agent"; final String header5 = "Accept-Encoding, \r \t User-Agent"; - String[] values = new String[]{"Accept-Encoding", "User-Agent"}; + String[] values = new String[] {"Accept-Encoding", "User-Agent"}; testValues(header1, values); testValues(header2, values); testValues(header3, values); @@ -106,7 +103,7 @@ public void testParsing() { header1 = "Accept-Encoding, Accept-Language, Accept"; header2 = "Accept-Encoding,Accept-Language,Accept"; - values = new String[]{"Accept-Encoding", "Accept-Language", "Accept"}; + values = new String[] {"Accept-Encoding", "Accept-Language", "Accept"}; testValues(header1, values); testValues(header2, values); @@ -114,62 +111,44 @@ public void testParsing() { header1 = "gzip;q=1.0, identity;q=0.5 , *;q=0"; ClientInfo clientInfo = new ClientInfo(); PreferenceReader.addEncodings(header1, clientInfo); - assertEquals(clientInfo.getAcceptedEncodings().get(0).getMetadata(), - Encoding.GZIP); - assertEquals(clientInfo.getAcceptedEncodings().get(0).getQuality(), - 1.0F); - assertEquals(clientInfo.getAcceptedEncodings().get(1).getMetadata(), - Encoding.IDENTITY); - assertEquals(clientInfo.getAcceptedEncodings().get(1).getQuality(), - 0.5F); - assertEquals(clientInfo.getAcceptedEncodings().get(2).getMetadata(), - Encoding.ALL); + assertEquals(clientInfo.getAcceptedEncodings().get(0).getMetadata(), Encoding.GZIP); + assertEquals(clientInfo.getAcceptedEncodings().get(0).getQuality(), 1.0F); + assertEquals(clientInfo.getAcceptedEncodings().get(1).getMetadata(), Encoding.IDENTITY); + assertEquals(clientInfo.getAcceptedEncodings().get(1).getQuality(), 0.5F); + assertEquals(clientInfo.getAcceptedEncodings().get(2).getMetadata(), Encoding.ALL); assertEquals(clientInfo.getAcceptedEncodings().get(2).getQuality(), 0F); // Test the parsing of a "Accept" header header1 = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"; clientInfo = new ClientInfo(); PreferenceReader.addMediaTypes(header1, clientInfo); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getMetadata(), - MediaType.TEXT_HTML); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getQuality(), - 1.0F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(1).getMetadata(), - MediaType.IMAGE_GIF); - assertEquals(clientInfo.getAcceptedMediaTypes().get(1).getQuality(), - 1.0F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(2).getMetadata(), - MediaType.IMAGE_JPEG); - assertEquals(clientInfo.getAcceptedMediaTypes().get(2).getQuality(), - 1.0F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(3).getMetadata(), - new MediaType("*")); - assertEquals(clientInfo.getAcceptedMediaTypes().get(3).getQuality(), - 0.2F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(4).getMetadata(), - MediaType.ALL); - assertEquals(clientInfo.getAcceptedMediaTypes().get(4).getQuality(), - 0.2F); + assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getMetadata(), MediaType.TEXT_HTML); + assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getQuality(), 1.0F); + assertEquals(clientInfo.getAcceptedMediaTypes().get(1).getMetadata(), MediaType.IMAGE_GIF); + assertEquals(clientInfo.getAcceptedMediaTypes().get(1).getQuality(), 1.0F); + assertEquals(clientInfo.getAcceptedMediaTypes().get(2).getMetadata(), MediaType.IMAGE_JPEG); + assertEquals(clientInfo.getAcceptedMediaTypes().get(2).getQuality(), 1.0F); + assertEquals(clientInfo.getAcceptedMediaTypes().get(3).getMetadata(), new MediaType("*")); + assertEquals(clientInfo.getAcceptedMediaTypes().get(3).getQuality(), 0.2F); + assertEquals(clientInfo.getAcceptedMediaTypes().get(4).getMetadata(), MediaType.ALL); + assertEquals(clientInfo.getAcceptedMediaTypes().get(4).getQuality(), 0.2F); // Test a more complex header - header1 = "text/html, application/vnd.wap.xhtml+xml, " - + "application/xhtml+xml; profile=\"http://www.wapforum.org/xhtml\", " - + "image/gif, image/jpeg, image/pjpeg, audio/amr, */*"; + header1 = + "text/html, application/vnd.wap.xhtml+xml, " + + "application/xhtml+xml; profile=\"http://www.wapforum.org/xhtml\", " + + "image/gif, image/jpeg, image/pjpeg, audio/amr, */*"; clientInfo = new ClientInfo(); PreferenceReader.addMediaTypes(header1, clientInfo); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getMetadata(), - MediaType.TEXT_HTML); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getQuality(), - 1.0F); + assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getMetadata(), MediaType.TEXT_HTML); + assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getQuality(), 1.0F); } /** * Test that the parsing of a header returns the given array of values. * - * @param header - * The header value to parse. - * @param values - * The parsed values. + * @param header The header value to parse. + * @param values The parsed values. */ public void testValues(String header, String[] values) { HeaderReader hr = new HeaderReader<>(header); diff --git a/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java index 65bc8bc0ce..596d1267e2 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java @@ -1,29 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file are subject to the terms of one of the following - * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can - * select the license that you prefer but you may not use this file except in - * compliance with one of these Licenses. - * - * You can obtain a copy of the Apache 2.0 license at - * http://www.opensource.org/licenses/apache-2.0 - * - * You can obtain a copy of the EPL 1.0 license at - * http://www.opensource.org/licenses/eclipse-1.0 - * - * See the Licenses for the specific language governing permissions and - * limitations under the Licenses. - * - * Alternatively, you can obtain a royalty free commercial license with less - * limitations, transferable or non-transferable, directly at - * https://restlet.talend.com/ - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; import org.junit.jupiter.api.Test; import org.restlet.Request; import org.restlet.Response; @@ -31,71 +22,62 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -import java.util.ArrayList; -import java.util.Base64; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertEquals; - public class HeaderUtilsTestCase { - @Test - public void whenHeaderRetryAfterIsDecimalThenParsingIsStillFine() { - // Given a retry_after header that contains a decimal value - Header header = new Header(HeaderConstants.HEADER_RETRY_AFTER, "2.1"); - - // Given a response - Response response = new Response(new Request()); - - // When I copy the retry_after header to the response - HeaderUtils.copyResponseTransportHeaders(new Series<>(Header.class, Collections.singletonList(header)), - response); - - // Then the response contains a valid value for the retry_after header - assertNotNull(response.getRetryAfter()); - } - - @Test - public void whenHeaderRetryAfterIsAlphabeticalThenParsingFailsSilently() { - // Given a retry_after header that contains an alphabetical value - Header header = new Header(HeaderConstants.HEADER_RETRY_AFTER, "2.1a"); - - // Given a response - Response response = new Response(new Request()); - - // When I copy the retry_after header to the response - HeaderUtils.copyResponseTransportHeaders(new Series<>(Header.class, Collections.singletonList(header)), - response); - - // Then the response does not contain a retry_after header - assertNull(response.getRetryAfter()); - } - - @Test - public void testExtracting() { - ArrayList

headers = new ArrayList<>(); - String md5hash = "aaaaaaaaaaaaaaaa"; - // encodes to "YWFhYWFhYWFhYWFhYWFhYQ==", the "==" at the end is padding - String encodedWithPadding = Base64.getEncoder().encodeToString(md5hash.getBytes()); - String encodedNoPadding = encodedWithPadding.substring(0, 22); - - Header header = new Header(HeaderConstants.HEADER_CONTENT_MD5, encodedWithPadding); - headers.add(header); - - // extract Content-MD5 header with padded Base64 encoding, make sure it - // decodes to original hash - Representation rep = HeaderUtils.extractEntityHeaders(headers, null); - assertEquals(rep.getDigest().getAlgorithm(), - org.restlet.data.Digest.ALGORITHM_MD5); - assertEquals(new String(rep.getDigest().getValue()), md5hash); - - // extract header with UNpadded encoding, make sure it also decodes to - // original hash - header.setValue(encodedNoPadding); - rep = HeaderUtils.extractEntityHeaders(headers, null); - assertEquals(rep.getDigest().getAlgorithm(), - org.restlet.data.Digest.ALGORITHM_MD5); - assertEquals(new String(rep.getDigest().getValue()), md5hash); - } + @Test + public void whenHeaderRetryAfterIsDecimalThenParsingIsStillFine() { + // Given a retry_after header that contains a decimal value + Header header = new Header(HeaderConstants.HEADER_RETRY_AFTER, "2.1"); + + // Given a response + Response response = new Response(new Request()); + + // When I copy the retry_after header to the response + HeaderUtils.copyResponseTransportHeaders( + new Series<>(Header.class, Collections.singletonList(header)), response); + + // Then the response contains a valid value for the retry_after header + assertNotNull(response.getRetryAfter()); + } + + @Test + public void whenHeaderRetryAfterIsAlphabeticalThenParsingFailsSilently() { + // Given a retry_after header that contains an alphabetical value + Header header = new Header(HeaderConstants.HEADER_RETRY_AFTER, "2.1a"); + + // Given a response + Response response = new Response(new Request()); + + // When I copy the retry_after header to the response + HeaderUtils.copyResponseTransportHeaders( + new Series<>(Header.class, Collections.singletonList(header)), response); + + // Then the response does not contain a retry_after header + assertNull(response.getRetryAfter()); + } + + @Test + public void testExtracting() { + ArrayList
headers = new ArrayList<>(); + String md5hash = "aaaaaaaaaaaaaaaa"; + // encodes to "YWFhYWFhYWFhYWFhYWFhYQ==", the "==" at the end is padding + String encodedWithPadding = Base64.getEncoder().encodeToString(md5hash.getBytes()); + String encodedNoPadding = encodedWithPadding.substring(0, 22); + + Header header = new Header(HeaderConstants.HEADER_CONTENT_MD5, encodedWithPadding); + headers.add(header); + + // extract Content-MD5 header with padded Base64 encoding, make sure it + // decodes to original hash + Representation rep = HeaderUtils.extractEntityHeaders(headers, null); + assertEquals(rep.getDigest().getAlgorithm(), org.restlet.data.Digest.ALGORITHM_MD5); + assertEquals(new String(rep.getDigest().getValue()), md5hash); + + // extract header with UNpadded encoding, make sure it also decodes to + // original hash + header.setValue(encodedNoPadding); + rep = HeaderUtils.extractEntityHeaders(headers, null); + assertEquals(rep.getDigest().getAlgorithm(), org.restlet.data.Digest.ALGORITHM_MD5); + assertEquals(new String(rep.getDigest().getValue()), md5hash); + } } diff --git a/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java index 1adbb80b5b..d80c11081f 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java @@ -1,38 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; -import org.junit.jupiter.api.Test; -import org.restlet.data.MediaType; -import org.restlet.data.Preference; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.ArrayList; import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.data.Preference; /** * Unit tests for the Preference related classes. - * + * * @author Jerome Louvel */ public class PreferenceReaderTestCase { /** * Tests the parsing of a single preference header. - * - * @param headerValue - * The preference header. + * + * @param headerValue The preference header. */ private void testMediaType(String headerValue, boolean testEquals) { - PreferenceReader pr = new PreferenceReader<>( - PreferenceReader.TYPE_MEDIA_TYPE, headerValue); + PreferenceReader pr = + new PreferenceReader<>(PreferenceReader.TYPE_MEDIA_TYPE, headerValue); List> prefs = new ArrayList<>(); pr.addValues(prefs); @@ -40,8 +37,7 @@ private void testMediaType(String headerValue, boolean testEquals) { String newHeaderValue = PreferenceWriter.write(prefs); // Reread and rewrite the header (prevent formatting issues) - pr = new PreferenceReader<>(PreferenceReader.TYPE_MEDIA_TYPE, - headerValue); + pr = new PreferenceReader<>(PreferenceReader.TYPE_MEDIA_TYPE, headerValue); prefs = new ArrayList<>(); pr.addValues(prefs); String newHeaderValue2 = PreferenceWriter.write(prefs); @@ -52,9 +48,7 @@ private void testMediaType(String headerValue, boolean testEquals) { } } - /** - * Tests the preferences parsing. - */ + /** Tests the preferences parsing. */ @Test public void testParsing() { testMediaType( diff --git a/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java index 6c9e8d28c6..42c54c9e37 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java @@ -1,21 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.header; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Unit tests for the header. * @@ -26,7 +25,8 @@ public class RangeReaderTestCase { @Test public void testUpdateRangeFirst9Bytes() { final String contentRangeHeaderValue = "bytes 1-9/10"; - final Representation representation = new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); + final Representation representation = + new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); RangeReader.update(contentRangeHeaderValue, representation); @@ -39,7 +39,8 @@ public void testUpdateRangeFirst9Bytes() { @Test public void testUpdateRange0To100Bytes() { final String contentRangeHeaderValue = "bytes 0-100/10"; - final Representation representation = new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); + final Representation representation = + new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); RangeReader.update(contentRangeHeaderValue, representation); @@ -52,7 +53,8 @@ public void testUpdateRange0To100Bytes() { @Test public void testUpdateRange1To9Bytes() { final String contentRangeHeaderValue = "bytes 1-9/*"; - final Representation representation = new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); + final Representation representation = + new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); RangeReader.update(contentRangeHeaderValue, representation); @@ -61,5 +63,4 @@ public void testUpdateRange1To9Bytes() { assertEquals(1, representation.getRange().getIndex()); assertEquals(9, representation.getRange().getSize()); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java index 6e2348c8e1..3d0baab0d1 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java @@ -1,27 +1,29 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; import org.restlet.data.CharacterSet; import org.restlet.data.MediaType; import org.restlet.representation.OutputRepresentation; -import java.io.*; -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test case for the ByteUtils class. - * + * * @author Kevin Conaway */ public class IoUtilsTestCase { @@ -38,16 +40,16 @@ public void testGetStream() throws IOException { @Test public void testPipe() throws IOException { - final byte[] content = new byte[] { 1, 2, 3, -1, -2, -3, 4, 5, 6 }; + final byte[] content = new byte[] {1, 2, 3, -1, -2, -3, 4, 5, 6}; ByteArrayInputStream bais = new ByteArrayInputStream(content); - OutputRepresentation or = new OutputRepresentation( - MediaType.APPLICATION_OCTET_STREAM) { - @Override - public void write(OutputStream outputStream) throws IOException { - outputStream.write(content); - } - }; + OutputRepresentation or = + new OutputRepresentation(MediaType.APPLICATION_OCTET_STREAM) { + @Override + public void write(OutputStream outputStream) throws IOException { + outputStream.write(content); + } + }; InputStream is = or.getStream(); int result = 0; @@ -58,5 +60,4 @@ public void write(OutputStream outputStream) throws IOException { System.out.println(result); } } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java index 49b0849f54..1df41acd30 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java @@ -1,28 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; -import org.junit.jupiter.api.Test; -import org.restlet.data.CharacterSet; -import org.restlet.representation.InputRepresentation; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; - -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; +import org.restlet.data.CharacterSet; +import org.restlet.representation.InputRepresentation; /** - * Test the conversion from {@link Reader} to {@link InputStream} and the other - * way around. + * Test the conversion from {@link Reader} to {@link InputStream} and the other way around. * * @author Jerome Louvel */ @@ -45,5 +42,4 @@ public void testConversion() throws IOException { assertEquals(s, ir.getText()); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java index bedd17530d..dc14abc1b1 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java @@ -1,21 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.io.InputStream; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; /** * Unit tests for the HTTP KeepAlive. diff --git a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java index 3e99c03f49..da1f94c6c9 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java @@ -1,25 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.io; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * Unit tests for the HTTP KeepAlive. - * + * * @author Kevin Conaway */ public class UnclosableOutputStreamTestCase { @@ -33,8 +33,7 @@ public void close() { } @Override - public void write(int b) { - } + public void write(int b) {} } @Test @@ -56,13 +55,12 @@ public void testWrite() throws IOException { out.write('a'); assertEquals("a", stream.toString()); - out.write(new byte[] { 'b', 'c' }); + out.write(new byte[] {'b', 'c'}); assertEquals("abc", stream.toString()); - out.write(new byte[] { 'd', 'e', 'f', 'g' }, 0, 2); + out.write(new byte[] {'d', 'e', 'f', 'g'}, 0, 2); assertEquals("abcde", stream.toString()); out.close(); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java index 8916c33028..a4e92a4fed 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java @@ -1,19 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import org.restlet.resource.ServerResource; /** * Abstract {@link ServerResource} that implements several annotated interfaces. - * + * * @author Thierry Boileau */ public abstract class AbstractAnnotatedServerResource03 extends ServerResource @@ -26,5 +25,4 @@ public String accept() { public String asText() { return "asText"; } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java index 53779b8b24..8aea26ab6e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java @@ -1,20 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; /** - * Annotated interface that extends {@link AnnotatedInterface03_01} and - * {@link AnnotatedInterface03_02}. - * + * Annotated interface that extends {@link AnnotatedInterface03_01} and {@link + * AnnotatedInterface03_02}. + * * @author Thierry Boileau - * */ -public interface AnnotatedInterface03 extends AnnotatedInterface03_01, AnnotatedInterface03_02 { -} +public interface AnnotatedInterface03 extends AnnotatedInterface03_01, AnnotatedInterface03_02 {} diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java index 0e6132535c..e4c7b29f02 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java @@ -1,25 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import org.restlet.resource.Get; /** * Annotated interface that declares a single "Get" method. - * + * * @author Thierry Boileau - * */ public interface AnnotatedInterface03_01 { @Get String asText(); - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java index ec12c95fbe..18fbd1f4e5 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java @@ -1,25 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import org.restlet.resource.Post; /** * Annotated interface that declares a single "Post" method. - * + * * @author Thierry Boileau - * */ public interface AnnotatedInterface03_02 { @Post String accept(); - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java index 99c4581845..7acb0033e1 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java @@ -1,22 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.restlet.data.MediaType.*; +import static org.restlet.data.MediaType.APPLICATION_XML; +import static org.restlet.data.MediaType.TEXT_HTML; +import static org.restlet.data.MediaType.TEXT_PLAIN; import java.io.IOException; import java.util.Arrays; import java.util.stream.Stream; - import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.restlet.Application; @@ -36,17 +36,17 @@ */ public class AnnotatedResource09TestCase extends JettyConnectorTestCase { - public static final Method SI = new Method("SI", "What a method!", - "http://restlet.org", true, true); + public static final Method SI = + new Method("SI", "What a method!", "http://restlet.org", true, true); - public static final Method SNI = new Method("SNI", "What a method!", - "http://restlet.org", true, false); + public static final Method SNI = + new Method("SNI", "What a method!", "http://restlet.org", true, false); - public static final Method USI = new Method("USI", "What a method!", - "http://restlet.org", false, true); + public static final Method USI = + new Method("USI", "What a method!", "http://restlet.org", false, true); - public static final Method USNI = new Method("USNI", "What a method!", - "http://restlet.org", false, false); + public static final Method USNI = + new Method("USNI", "What a method!", "http://restlet.org", false, false); protected Application createApplication(final String path) { return new Application() { @@ -59,8 +59,8 @@ public Restlet createInboundRoot() { }; } - private final static String TEXT = "text"; - private final static Form FORM = new Form("name=value"); + private static final String TEXT = "text"; + private static final Form FORM = new Form("name=value"); static Stream methodsProvider() { return Stream.of(SI, SNI, USI, USNI); @@ -76,7 +76,8 @@ public void testCustomMethod(final Method method) throws IOException, ResourceEx assertTrue(response.getStatus().isSuccess()); releaseResponse(response); - // the annotated method of the ServerResource that generates HTML and does not require an entity is invoked + // the annotated method of the ServerResource that generates HTML and does not require an + // entity is invoked request = createRequest(method); request.getClientInfo().getAcceptedMediaTypes().add(new Preference<>(TEXT_HTML)); response = handle(request); @@ -87,7 +88,8 @@ public void testCustomMethod(final Method method) throws IOException, ResourceEx // Invokes the annotated method of the ServerResource that // - generates XML // - does not require an entity - // - which annotation does not handle input media type (@SIMethod(":xml") is preferred over @SIMethod("xml")) + // - which annotation does not handle input media type (@SIMethod(":xml") is preferred over + // @SIMethod("xml")) request = createRequest(method); request.getClientInfo().getAcceptedMediaTypes().add(new Preference<>(APPLICATION_XML)); response = handle(request); @@ -116,14 +118,17 @@ public void testCustomMethod(final Method method) throws IOException, ResourceEx assertEquals(TEXT_PLAIN, response.getEntity().getMediaType()); assertEquals(methodName + "-txt|text", response.getEntity().getText()); - // Without client's preference, one of the annotated method of the ServerResource that handles a Form is invoked + // Without client's preference, one of the annotated method of the ServerResource that + // handles a Form is invoked // (declarations order in class cannot be guaranteed) request = createRequest(method); request.setEntity(FORM.getWebRepresentation()); response = handle(request); - assertTrue(Arrays.asList(TEXT_PLAIN, TEXT_HTML).contains(response.getEntity().getMediaType())); - assertTrue(Arrays.asList(methodName + "-form:txt", methodName + "-form:html").contains(response.getEntity().getText())); + assertTrue( + Arrays.asList(TEXT_PLAIN, TEXT_HTML).contains(response.getEntity().getMediaType())); + assertTrue( + Arrays.asList(methodName + "-form:txt", methodName + "-form:html") + .contains(response.getEntity().getText())); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java index d359b07b4f..817209fdbd 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java @@ -1,18 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; - import org.junit.jupiter.api.Test; import org.restlet.Application; import org.restlet.Request; @@ -24,8 +22,8 @@ import org.restlet.routing.Router; /** - * Test annotated resource inheriting abstract super class that implements - * several annotated interfaces. + * Test annotated resource inheriting abstract super class that implements several annotated + * interfaces. * * @author Thierry Boileau */ @@ -62,5 +60,4 @@ public void test() throws IOException, ResourceException { assertEquals("accept", response.getEntity().getText()); response.getEntity().release(); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java index de66cc0779..fc04a8e294 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java @@ -1,18 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; - import org.junit.jupiter.api.Test; import org.restlet.Application; import org.restlet.Request; @@ -24,9 +22,9 @@ import org.restlet.routing.Router; /** - * Test annotated resource that reimplements of one the annotated method from - * its abstract super class that implements several annotated interfaces. - * + * Test annotated resource that reimplements of one the annotated method from its abstract super + * class that implements several annotated interfaces. + * * @author Thierry Boileau */ public class AnnotatedResource11TestCase extends JettyConnectorTestCase { @@ -44,7 +42,7 @@ public Restlet createInboundRoot() { /** * Test annotated methods. - * + * * @throws IOException * @throws ResourceException */ @@ -62,5 +60,4 @@ public void test() throws IOException, ResourceException { assertEquals("accept", response.getEntity().getText()); response.getEntity().release(); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java index d13463aa8b..6676cd7560 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java @@ -1,16 +1,14 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import java.util.logging.Level; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.restlet.Application; @@ -27,9 +25,8 @@ import org.restlet.representation.ObjectRepresentation; /** - * All test cases relying on a client and a server should inherit from this - * class. - * + * All test cases relying on a client and a server should inherit from this class. + * * @author Jerome Louvel */ public abstract class JettyConnectorTestCase { @@ -117,5 +114,4 @@ protected void tearDownServer() throws Exception { c.stop(); c = null; } - -} \ No newline at end of file +} diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java index 5907c408d7..83c3a7d33d 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import org.restlet.data.Form; diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java index 8057381e01..0d6ce92e91 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java @@ -1,20 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; /** * Resource that inherits annotated methods. - * + * * @author Thierry Boileau - * */ -public class MyResource10 extends AbstractAnnotatedServerResource03 { - -} +public class MyResource10 extends AbstractAnnotatedServerResource03 {} diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java index bf38331cc4..79daee5191 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java @@ -1,22 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import org.restlet.resource.Get; /** - * Resource that precises the media type of one of its inherited annotated - * methods. - * + * Resource that precises the media type of one of its inherited annotated methods. + * * @author Thierry Boileau - * */ public class MyResource11 extends AbstractAnnotatedServerResource03 { @@ -24,5 +21,6 @@ public class MyResource11 extends AbstractAnnotatedServerResource03 { @Override public String asText() { return "asText-txt"; - }; + } + ; } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java index 559ce70201..9d2aa7a16a 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import java.lang.annotation.Documented; @@ -14,7 +13,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - import org.restlet.engine.connector.Method; @Documented @@ -24,5 +22,4 @@ public @interface SIMethod { String value() default ""; - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java index 61c28765de..50fc9d0c50 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import java.lang.annotation.Documented; @@ -14,7 +13,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - import org.restlet.engine.connector.Method; @Documented @@ -24,5 +22,4 @@ public @interface SNIMethod { String value() default ""; - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java index e9edf364ef..f620f23f8b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import java.lang.annotation.Documented; @@ -14,7 +13,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - import org.restlet.engine.connector.Method; @Documented @@ -24,5 +22,4 @@ public @interface USIMethod { String value() default ""; - } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java index 68d5fc3f37..96b0f6d33b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.jetty.resource; import java.lang.annotation.Documented; @@ -14,7 +13,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - import org.restlet.engine.connector.Method; @Documented @@ -24,5 +22,4 @@ public @interface USNIMethod { String value() default ""; - } diff --git a/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java index 694606446d..cfca87ad49 100644 --- a/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java @@ -1,24 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.data.Method; import org.restlet.resource.Get; import org.restlet.resource.Put; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - /** * Test case for generic interfaces. * @@ -26,9 +24,7 @@ */ public class AnnotationUtilsTestCase { - public interface IChild extends IParent { - - } + public interface IChild extends IParent {} public interface IParent { @@ -37,7 +33,6 @@ public interface IParent { @Put void update(T generic); - } @Test diff --git a/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java index 17706c04e2..df14276d87 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java @@ -1,27 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.restlet.data.Reference; import org.restlet.resource.Directory; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - /** * Test case for the alphanum algorithm used by {@link Directory}. * @@ -39,11 +37,11 @@ private static List refs(String... uris) { return result; } - private static final List unsorted = refs("1", "2", "3", "1.0", "1.1", - "1.1.1", "2.0", "2.2", "2.2.2", "3.0", "3.3"); + private static final List unsorted = + refs("1", "2", "3", "1.0", "1.1", "1.1.1", "2.0", "2.2", "2.2.2", "3.0", "3.3"); - private static final List expected = refs("1", "1.0", "1.1", "1.1.1", - "2", "2.0", "2.2", "2.2.2", "3", "3.0", "3.3"); + private static final List expected = + refs("1", "1.0", "1.1", "1.1.1", "2", "2.0", "2.2", "2.2.2", "3", "3.0", "3.3"); @Test public void testBug() { @@ -53,15 +51,9 @@ public void testBug() { } @ParameterizedTest - @CsvSource({ - "Intel 5000X,Intel 5500", - "3,66", - "200,66", - "18,2" - }) + @CsvSource({"Intel 5000X,Intel 5500", "3,66", "200,66", "18,2"}) void testFirstIsLessThan(final String first, final String second) { AlphaNumericComparator anc = new AlphaNumericComparator(); assertTrue(anc.compare(first, second) < 0); } - } diff --git a/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java index a4966e7204..806d3eb2cb 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java @@ -1,26 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; /** * Test {@link DateUtils} - * + * * @author Jerome Louvel */ public class DateUtilsTestCase { @@ -43,61 +41,47 @@ public class DateUtilsTestCase { private final String DATE_RFC822_1 = "Fri, 12 Apr 85 23:20:50 GMT"; - /** - * Tests for dates in the RFC 822 format. - */ + /** Tests for dates in the RFC 822 format. */ @Test public void testRfc822() { Date date1 = DateUtils.parse(DATE_RFC822_1, DateUtils.FORMAT_RFC_822); - String dateFormat1 = DateUtils.format(date1, - DateUtils.FORMAT_RFC_822.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_822.get(0)); assertEquals(DATE_RFC822_1, dateFormat1); } - /** - * Tests for dates in the RFC 1123 format. - */ + /** Tests for dates in the RFC 1123 format. */ @Test public void testRfc1123() { Date date1 = DateUtils.parse(DATE_RFC1123_1, DateUtils.FORMAT_RFC_1123); - String dateFormat1 = DateUtils.format(date1, - DateUtils.FORMAT_RFC_1123.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_1123.get(0)); assertEquals(DATE_RFC1123_1, dateFormat1); } - /** - * Tests for dates in the RFC 1036 format. - */ + /** Tests for dates in the RFC 1036 format. */ @Test public void testRfc1036() { Date date1 = DateUtils.parse(DATE_RFC1036_1, DateUtils.FORMAT_RFC_1036); - String dateFormat1 = DateUtils.format(date1, - DateUtils.FORMAT_RFC_1036.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_1036.get(0)); assertEquals(DATE_RFC1036_1, dateFormat1); } - /** - * Tests for dates in the RFC 3339 format. - */ + /** Tests for dates in the RFC 3339 format. */ @Test public void testAsc() { Date date1 = DateUtils.parse(DATE_ASC_1, DateUtils.FORMAT_ASC_TIME); - String dateFormat1 = DateUtils.format(date1, - DateUtils.FORMAT_ASC_TIME.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_ASC_TIME.get(0)); assertEquals(DATE_ASC_1, dateFormat1); } - /** - * Tests for dates in the RFC 3339 format. - */ + /** Tests for dates in the RFC 3339 format. */ @Test public void testRfc3339() { Date date1 = DateUtils.parse(DATE_RFC3339_1, DateUtils.FORMAT_RFC_3339); @@ -106,16 +90,11 @@ public void testRfc3339() { Date date4 = DateUtils.parse(DATE_RFC3339_4, DateUtils.FORMAT_RFC_3339); Date date5 = DateUtils.parse(DATE_RFC3339_5, DateUtils.FORMAT_RFC_3339); - String dateFormat1 = DateUtils.format(date1, - DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat2 = DateUtils.format(date2, - DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat3 = DateUtils.format(date3, - DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat4 = DateUtils.format(date4, - DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat5 = DateUtils.format(date5, - DateUtils.FORMAT_RFC_3339.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_3339.get(0)); + String dateFormat2 = DateUtils.format(date2, DateUtils.FORMAT_RFC_3339.get(0)); + String dateFormat3 = DateUtils.format(date3, DateUtils.FORMAT_RFC_3339.get(0)); + String dateFormat4 = DateUtils.format(date4, DateUtils.FORMAT_RFC_3339.get(0)); + String dateFormat5 = DateUtils.format(date5, DateUtils.FORMAT_RFC_3339.get(0)); assertEquals(DATE_RFC3339_1, dateFormat1); assertEquals("1996-12-20T00:39:57Z", dateFormat2); @@ -135,12 +114,10 @@ public void unmodifiableDates() { assertTrue(now.after(yesterday)); assertTrue(now.after(DateUtils.unmodifiable(yesterday))); assertTrue(DateUtils.unmodifiable(now).after(yesterday)); - assertTrue(DateUtils.unmodifiable(now).after( - DateUtils.unmodifiable(yesterday))); + assertTrue(DateUtils.unmodifiable(now).after(DateUtils.unmodifiable(yesterday))); assertTrue(yesterday.before(DateUtils.unmodifiable(now))); - assertTrue(DateUtils.unmodifiable(yesterday).before( - DateUtils.unmodifiable(now))); + assertTrue(DateUtils.unmodifiable(yesterday).before(DateUtils.unmodifiable(now))); assertTrue(DateUtils.unmodifiable(yesterday).before(now)); } } diff --git a/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java index 7ce91b5e1a..e9a3d0c551 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java @@ -1,23 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.engine.util; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.params.provider.Arguments.arguments; - /** * Unit tests for the HTML encoding/decoding. * @@ -25,22 +23,24 @@ */ public class HtmlEncodingTestCase { - private static final String MIXED_ENCODED_STRING = "azertyéè@à&%ù€®®®&&&;&testest"; - private static final String ENCODED_STRING = "<test>azertyéè@à&%ù€®&#174;&reg;</test>&&&;&testest"; + private static final String MIXED_ENCODED_STRING = + "azertyéè@à&%ù€®®®&&&;&testest"; + private static final String ENCODED_STRING = + "<test>azertyéè@à&%ù€®&#174;&reg;</test>&&&;&testest"; private static final String DECODED_STRING = "azertyéè@à&%ù€®®®&&&;&testest"; static Stream htmlEscapeArgumentsProvider() { return Stream.of( arguments("", ""), arguments(null, null), - arguments(MIXED_ENCODED_STRING, ENCODED_STRING) - ); + arguments(MIXED_ENCODED_STRING, ENCODED_STRING)); } @ParameterizedTest @MethodSource("htmlEscapeArgumentsProvider") public void testEncoding(final String toEscape, final String expected) { - assertEquals(expected, StringUtils.htmlEscape(toEscape), "Error while escaping " + toEscape); + assertEquals( + expected, StringUtils.htmlEscape(toEscape), "Error while escaping " + toEscape); } static Stream htmlUnescapeArgumentsProvider() { @@ -49,14 +49,15 @@ static Stream htmlUnescapeArgumentsProvider() { arguments(null, null), arguments(MIXED_ENCODED_STRING, DECODED_STRING), arguments(ENCODED_STRING, MIXED_ENCODED_STRING), - arguments(DECODED_STRING, DECODED_STRING) - ); + arguments(DECODED_STRING, DECODED_STRING)); } @ParameterizedTest @MethodSource("htmlUnescapeArgumentsProvider") public void testDecoding(final String toUnescape, final String expected) { - assertEquals(expected, StringUtils.htmlUnescape(toUnescape), "Error while unescaping " + toUnescape); + assertEquals( + expected, + StringUtils.htmlUnescape(toUnescape), + "Error while unescaping " + toUnescape); } - } diff --git a/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java index 9eca7e2d2e..7cf20d0f1c 100644 --- a/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java @@ -1,21 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + /** * Unit test case for the {@link AppendableRepresentation} class. - * + * * @author Jerome Louvel */ public class AppendableRepresentationTestCase { @@ -27,5 +26,4 @@ public void testAppendable() throws Exception { ar.append("efgh"); assertEquals("abcdefgh", ar.getText()); } - } diff --git a/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java index a4a67f5268..d702633c34 100644 --- a/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java @@ -1,29 +1,34 @@ /** * Copyright 2005-2024 Qlik - *

- * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - *

+ *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.restlet.*; +import org.restlet.Application; +import org.restlet.Client; +import org.restlet.Component; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.Server; import org.restlet.data.Method; import org.restlet.data.Protocol; import org.restlet.data.Status; import org.restlet.engine.Engine; import org.restlet.routing.Router; -import java.io.IOException; -import java.security.NoSuchAlgorithmException; - -import static org.junit.jupiter.api.Assertions.*; - /** * Test {@link DigesterRepresentation}. * @@ -33,6 +38,7 @@ public class DigesterRepresentationTestCase { /** Component used for the tests. */ private Component component; + private int serverPort; @BeforeEach @@ -57,7 +63,8 @@ protected void tearDownEach() throws Exception { @Test public void checkDigestSentByClient() { Client client = new Client(Protocol.HTTP); - Request request = new Request(Method.PUT, "http://localhost:" + serverPort + "/checkRequestEntity"); + Request request = + new Request(Method.PUT, "http://localhost:" + serverPort + "/checkRequestEntity"); request.setEntity(getDigestedRepresentation("0123456789")); Response response = client.handle(request); @@ -67,39 +74,42 @@ public void checkDigestSentByClient() { @Test public void checkDigestSentByServer() { Client client = new Client(Protocol.HTTP); - Request request = new Request(Method.GET, "http://localhost:" + serverPort + "/checkResponseEntity"); + Request request = + new Request(Method.GET, "http://localhost:" + serverPort + "/checkResponseEntity"); Response response = client.handle(request); assertEquals(Status.SUCCESS_OK, response.getStatus()); assertTrue(checkRepresentation(response.getEntity())); } - /** - * Internal class used for test purpose. - * - */ + /** Internal class used for test purpose. */ private class TestDigestApplication extends Application { @Override public Restlet createInboundRoot() { Router router = new Router(getContext()); - router.attach("/checkRequestEntity", new Restlet() { - @Override - public void handle(Request request, Response response) { - Status responseStatus = checkRepresentation(request.getEntity()) - ? Status.SUCCESS_OK - : Status.CLIENT_ERROR_BAD_REQUEST; - response.setStatus(responseStatus); - } - }); - - router.attach("/checkResponseEntity", new Restlet() { - @Override - public void handle(Request request, Response response) { - response.setEntity(getDigestedRepresentation("9876543210")); - } - }); + router.attach( + "/checkRequestEntity", + new Restlet() { + @Override + public void handle(Request request, Response response) { + Status responseStatus = + checkRepresentation(request.getEntity()) + ? Status.SUCCESS_OK + : Status.CLIENT_ERROR_BAD_REQUEST; + response.setStatus(responseStatus); + } + }); + + router.attach( + "/checkResponseEntity", + new Restlet() { + @Override + public void handle(Request request, Response response) { + response.setEntity(getDigestedRepresentation("9876543210")); + } + }); return router; } } @@ -117,7 +127,8 @@ private boolean checkRepresentation(final Representation representation) { private DigesterRepresentation getDigestedRepresentation(final String string) { try { - DigesterRepresentation digester = new DigesterRepresentation(new StringRepresentation(string)); + DigesterRepresentation digester = + new DigesterRepresentation(new StringRepresentation(string)); // Consume first digester.exhaust(); // Set the digest @@ -128,5 +139,4 @@ private DigesterRepresentation getDigestedRepresentation(final String string) { throw new RuntimeException(e); } } - } diff --git a/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java index 6dbb6d3ca2..89418f0100 100644 --- a/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.representation; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,7 +14,6 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Path; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -81,19 +79,23 @@ public void testConstructors() { @Test public void testFileName() throws Exception { - Application application = new Application() { - @Override - public Restlet createInboundRoot() { - return new Restlet() { + Application application = + new Application() { @Override - public void handle(Request request, Response response) { - response.setEntity(new FileRepresentation(filePath.toFile(), MediaType.TEXT_PLAIN)); - response.getEntity().getDisposition() - .setType(Disposition.TYPE_ATTACHMENT); + public Restlet createInboundRoot() { + return new Restlet() { + @Override + public void handle(Request request, Response response) { + response.setEntity( + new FileRepresentation( + filePath.toFile(), MediaType.TEXT_PLAIN)); + response.getEntity() + .getDisposition() + .setType(Disposition.TYPE_ATTACHMENT); + } + }; } }; - } - }; component.getDefaultHost().attach(application); @@ -107,5 +109,4 @@ public void handle(Request request, Response response) { assertEquals(filePath.toFile().getName(), entity.getDisposition().getFilename()); client.stop(); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java index 3badf2983b..f2eff9fd04 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import org.junit.jupiter.api.AfterEach; @@ -42,5 +41,4 @@ protected void tearDownEach() { clientResource.release(); clientResource = null; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java index 98d758bd64..8c7c63e789 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** @@ -14,7 +13,8 @@ * * @author Jerome Louvel */ -public abstract class AbstractAnnotatedResourceWithFinderTestCase extends AbstractAnnotatedResourceTestCase { +public abstract class AbstractAnnotatedResourceWithFinderTestCase + extends AbstractAnnotatedResourceTestCase { @Override protected void configureClientResource(final ClientResource clientResource) { @@ -24,5 +24,4 @@ protected void configureClientResource(final ClientResource clientResource) { } abstract void configureFinder(final Finder finder); - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java b/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java index 4035a8512c..b2aed7fdf2 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java +++ b/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public abstract class AbstractGenericAnnotatedServerResource extends ServerResource { diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java index b039fdc0a9..55dc9db8a7 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java @@ -1,24 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.representation.ObjectRepresentation; import org.restlet.representation.StringRepresentation; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.*; - /** * Test the annotated resources, client and server sides. * @@ -53,9 +53,9 @@ public void testGet() throws IOException, ResourceException { ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED = true; String result = clientResource.get(MediaType.APPLICATION_JAVA_OBJECT_XML).getText(); - assertTrue(result - .startsWith("") - && result.contains("") + && result.contains(" + * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource02TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -37,5 +35,4 @@ public void testGet() throws IOException, ResourceException { assertEquals("", result.getText()); assertEquals(MediaType.TEXT_XML, result.getMediaType()); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java index 5316b8e9b7..dffe263607 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java @@ -1,19 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.restlet.data.Status; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test the annotated resources, client and server sides. * @@ -37,5 +36,4 @@ public void testGet() throws ResourceException { } assertEquals(Status.CLIENT_ERROR_NOT_FOUND, status); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java index 417cfa327c..e1b9d7a6a4 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java @@ -1,29 +1,27 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.restlet.data.MediaType; import org.restlet.representation.Representation; -import java.io.IOException; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource04TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -35,7 +33,8 @@ void configureFinder(Finder finder) { @ParameterizedTest @MethodSource("argumentsProvider") - public void testGet(final MediaType responseMediaType, final String responseBody) throws IOException, ResourceException { + public void testGet(final MediaType responseMediaType, final String responseBody) + throws IOException, ResourceException { Representation result = clientResource.get(responseMediaType); assertNotNull(result); assertEquals(responseBody, result.getText()); @@ -46,8 +45,6 @@ static Stream argumentsProvider() { return Stream.of( Arguments.arguments(MediaType.TEXT_HTML, "root"), Arguments.arguments(MediaType.APPLICATION_JSON, "[\"root\"]"), - Arguments.arguments(MediaType.APPLICATION_XML, "") - ); + Arguments.arguments(MediaType.APPLICATION_XML, "")); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java index 7e5cc4e9b6..e5f4428d64 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java @@ -1,26 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource05TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -42,5 +40,4 @@ public void testPost() throws IOException, ResourceException { assertEquals("", result.getText()); assertEquals(MediaType.APPLICATION_XML, result.getMediaType()); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java index 1c2052e051..3cc9ed0758 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java @@ -1,26 +1,24 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource06TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -32,8 +30,7 @@ void configureFinder(Finder finder) { @Test public void testPost() throws IOException, ResourceException { - Representation result = clientResource.post("[\"root\"]", - MediaType.APPLICATION_JSON); + Representation result = clientResource.post("[\"root\"]", MediaType.APPLICATION_JSON); assertNotNull(result); assertEquals("[\"root\"]2", result.getText()); assertEquals(MediaType.APPLICATION_JSON, result.getMediaType()); @@ -43,5 +40,4 @@ public void testPost() throws IOException, ResourceException { assertEquals("1", result.getText()); assertEquals(MediaType.APPLICATION_XML, result.getMediaType()); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java index 7bb0a87eca..fd5b8b791a 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java @@ -1,27 +1,25 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource07TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -33,8 +31,7 @@ void configureFinder(Finder finder) { @Test public void testPost() throws IOException, ResourceException { - Representation input = new StringRepresentation("[\"root\"]", - MediaType.APPLICATION_JSON); + Representation input = new StringRepresentation("[\"root\"]", MediaType.APPLICATION_JSON); Representation result = clientResource.post(input); assertNotNull(result); assertEquals("[\"root\"]1", result.getText()); @@ -46,5 +43,4 @@ public void testPost() throws IOException, ResourceException { assertEquals("2", result.getText()); assertEquals(MediaType.APPLICATION_XML, result.getMediaType()); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java index 876935cdda..18a68064c5 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java @@ -1,14 +1,22 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.restlet.data.MediaType.APPLICATION_JSON; +import static org.restlet.data.MediaType.APPLICATION_WWW_FORM; +import static org.restlet.data.MediaType.APPLICATION_XML; +import static org.restlet.data.MediaType.TEXT_HTML; + +import java.io.IOException; +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -16,16 +24,9 @@ import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import java.io.IOException; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.restlet.data.MediaType.*; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource08TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -37,7 +38,12 @@ void configureFinder(Finder finder) { @ParameterizedTest @MethodSource("argumentsProvider") - public void testPost(final MediaType requestBodyMediaType, final String requestBody, final MediaType responseBodyMediaType, final String responseBody) throws IOException, ResourceException { + public void testPost( + final MediaType requestBodyMediaType, + final String requestBody, + final MediaType responseBodyMediaType, + final String responseBody) + throws IOException, ResourceException { Representation input = new StringRepresentation(requestBody, requestBodyMediaType); Representation result = clientResource.post(input, responseBodyMediaType); assertNotNull(result); @@ -51,8 +57,6 @@ static Stream argumentsProvider() { Arguments.arguments(APPLICATION_XML, "root", APPLICATION_JSON, "root1"), Arguments.arguments(APPLICATION_JSON, "root", APPLICATION_XML, "root1"), Arguments.arguments(APPLICATION_WWW_FORM, "root", APPLICATION_WWW_FORM, "root2"), - Arguments.arguments(APPLICATION_WWW_FORM, "root", TEXT_HTML, "root2") - ); + Arguments.arguments(APPLICATION_WWW_FORM, "root", TEXT_HTML, "root2")); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java index 49382079dc..34c08bb163 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java @@ -1,19 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + import org.junit.jupiter.api.Test; import org.restlet.data.Form; -import static org.junit.jupiter.api.Assertions.*; - /** * Test the annotated resources, client and server sides. * @@ -49,5 +50,4 @@ public void testPutGet() { assertEquals("value1", myForm.getFirstValue("param1")); assertEquals("value2", myForm.getFirstValue("param2")); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java index 39d85d1fe1..71e8313b28 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java @@ -1,25 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.Serializable; import java.util.Date; - -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import org.junit.jupiter.api.Test; /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource13TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -67,14 +65,21 @@ public LightContact retrieveLight() { } public Contact retrieve() { - return new Contact("test@domain.com", "Scott", "Tiger", new Date(), - "test@perso.fr"); + return new Contact("test@domain.com", "Scott", "Tiger", new Date(), "test@perso.fr"); } public FullContact retrieveFull() { - return new FullContact("test@domain.com", "Scott", "Tiger", new Date(), - "test@perso.fr", "1 Main Street", "Restlet city, 0102", - "RESTland", "+123.456", "+789012"); + return new FullContact( + "test@domain.com", + "Scott", + "Tiger", + new Date(), + "test@perso.fr", + "1 Main Street", + "Restlet city, 0102", + "RESTland", + "+123.456", + "+789012"); } } @@ -84,8 +89,8 @@ public static class Contact extends LightContact implements Serializable { private String email2; - public Contact(String email, String firstName, String lastName, - Date birthDate, String email2) { + public Contact( + String email, String firstName, String lastName, Date birthDate, String email2) { super(email, firstName, lastName); this.birthDate = birthDate; email = email2; @@ -106,7 +111,6 @@ public void setBirthDate(Date birthDate) { public void setEmail2(String email) { this.email2 = email; } - } public static class FullContact extends Contact implements Serializable { @@ -117,9 +121,17 @@ public static class FullContact extends Contact implements Serializable { private String address3; - public FullContact(String email, String firstName, String lastName, - Date birthDate, String email2, String address1, String address2, - String address3, String fax, String phone) { + public FullContact( + String email, + String firstName, + String lastName, + Date birthDate, + String email2, + String address1, + String address2, + String address3, + String fax, + String phone) { super(email, firstName, lastName, birthDate, email2); this.address1 = address1; this.address2 = address2; @@ -171,7 +183,6 @@ public void setFax(String fax) { public void setPhone(String phone) { this.phone = phone; } - } public static class LightContact implements Serializable { @@ -212,7 +223,6 @@ public void setFirstName(String firstName) { public void setLastName(String lastName) { this.lastName = lastName; } - } public interface MyResource13 { @@ -225,7 +235,5 @@ public interface MyResource13 { @Get("?deep") FullContact retrieveFull(); - } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java index a8ac2f7251..9c27bb3c5e 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java @@ -1,14 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.restlet.data.MediaType.APPLICATION_JSON; +import static org.restlet.data.MediaType.APPLICATION_XML; +import static org.restlet.data.MediaType.TEXT_PLAIN; + +import java.io.IOException; +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -16,13 +23,6 @@ import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; -import java.io.IOException; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.restlet.data.MediaType.*; - /** * Test the annotated resources, client and server sides. * @@ -41,18 +41,23 @@ static Stream argumentsProvider() { Arguments.arguments(APPLICATION_XML, APPLICATION_JSON, "xml:json"), Arguments.arguments(APPLICATION_XML, APPLICATION_XML, "xml"), Arguments.arguments(TEXT_PLAIN, null, "*"), - Arguments.arguments(TEXT_PLAIN, TEXT_PLAIN, "*") - ); + Arguments.arguments(TEXT_PLAIN, TEXT_PLAIN, "*")); } @ParameterizedTest @MethodSource("argumentsProvider") - public void testQuery(final MediaType requestEntityMediaType, final MediaType responseMediaTypePreference, final String responseEntityAsText) throws IOException { - StringRepresentation requestEntity = new StringRepresentation("test", requestEntityMediaType); + public void testQuery( + final MediaType requestEntityMediaType, + final MediaType responseMediaTypePreference, + final String responseEntityAsText) + throws IOException { + StringRepresentation requestEntity = + new StringRepresentation("test", requestEntityMediaType); - Representation rep = responseMediaTypePreference == null - ? clientResource.put(requestEntity) - : clientResource.put(requestEntity, responseMediaTypePreference); + Representation rep = + responseMediaTypePreference == null + ? clientResource.put(requestEntity) + : clientResource.put(requestEntity, responseMediaTypePreference); assertNotNull(rep); if (responseMediaTypePreference != null) { diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java index 412afa1932..3ea1eddc50 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java @@ -1,21 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.Representation; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. * diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java index 181ae0493b..a733daf6d5 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java @@ -1,21 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.data.Status; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - /** * Test the annotated resources, client and server sides. * @@ -30,14 +29,17 @@ void configureFinder(Finder finder) { /** * The Java serialization (ObjectRepresentation) works well when the target class is correctly - * available at runtime. The usage of annotation like @Post on a generic method leads - * to the DefaultConverter to handle Object class and not Serializable interface. - * (cf AbstractGenericAnnotatedServerResource class). + * available at runtime. The usage of annotation like @Post on a generic method leads to the + * DefaultConverter to handle Object class and not Serializable interface. (cf + * AbstractGenericAnnotatedServerResource class). */ @Test public void shouldFailBecauseServerResourceAnnotationResidesOnGenericServerResource() { MyBean myBean = new MyBean("test", "description"); - ResourceException resourceException = assertThrows(ResourceException.class, () -> clientResource.post(myBean, MediaType.APPLICATION_JAVA_OBJECT)); + ResourceException resourceException = + assertThrows( + ResourceException.class, + () -> clientResource.post(myBean, MediaType.APPLICATION_JAVA_OBJECT)); assertEquals(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE, resourceException.getStatus()); } } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java index 84c7bc8d47..8d137c0462 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java @@ -1,22 +1,21 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.ObjectRepresentation; import org.restlet.representation.Representation; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. * @@ -32,8 +31,9 @@ void configureFinder(Finder finder) { @Test public void testQuery() { final MyBean myBean = new MyBean("test", "description"); - final Representation rep = clientResource.post(new ObjectRepresentation<>(myBean), - MediaType.APPLICATION_JAVA_OBJECT); + final Representation rep = + clientResource.post( + new ObjectRepresentation<>(myBean), MediaType.APPLICATION_JAVA_OBJECT); assertNotNull(rep); assertEquals(MediaType.APPLICATION_JAVA_OBJECT, rep.getMediaType()); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java index 446d7d71c6..627d60059b 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java @@ -1,28 +1,26 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.ObjectRepresentation; import org.restlet.representation.Representation; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource17TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -35,10 +33,13 @@ void configureFinder(Finder finder) { @Test public void testQuery() throws IOException, ClassNotFoundException { MyBean myBean = new MyBean("test", "description"); - Representation rep = clientResource.post(new ObjectRepresentation<>(myBean), MediaType.APPLICATION_JAVA_OBJECT); + Representation rep = + clientResource.post( + new ObjectRepresentation<>(myBean), MediaType.APPLICATION_JAVA_OBJECT); assertNotNull(rep); assertEquals(MediaType.APPLICATION_JAVA_OBJECT, rep.getMediaType()); - ObjectRepresentation jr = new ObjectRepresentation<>(rep, MyBean.class.getClassLoader()); + ObjectRepresentation jr = + new ObjectRepresentation<>(rep, MyBean.class.getClassLoader()); assertNotNull(jr.getObject()); Assertions.assertEquals("test", jr.getObject().getName()); } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java index cc71fef0bc..9f117ae056 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java @@ -1,28 +1,26 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.ObjectRepresentation; import org.restlet.representation.Representation; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource18TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @@ -35,10 +33,13 @@ void configureFinder(Finder finder) { @Test public void testQuery() throws IOException, ClassNotFoundException { MyBean myBean = new MyBean("test", "description"); - Representation rep = clientResource.post(new ObjectRepresentation<>(myBean), MediaType.APPLICATION_JAVA_OBJECT); + Representation rep = + clientResource.post( + new ObjectRepresentation<>(myBean), MediaType.APPLICATION_JAVA_OBJECT); assertNotNull(rep); assertEquals(MediaType.APPLICATION_JAVA_OBJECT, rep.getMediaType()); - ObjectRepresentation jr = new ObjectRepresentation<>(rep, MyBean.class.getClassLoader()); + ObjectRepresentation jr = + new ObjectRepresentation<>(rep, MyBean.class.getClassLoader()); assertNotNull(jr.getObject()); Assertions.assertEquals("test", jr.getObject().getName()); } diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java index fbcf72d6de..9a9e6b116d 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java @@ -1,24 +1,23 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + import org.junit.jupiter.api.Test; import org.restlet.Application; import org.restlet.data.MediaType; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - /** * Test the annotated resources, client and server sides. - * + * * @author Jerome Louvel */ public class AnnotatedResource20TestCase extends AbstractAnnotatedResourceTestCase { @@ -45,7 +44,9 @@ public void testGet() { @Test public void testGetAndSerializeException() { - MyException02 e = assertThrows(MyException02.class, () -> myResource.representAndSerializeException()); + MyException02 e = + assertThrows( + MyException02.class, () -> myResource.representAndSerializeException()); assertEquals("my custom error", e.getCustomProperty()); assertEquals(400, clientResource.getStatus().getCode()); } diff --git a/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java b/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java index db70890c28..897396eea6 100644 --- a/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import static java.io.File.createTempFile; @@ -32,7 +31,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; - import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -58,7 +56,7 @@ /** * Unit tests for the Directory class. - * + * * @author Thierry Boileau */ public class DirectoryTestCase { @@ -73,11 +71,9 @@ public class DirectoryTestCase { String baseFileUrlFrBis = this.webSiteURL.concat("fichier.fr.txt"); - String percentEncodedFileUrl = this.webSiteURL.concat(Reference - .encode("a new %file.txt.fr")); + String percentEncodedFileUrl = this.webSiteURL.concat(Reference.encode("a new %file.txt.fr")); - String percentEncodedFileUrlBis = this.webSiteURL - .concat("a+new%20%25file.txt.fr"); + String percentEncodedFileUrlBis = this.webSiteURL.concat("a+new%20%25file.txt.fr"); /** Tests the creation of directory with unknown parent directories. */ String testCreationDirectory = webSiteURL.concat("dir/does/not/exist"); @@ -86,8 +82,7 @@ public class DirectoryTestCase { String testCreationFile = webSiteURL.concat("file/does/not/exist.xml"); /** Tests the creation of text file with unknown parent directories. */ - String testCreationTextFile = webSiteURL - .concat("text/file/does/not/exist.txt"); + String testCreationTextFile = webSiteURL.concat("text/file/does/not/exist.txt"); private static Component clientComponent; private static MyApplication application; @@ -130,7 +125,8 @@ public void afterEach() { @Test public void testDirectoryWithExtensionTunnelAndIndexName() throws IOException { application.getTunnelService().setExtensionsTunnel(true); - this.testDir = Files.createTempDirectory("testDirectoryWithExtensionTunnelAndIndexName").toFile(); + this.testDir = + Files.createTempDirectory("testDirectoryWithExtensionTunnelAndIndexName").toFile(); application.setTestDirectory(testDir); // Test the directory Restlet with an index name @@ -140,7 +136,9 @@ public void testDirectoryWithExtensionTunnelAndIndexName() throws IOException { @Test public void testDirectoryWithExtensionTunnelAndWithoutIndexName() throws IOException { application.getTunnelService().setExtensionsTunnel(true); - this.testDir = Files.createTempDirectory("testDirectoryWithExtensionTunnelAndWithoutIndexName").toFile(); + this.testDir = + Files.createTempDirectory("testDirectoryWithExtensionTunnelAndWithoutIndexName") + .toFile(); application.setTestDirectory(testDir); // Test the directory Restlet with no index name @@ -150,7 +148,9 @@ public void testDirectoryWithExtensionTunnelAndWithoutIndexName() throws IOExcep @Test public void testDirectoryWithoutExtensionTunnelAndIndexName() throws IOException { application.getTunnelService().setExtensionsTunnel(false); - this.testDir = Files.createTempDirectory("testDirectoryWithoutExtensionTunnelAndIndexName").toFile(); + this.testDir = + Files.createTempDirectory("testDirectoryWithoutExtensionTunnelAndIndexName") + .toFile(); application.setTestDirectory(testDir); // Test the directory Restlet with an index name @@ -160,7 +160,9 @@ public void testDirectoryWithoutExtensionTunnelAndIndexName() throws IOException @Test public void testDirectoryWithoutExtensionTunnelAndWithoutIndexName() throws IOException { application.getTunnelService().setExtensionsTunnel(false); - this.testDir = Files.createTempDirectory("testDirectoryWithoutExtensionTunnelAndWithoutIndexName").toFile(); + this.testDir = + Files.createTempDirectory("testDirectoryWithoutExtensionTunnelAndWithoutIndexName") + .toFile(); application.setTestDirectory(testDir); // Test the directory Restlet with no index name @@ -178,25 +180,29 @@ public void testDirectoryDeeplyAccessible() throws IOException { application.getDirectory().setDeeplyAccessible(true); application.getDirectory().setListingAllowed(true); - Response response = new TestRequest(this.webSiteURL, "dir/subDir/") - .baseRef(this.webSiteURL) - .handle(GET); + Response response = + new TestRequest(this.webSiteURL, "dir/subDir/") + .baseRef(this.webSiteURL) + .handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); - response = new TestRequest(this.webSiteURL, "dir/subDir/", testFile.getName()) - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "dir/subDir/", testFile.getName()) + .baseRef(this.webSiteURL) + .handle(GET); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); application.getDirectory().setDeeplyAccessible(false); - response = new TestRequest(this.webSiteURL, "dir/subDir/") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "dir/subDir/") + .baseRef(this.webSiteURL) + .handle(GET); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - response = new TestRequest(this.webSiteURL, "dir/subDir/", testFile.getName()) - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "dir/subDir/", testFile.getName()) + .baseRef(this.webSiteURL) + .handle(GET); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); } @@ -221,83 +227,79 @@ public void testParentDirectoryInaccessible() throws IOException { Response response; - response = new TestRequest(this.webSiteURL, "file.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "file.txt").baseRef(this.webSiteURL).handle(GET); // assert no content as the file is empty assertEquals(Status.SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(this.webSiteURL, "%2e%2e/child%20dir/file.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "%2e%2e/child%20dir/file.txt") + .baseRef(this.webSiteURL) + .handle(GET); // assert no content as the file is empty assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(this.webSiteURL, "%2e%2e%2fchild%20dir/file.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "%2e%2e%2fchild%20dir/file.txt") + .baseRef(this.webSiteURL) + .handle(GET); // assert no content as the file is empty assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(this.webSiteURL, "../child%20dir/file.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "../child%20dir/file.txt") + .baseRef(this.webSiteURL) + .handle(GET); // assert no content as the file is empty assertEquals(Status.SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(this.webSiteURL, "..") - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(this.webSiteURL, "..").baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL, "../") - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(this.webSiteURL, "../").baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL, "../private.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "../private.txt") + .baseRef(this.webSiteURL) + .handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL, "%2e%2e") - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(this.webSiteURL, "%2e%2e").baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL, "%2e%2e/") - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(this.webSiteURL, "%2e%2e/").baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL, "%2e%2e%2f") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "%2e%2e%2f").baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL, "%2e%2e/private.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "%2e%2e/private.txt") + .baseRef(this.webSiteURL) + .handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL, "%2e%2e%2fprivate.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "%2e%2e%2fprivate.txt") + .baseRef(this.webSiteURL) + .handle(GET); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); } - /** - * Test content negotiation based on client preferences. - */ + /** Test content negotiation based on client preferences. */ @Test public void testUserPreferences() throws IOException { application.getTunnelService().setExtensionsTunnel(true); // Allow extensions tunneling - application.getMetadataService().setDefaultLanguage(Language.ENGLISH); // default language is not es or fr. + application + .getMetadataService() + .setDefaultLanguage(Language.ENGLISH); // default language is not es or fr. this.testDir = Files.createTempDirectory("testUserPreferences").toFile(); System.out.println(this.testDir.getAbsolutePath()); application.setTestDirectory(testDir); @@ -309,45 +311,35 @@ public void testUserPreferences() throws IOException { final String testEsTxtFileUrl = this.webSiteURL.concat("test.es.txt"); // Create two files - Response response = new TestRequest(testFrTxtFileUrl) - .baseRef(this.webSiteURL) - .entity("fr") - .handle(PUT); + Response response = + new TestRequest(testFrTxtFileUrl).baseRef(this.webSiteURL).entity("fr").handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - response = new TestRequest(testEsTxtFileUrl) - .baseRef(this.webSiteURL) - .entity("es") - .handle(PUT); + response = + new TestRequest(testEsTxtFileUrl).baseRef(this.webSiteURL).entity("es").handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - response = new TestRequest(testFileUrl) - .baseRef(this.webSiteURL) - .accept(SPANISH) - .handle(GET); + response = + new TestRequest(testFileUrl).baseRef(this.webSiteURL).accept(SPANISH).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("es", response.getEntityAsText()); - response = new TestRequest(testFileUrl) - .baseRef(this.webSiteURL) - .accept(FRENCH) - .handle(GET); + response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).accept(FRENCH).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("fr", response.getEntityAsText()); - response = new TestRequest(testTxtFileUrl) - .baseRef(this.webSiteURL) - .accept(SPANISH) - .handle(GET); + response = + new TestRequest(testTxtFileUrl) + .baseRef(this.webSiteURL) + .accept(SPANISH) + .handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("es", response.getEntityAsText()); - response = new TestRequest(testTxtFileUrl) - .baseRef(this.webSiteURL) - .accept(FRENCH) - .handle(GET); + response = + new TestRequest(testTxtFileUrl).baseRef(this.webSiteURL).accept(FRENCH).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("fr", response.getEntityAsText()); } @@ -364,8 +356,7 @@ private static class MyApplication extends Application { /** * Constructor. * - * @param testDirectory - * The test directory. + * @param testDirectory The test directory. */ public MyApplication(File testDirectory) { // Create a DirectoryHandler that manages a local Directory @@ -401,7 +392,6 @@ private void resetDirectoryToDefault() { this.directory.setModifiable(false); this.directory.setNegotiatingContent(true); } - } private static class TestRequest { @@ -448,7 +438,8 @@ protected TestRequest query(String name, String value) { protected Response handle(Method method) { final Response response = new Response(request); request.setMethod(method); - request.setOriginalRef(ReferenceUtils.getOriginalRef(request.getResourceRef(), request.getHeaders())); + request.setOriginalRef( + ReferenceUtils.getOriginalRef(request.getResourceRef(), request.getHeaders())); Application.getCurrent().handle(request, response); return response; } @@ -461,7 +452,8 @@ protected Response handle(Method method) { * @param indexName * @throws IOException */ - private void testDirectory(MyApplication application, Directory directory, String indexName) throws IOException { + private void testDirectory(MyApplication application, Directory directory, String indexName) + throws IOException { // Create a temporary file for the tests (the tests directory is not empty) final File testFile = File.createTempFile("test", ".txt", this.testDir); // Create a temporary directory @@ -474,230 +466,187 @@ private void testDirectory(MyApplication application, Directory directory, Strin directory.setIndexName(indexName); // Test 1a : directory does not allow to GET its content directory.setListingAllowed(false); - Response response = new TestRequest(this.webSiteURL) - .baseRef(this.webSiteURL) - .handle(GET); + Response response = new TestRequest(this.webSiteURL).baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); // Test 1b : directory allows to GET its content directory.setListingAllowed(true); - response = new TestRequest(this.webSiteURL) - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(this.webSiteURL).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); // should list all files in the directory (at least the temporary file generated before) assertTrue(response.getEntityAsText().contains(testFile.getName())); // Test 2a : tests the HEAD method - response = new TestRequest(testFileUrl) - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(testFileUrl) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); // Test 2b : try to GET a file that does not exist - response = new TestRequest(this.webSiteURL, "123456.txt") - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.webSiteURL, "123456.txt").baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); // Test 3a : try to put a new representation, but the directory is read only directory.setModifiable(false); - response = new TestRequest(this.baseFileUrl) - .baseRef(this.webSiteURL) - .entity("this is test 3a") - .handle(PUT); + response = + new TestRequest(this.baseFileUrl) + .baseRef(this.webSiteURL) + .entity("this is test 3a") + .handle(PUT); assertEquals(CLIENT_ERROR_METHOD_NOT_ALLOWED, response.getStatus()); // Test 3b : try to put a new representation, the directory is no more // read only directory.setModifiable(true); - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .entity("this is test 3b") - .entityLanguage(FRENCH) - .handle(PUT); + response = + new TestRequest(this.baseFileUrlFr) + .baseRef(this.webSiteURL) + .entity("this is test 3b") + .entityLanguage(FRENCH) + .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - response = new TestRequest(this.baseFileUrl) - .baseRef(this.webSiteURL) - .query("x", "y") - .handle(GET); + response = + new TestRequest(this.baseFileUrl) + .baseRef(this.webSiteURL) + .query("x", "y") + .handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 3b", response.getEntityAsText()); // Test 4 : Try to get the representation of the new file - response = new TestRequest(this.baseFileUrl) - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(this.baseFileUrl).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 3b", response.getEntityAsText()); // Test 5 : add a new representation of the same base file - response = new TestRequest(this.baseFileUrlEn) - .baseRef(this.webSiteURL) - .entity("this is a test - En") - .handle(PUT); + response = + new TestRequest(this.baseFileUrlEn) + .baseRef(this.webSiteURL) + .entity("this is a test - En") + .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - response = new TestRequest(this.baseFileUrl) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrl).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); - response = new TestRequest(this.baseFileUrlEn) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlEn).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); // Test 6a : delete a file - response = new TestRequest(testFileUrl) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(testFileUrl) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(HEAD); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); // Test 6b : delete a file that does not exist - response = new TestRequest(testFileUrl) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(DELETE); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); // Test 6c : delete a directory (without and with trailing slash) // Distinct behaviors if an index has been defined or not if (indexName.isEmpty()) { - response = new TestRequest(testDirectoryUrl) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(testDirectoryUrl).baseRef(this.webSiteURL).handle(DELETE); assertEquals(REDIRECTION_SEE_OTHER, response.getStatus()); - response = new TestRequest(response.getLocationRef().getIdentifier()) - .baseRef(response.getLocationRef().getIdentifier()) - .handle(DELETE); + response = + new TestRequest(response.getLocationRef().getIdentifier()) + .baseRef(response.getLocationRef().getIdentifier()) + .handle(DELETE); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); - response = new TestRequest(this.webSiteURL) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(this.webSiteURL).baseRef(this.webSiteURL).handle(DELETE); assertEquals(CLIENT_ERROR_FORBIDDEN, response.getStatus()); } else { // As there is no index file in the directory, the response must // return Status.CLIENT_ERROR_NOT_FOUND - response = new TestRequest(testDirectoryUrl, "/") - .baseRef(this.webSiteURL) - .handle(DELETE); + response = + new TestRequest(testDirectoryUrl, "/").baseRef(this.webSiteURL).handle(DELETE); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - response = new TestRequest(this.webSiteURL) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(this.webSiteURL).baseRef(this.webSiteURL).handle(DELETE); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); } // Test 7a : put one representation of the base file (in French language) - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .entity("message de test") - .handle(PUT); + response = + new TestRequest(this.baseFileUrlFr) + .baseRef(this.webSiteURL) + .entity("message de test") + .handle(PUT); assertTrue(response.getStatus().isSuccess()); // Test 7b : put another representation of the base file (in French // language) but the extensions are mixed // and there is no content negotiation directory.setNegotiatingContent(false); - response = new TestRequest(this.baseFileUrlFrBis) - .baseRef(this.webSiteURL) - .entity("message de test") - .handle(PUT); + response = + new TestRequest(this.baseFileUrlFrBis) + .baseRef(this.webSiteURL) + .entity("message de test") + .handle(PUT); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); // The 2 resources in French must be present (the same actually) - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(HEAD); assertEquals(Status.SUCCESS_OK, response.getStatus()); - response = new TestRequest(this.baseFileUrlFrBis) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(HEAD); assertEquals(Status.SUCCESS_OK, response.getStatus()); // Test 7c : delete the file representation of the resources with no content negotiation // The 2 French resources are deleted (there were only one) - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(HEAD); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - response = new TestRequest(this.baseFileUrlFrBis) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(HEAD); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - response = new TestRequest(this.baseFileUrlFrBis) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(DELETE); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); // Test 7d : put another representation of the base file (in French // language) but the extensions are mixed // and there is content negotiation directory.setNegotiatingContent(true); - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .entity("message de test") - .handle(PUT); + response = + new TestRequest(this.baseFileUrlFr) + .baseRef(this.webSiteURL) + .entity("message de test") + .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - response = new TestRequest(this.baseFileUrlFrBis) - .baseRef(this.webSiteURL) - .entity("message de test Bis") - .handle(PUT); + response = + new TestRequest(this.baseFileUrlFrBis) + .baseRef(this.webSiteURL) + .entity("message de test Bis") + .handle(PUT); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); // only one resource in French must be present - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); - response = new TestRequest(this.baseFileUrlFrBis) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); // TBOI : not sure this test is correct // Check if only one resource has been created directory.setNegotiatingContent(false); - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); // Test 7e : delete the file representation of the resources with content negotiation directory.setNegotiatingContent(true); - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(HEAD); if (application.getTunnelService().isExtensionsTunnel()) { assertEquals(SUCCESS_OK, response.getStatus()); } else { assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); } - response = new TestRequest(this.baseFileUrlFrBis) - .baseRef(this.webSiteURL) - .handle(HEAD); + response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(HEAD); if (application.getTunnelService().isExtensionsTunnel()) { assertEquals(SUCCESS_OK, response.getStatus()); } else { @@ -705,111 +654,112 @@ private void testDirectory(MyApplication application, Directory directory, Strin } // Test 8 : must delete the English representation - response = new TestRequest(this.baseFileUrlFr) - .baseRef(this.webSiteURL) - .handle(DELETE); - response = new TestRequest(this.baseFileUrlEn) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(DELETE); + response = new TestRequest(this.baseFileUrlEn).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); // Test 9a : put a new representation, the resource's URI contains // percent-encoded characters directory.setModifiable(true); - response = new TestRequest(this.percentEncodedFileUrl) - .baseRef(this.webSiteURL) - .entity("this is test 9a") - .entityLanguage(FRENCH) - .handle(PUT); + response = + new TestRequest(this.percentEncodedFileUrl) + .baseRef(this.webSiteURL) + .entity("this is test 9a") + .entityLanguage(FRENCH) + .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); // Test 9b : Try to get the representation of the new file - response = new TestRequest(this.percentEncodedFileUrl) - .baseRef(this.webSiteURL) - .handle(GET); + response = new TestRequest(this.percentEncodedFileUrl).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 9a", response.getEntityAsText()); // Test 9c : Try to get the representation of the new file with an // equivalent URI - response = new TestRequest(this.percentEncodedFileUrlBis) - .baseRef(this.webSiteURL) - .handle(GET); + response = + new TestRequest(this.percentEncodedFileUrlBis).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 9a", response.getEntityAsText()); // Test 9d : Try to delete the file - response = new TestRequest(this.percentEncodedFileUrl) - .baseRef(this.webSiteURL) - .handle(DELETE); + response = + new TestRequest(this.percentEncodedFileUrl).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); // Test 10a : Try to create a directory with an unknown hierarchy of // parent directories. - response = new TestRequest(this.testCreationDirectory) - .baseRef(this.webSiteURL) - .entity("useless entity") - .handle(PUT); + response = + new TestRequest(this.testCreationDirectory) + .baseRef(this.webSiteURL) + .entity("useless entity") + .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); // Test 10b : Try to create a directory (with the trailing "/") with an // unknown hierarchy of parent directories. - response = new TestRequest(this.testCreationDirectory, "/") - .baseRef(this.webSiteURL) - .entity("useless entity") - .handle(PUT); + response = + new TestRequest(this.testCreationDirectory, "/") + .baseRef(this.webSiteURL) + .entity("useless entity") + .handle(PUT); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); // Test 10c : Try to create a file with an unknown hierarchy of // parent directories. The name and the metadata of the provided entity // don't match - response = new TestRequest(testCreationFile) - .baseRef(this.webSiteURL) - .entity("file entity") - .handle(PUT); + response = + new TestRequest(testCreationFile) + .baseRef(this.webSiteURL) + .entity("file entity") + .handle(PUT); assertEquals(CLIENT_ERROR_BAD_REQUEST, response.getStatus()); // Test 10d : Try to create a file with an unknown hierarchy of // parent directories. The name and the metadata of the provided entity // match - response = new TestRequest(testCreationTextFile) - .baseRef(this.webSiteURL) - .entity("file entity") - .handle(PUT); + response = + new TestRequest(testCreationTextFile) + .baseRef(this.webSiteURL) + .entity("file entity") + .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); // Test 11 : redirection for Directory - response = new TestRequest(testDirectoryUrl) - .baseRef(this.webSiteURL) - .entity("file entity") - .handle(GET); + response = + new TestRequest(testDirectoryUrl) + .baseRef(this.webSiteURL) + .entity("file entity") + .handle(GET); assertEquals(REDIRECTION_SEE_OTHER, response.getStatus()); assertEquals("http://myapplication/try/", response.getLocationRef().toString()); // Test 12 : redirection for Directory with proxy forwarding - response = new TestRequest(testDirectoryUrl) - .baseRef(this.webSiteURL) - .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PROTO, "https")) - .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PORT, "123")) - .handle(GET); + response = + new TestRequest(testDirectoryUrl) + .baseRef(this.webSiteURL) + .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PROTO, "https")) + .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PORT, "123")) + .handle(GET); assertEquals(REDIRECTION_SEE_OTHER, response.getStatus()); assertEquals("https://myapplication:123/try/", response.getLocationRef().toString()); // Test 13 : creation of resource with proxy forwarding - response = new TestRequest(this.percentEncodedFileUrl) - .baseRef(this.webSiteURL) - .entity("this is test") - .handle(PUT); + response = + new TestRequest(this.percentEncodedFileUrl) + .baseRef(this.webSiteURL) + .entity("this is test") + .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - response = new TestRequest(this.percentEncodedFileUrl) - .baseRef(this.webSiteURL) - .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PROTO, "https")) - .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PORT, "123")) - .handle(GET); - assertEquals("https://myapplication:123/a%20new%20%25file.txt.fr", response.getEntity().getLocationRef() - .toString()); + response = + new TestRequest(this.percentEncodedFileUrl) + .baseRef(this.webSiteURL) + .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PROTO, "https")) + .header(new Header(HeaderConstants.HEADER_X_FORWARDED_PORT, "123")) + .handle(GET); + assertEquals( + "https://myapplication:123/a%20new%20%25file.txt.fr", + response.getEntity().getLocationRef().toString()); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java b/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java index 697ab7f5af..ca28eecaa0 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public class GenericAnnotatedServerResource extends AbstractGenericAnnotatedServerResource { diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java index 8a63c66199..fac8c6417c 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public class GenericServerResource16 extends AbstractGenericAnnotatedServerResource { @@ -14,6 +13,6 @@ public class GenericServerResource16 extends AbstractGenericAnnotatedServerRe @Override public E addResponse(E representation) { return representation; - }; - + } + ; } diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java index daca53d726..b528f6bbdb 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java @@ -1,18 +1,17 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public class GenericServerResource17 extends ServerResource implements MyResource17 { public E add(E rep) { return rep; - }; - + } + ; } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyBean.java b/org.restlet/src/test/java/org/restlet/resource/MyBean.java index 37f0d481ed..87d1692d8a 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyBean.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyBean.java @@ -1,21 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import java.io.Serializable; - import org.restlet.engine.util.SystemUtils; /** * Test bean to be serialized. - * + * * @author Jerome Louvel */ public class MyBean implements Serializable { @@ -26,8 +24,7 @@ public class MyBean implements Serializable { private String name; - public MyBean() { - } + public MyBean() {} public MyBean(String name, String description) { super(); @@ -37,18 +34,13 @@ public MyBean(String name, String description) { @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; MyBean other = (MyBean) obj; if (description == null) { - if (other.description != null) - return false; - } else if (!description.equals(other.description)) - return false; + if (other.description != null) return false; + } else if (!description.equals(other.description)) return false; if (name == null) { return other.name == null; } else return name.equals(other.name); @@ -74,5 +66,4 @@ public void setDescription(String description) { public void setName(String name) { this.name = name; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyException01.java b/org.restlet/src/test/java/org/restlet/resource/MyException01.java index 620bf91493..2ef17c8d90 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyException01.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyException01.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import java.util.Date; @@ -18,8 +17,7 @@ public class MyException01 extends Throwable { private Date date; - public MyException01() { - } + public MyException01() {} public MyException01(Date date) { this.date = date; @@ -32,5 +30,4 @@ public Date getDate() { public void setDate(Date date) { this.date = date; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyException02.java b/org.restlet/src/test/java/org/restlet/resource/MyException02.java index 5ff4e2055d..a9ffd1cbe8 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyException02.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyException02.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; @Status(value = 400) diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource01.java b/org.restlet/src/test/java/org/restlet/resource/MyResource01.java index 6f1a74a078..2fef14e0a0 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource01.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource01.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample annotated interface. - * + * * @author Jerome Louvel */ public interface MyResource01 { @@ -30,5 +29,4 @@ public interface MyResource01 { @Options("txt") String describe(); - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource02.java b/org.restlet/src/test/java/org/restlet/resource/MyResource02.java index 786b164cb4..d68ff18026 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource02.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource02.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import org.restlet.data.MediaType; @@ -19,5 +18,4 @@ public class MyResource02 extends ServerResource { public Representation represent() { return new StringRepresentation("", MediaType.TEXT_XML); } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource03.java b/org.restlet/src/test/java/org/restlet/resource/MyResource03.java index f62e953694..0fa253dc13 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource03.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource03.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample server resource that sets the "existing" flag to false. - * + * * @author Jerome Louvel */ public class MyResource03 extends ServerResource implements MyResource01 { @@ -45,5 +44,4 @@ public String store(MyBean bean) { myBean = bean; return "Done"; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource04.java b/org.restlet/src/test/java/org/restlet/resource/MyResource04.java index 2c0240b31f..e6d898fac2 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource04.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource04.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public class MyResource04 extends ServerResource { @@ -25,5 +24,4 @@ public String toJson() { public String toHtml() { return "root"; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource05.java b/org.restlet/src/test/java/org/restlet/resource/MyResource05.java index a036ccef83..e06ad161fa 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource05.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource05.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public class MyResource05 extends ServerResource { @@ -20,5 +19,4 @@ public String storeXml(String entity) { public String storeJson(String entity) { return entity; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource06.java b/org.restlet/src/test/java/org/restlet/resource/MyResource06.java index 206681092a..c76c0dd99d 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource06.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource06.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import java.io.IOException; @@ -22,5 +21,4 @@ public String storeXml(String entity) throws IOException { public String storeJson(String entity) throws IOException { return entity + "2"; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource07.java b/org.restlet/src/test/java/org/restlet/resource/MyResource07.java index f38a0f867a..8aca40b0aa 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource07.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource07.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public class MyResource07 extends ServerResource { @@ -20,5 +19,4 @@ public String storeJson(String entity) { public String storeXml(String entity) { return entity + "2"; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource08.java b/org.restlet/src/test/java/org/restlet/resource/MyResource08.java index cb0eb69e41..e98533d9f4 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource08.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource08.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public class MyResource08 extends ServerResource { @@ -20,5 +19,4 @@ public String store1(String entity) { public String store2(String entity) { return entity + "2"; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource12.java b/org.restlet/src/test/java/org/restlet/resource/MyResource12.java index 9ab5705ec1..d4cc699cb6 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource12.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource12.java @@ -1,19 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import org.restlet.data.Form; /** * Sample annotated interface. - * + * * @author Jerome Louvel */ public interface MyResource12 { @@ -23,5 +22,4 @@ public interface MyResource12 { @Put void store(Form form); - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource17.java b/org.restlet/src/test/java/org/restlet/resource/MyResource17.java index 2294da3ab3..7dc8622101 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource17.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource17.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; public interface MyResource17 { @Post R add(R rep); - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource20.java b/org.restlet/src/test/java/org/restlet/resource/MyResource20.java index 7284564db4..d40ab009d9 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource20.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource20.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample annotated interface. - * + * * @author Jerome Louvel */ public interface MyResource20 { @@ -21,5 +20,4 @@ public interface MyResource20 { @Put MyBean representAndSerializeException() throws MyException02; - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java index 031b099644..175282e737 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample server resource. - * + * * @author Jerome Louvel */ public class MyServerResource01 extends ServerResource implements MyResource01 { @@ -39,5 +38,4 @@ public String store(MyBean bean) { myBean = bean; return "Done"; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java index 84e17714e9..cc3270099b 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java @@ -1,19 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import org.restlet.data.Form; /** * Sample server resource. - * + * * @author Jerome Louvel */ public class MyServerResource12 extends ServerResource implements MyResource12 { @@ -27,5 +26,4 @@ public void store(Form form) { public Form represent() { return myForm; } - } diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java index 236fbad80a..8d4f58bfa8 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample server resource implementing abstract generic class. - * + * * @author Jerome Louvel */ public class MyServerResource15 extends AbstractGenericAnnotatedServerResource { diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java index e12a1ad20b..6ccbf0bc69 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java @@ -1,17 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample server resource implementing abstract generic class. - * + * * @author Thierry Boileau */ public class MyServerResource15Ok extends AbstractGenericAnnotatedServerResource { diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java index ab2119dd8b..b46cc58290 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java @@ -1,19 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample server resource implementing generic class. - * + * * @author Jerome Louvel */ -public class MyServerResource16 extends GenericServerResource16 { - -} +public class MyServerResource16 extends GenericServerResource16 {} diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java index 30972d9504..c837671ef5 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java @@ -1,19 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample server resource implementing generic class. - * + * * @author Jerome Louvel */ -public class MyServerResource17 extends GenericServerResource17 { - -} +public class MyServerResource17 extends GenericServerResource17 {} diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java index 7ccb1286b0..cbc4106a19 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java @@ -1,19 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; /** * Sample server resource implementing generic class. - * + * * @author Jerome Louvel */ -public class MyServerResource18 extends GenericAnnotatedServerResource { - -} +public class MyServerResource18 extends GenericAnnotatedServerResource {} diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java index 4bc9c92df3..9d7ba2009c 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java @@ -1,22 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.resource; import java.util.Date; - import org.restlet.Server; import org.restlet.data.Protocol; /** * Sample server resource. - * + * * @author Jerome Louvel */ public class MyServerResource20 extends ServerResource implements MyResource20 { @@ -35,5 +33,4 @@ public MyBean represent() throws MyException01 { public MyBean representAndSerializeException() throws MyException02 { throw new MyException02("my custom error"); } - } diff --git a/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java b/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java index e88b04ce5c..d002d8c5dc 100644 --- a/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java @@ -1,60 +1,59 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + import org.junit.jupiter.api.Test; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; -import static org.junit.jupiter.api.Assertions.*; - /** * Tests where every Filter should run through. - * + * * @author Lars Heuer */ public abstract class AbstractFilterTestCase { /** * Returns a Filter to be used for the tests. - * + * * @return Filter instance. */ protected abstract Filter getFilter(); /** * Returns a request. - * + * * @return Request instance. */ protected abstract Request getRequest(); /** * Returns a response. - * - * @param request - * The associated request. + * + * @param request The associated request. * @return Response instance. */ protected abstract Response getResponse(Request request); /** * Returns a restlet. - * + * * @return Restlet instance. */ protected abstract Restlet getRestlet(); - /** - * Test Restlet instance attaching/detaching. - */ + /** Test Restlet instance attaching/detaching. */ @Test public void testAttachDetachInstance() throws Exception { final Filter filter = getFilter(); @@ -71,9 +70,7 @@ public void testAttachDetachInstance() throws Exception { assertFalse(filter.hasNext()); } - /** - * Test not started Filter. - */ + /** Test not started Filter. */ @Test public void testIllegalStartedState() { final Filter filter = getFilter(); @@ -94,9 +91,7 @@ public void testIllegalStartedState() { } } - /** - * Test with null target. - */ + /** Test with null target. */ @Test public void testIllegalTarget() throws Exception { final Filter filter = getFilter(); @@ -109,5 +104,4 @@ public void testIllegalTarget() throws Exception { assertThrows(Exception.class, () -> filter.handle(request, response)); } - } diff --git a/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java b/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java index da93437754..0df72a60c3 100644 --- a/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; import org.restlet.Request; @@ -15,9 +14,8 @@ /** * Test {@link org.restlet.routing.Filter}. - * - * @author Lars Heuer (heuer[at]semagia.com) Semagia + * + * @author Lars Heuer (heuer[at]semagia.com) Semagia */ public class FilterTestCase extends AbstractFilterTestCase { @Override @@ -39,5 +37,4 @@ protected Response getResponse(Request request) { protected Restlet getRestlet() { return new MockRestlet(null); } - } diff --git a/org.restlet/src/test/java/org/restlet/routing/MockFilter.java b/org.restlet/src/test/java/org/restlet/routing/MockFilter.java index 45181dad08..2e45ae8dbd 100644 --- a/org.restlet/src/test/java/org/restlet/routing/MockFilter.java +++ b/org.restlet/src/test/java/org/restlet/routing/MockFilter.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; import org.restlet.Context; @@ -14,31 +13,29 @@ import org.restlet.Response; /** - * Thin layer around an AbstractFilter. Takes care about being started and - * having a target when it should handle a call. - * - * Concurrency note: instances of this class or its subclasses can be invoked by - * several threads at the same time and therefore must be thread-safe. You - * should be especially careful when storing state in member variables. - * - * @author Lars Heuer (heuer[at]semagia.com) - * Semagia + * Thin layer around an AbstractFilter. Takes care about being started and having a target when it + * should handle a call. + * + *

Concurrency note: instances of this class or its subclasses can be invoked by several threads + * at the same time and therefore must be thread-safe. You should be especially careful when storing + * state in member variables. + * + * @author Lars Heuer (heuer[at]semagia.com) Semagia */ public class MockFilter extends Filter { - public MockFilter(Context context) { - super(context); - } - - @Override - protected int beforeHandle(Request request, Response response) { - if (!super.isStarted()) { - throw new IllegalStateException("Filter is not started"); - } - if (!super.hasNext()) { - throw new IllegalStateException("Target is not set"); - } + public MockFilter(Context context) { + super(context); + } - return CONTINUE; - } + @Override + protected int beforeHandle(Request request, Response response) { + if (!super.isStarted()) { + throw new IllegalStateException("Filter is not started"); + } + if (!super.hasNext()) { + throw new IllegalStateException("Target is not set"); + } + return CONTINUE; + } } diff --git a/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java b/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java index 48556f7917..1c9f7c97df 100644 --- a/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java +++ b/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; import org.restlet.Context; @@ -15,11 +14,10 @@ import org.restlet.Restlet; /** - * Thin layer around an AbstractRestlet. Takes care about being started when it - * should handle a call. - * - * @author Lars Heuer (heuer[at]semagia.com) Semagia + * Thin layer around an AbstractRestlet. Takes care about being started when it should handle a + * call. + * + * @author Lars Heuer (heuer[at]semagia.com) Semagia */ public class MockRestlet extends Restlet { public MockRestlet(Context context) { diff --git a/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java b/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java index 528c3c04ee..3c14af40f4 100644 --- a/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java @@ -1,40 +1,41 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.restlet.*; +import org.restlet.Component; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; import org.restlet.data.MediaType; import org.restlet.data.Method; import org.restlet.data.Protocol; import org.restlet.engine.Engine; import org.restlet.representation.StringRepresentation; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Unit tests for the RedirectRestlet. - * + * * @author Jerome Louvel */ public class RedirectTestCase { private static final int TEST_PORT = 1337; - private void testCall(Context context, Method method, String uri) - throws Exception { - final Response response = context.getClientDispatcher().handle( - new Request(method, uri)); + private void testCall(Context context, Method method, String uri) throws Exception { + final Response response = context.getClientDispatcher().handle(new Request(method, uri)); assertNotNull(response.getEntity()); response.getEntity().write(System.out); } @@ -51,9 +52,7 @@ public void tearDown() { Engine.register(); } - /** - * Tests the cookies parsing. - */ + /** Tests the cookies parsing. */ @Test public void testRedirect() throws Exception { // Create components @@ -67,22 +66,34 @@ public void testRedirect() throws Exception { // Create the proxy Restlet final String target = "http://localhost:" + (TEST_PORT + 1) + "{rr}"; - final Redirector proxy = new Redirector(proxyComponent.getContext() - .createChildContext(), target, Redirector.MODE_SERVER_OUTBOUND); + final Redirector proxy = + new Redirector( + proxyComponent.getContext().createChildContext(), + target, + Redirector.MODE_SERVER_OUTBOUND); // Create a new Restlet that will display some path information. - final Restlet trace = new Restlet(originComponent.getContext() - .createChildContext()) { - @Override - public void handle(Request request, Response response) { - // Print the requested URI path - final String message = "Resource URI: " + request.getResourceRef() + '\n' - + "Base URI: " + request.getResourceRef().getBaseRef() + '\n' - + "Remaining part: " + request.getResourceRef().getRemainingPart() + '\n' - + "Method name: " + request.getMethod() + '\n'; - response.setEntity(new StringRepresentation(message, MediaType.TEXT_PLAIN)); - } - }; + final Restlet trace = + new Restlet(originComponent.getContext().createChildContext()) { + @Override + public void handle(Request request, Response response) { + // Print the requested URI path + final String message = + "Resource URI: " + + request.getResourceRef() + + '\n' + + "Base URI: " + + request.getResourceRef().getBaseRef() + + '\n' + + "Remaining part: " + + request.getResourceRef().getRemainingPart() + + '\n' + + "Method name: " + + request.getMethod() + + '\n'; + response.setEntity(new StringRepresentation(message, MediaType.TEXT_PLAIN)); + } + }; // Set the component roots proxyComponent.getDefaultHost().attach("", proxy); @@ -107,9 +118,11 @@ public void handle(Request request, Response response) { testCall(context, Method.GET, uri); testCall(context, Method.DELETE, uri); - uri = "http://localhost:" + TEST_PORT - + "/v1/client/kwse/CnJlNUQV9%252BNNqbUf7Lhs2BYEK2Y%253D" - + "/user/johnm/uVGYTDK4kK4zsu96VHGeTCzfwso%253D/"; + uri = + "http://localhost:" + + TEST_PORT + + "/v1/client/kwse/CnJlNUQV9%252BNNqbUf7Lhs2BYEK2Y%253D" + + "/user/johnm/uVGYTDK4kK4zsu96VHGeTCzfwso%253D/"; testCall(context, Method.GET, uri); // Stop the components diff --git a/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java b/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java index 10514bf6ef..60d2b6ebbd 100644 --- a/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java @@ -1,24 +1,26 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.Test; import org.restlet.Request; import org.restlet.Response; import org.restlet.util.RouteList; -import static org.junit.jupiter.api.Assertions.*; - /** * Test case for RouteList class. - * + * * @author Kevin Conaway */ public class RouteListTestCase { @@ -90,13 +92,11 @@ public void testGetRandom() { list.add(new MockScoringRoute(7)); list.add(new MockScoringRoute(8)); - final MockScoringRoute r = (MockScoringRoute) list.getRandom(null, - null, 5f); + final MockScoringRoute r = (MockScoringRoute) list.getRandom(null, null, 5f); assertNotNull(r); assertTrue(r.score > 5); assertNull(list.getRandom(null, null, 9f)); } - } diff --git a/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java b/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java index c2fc229fb9..90878787ea 100644 --- a/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java @@ -1,38 +1,35 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; -import org.junit.jupiter.api.Test; -import org.restlet.engine.Engine; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashMap; import java.util.List; import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; +import org.restlet.engine.Engine; /** * Test case for URI templates. - * + * * @author Jerome Louvel */ public class TemplateTestCase { @Test public void testEncodedCharacters() { - Template template = new Template( - "http://localhost/{token}/bookstore/{bookid}"); - String encodedToken = "FtDF91VSX%2F7AN6C39k51ZV510SW%2Fot6SIGstq8XGCcHfOfHbZOZLUD4u%2BGUNK0bBawVZ4GR5TgV7PtRbF%2Bnm9abYJN6AWycdj9J6CLyU4D7Zou36KEjkel%2B0LtlGGhFPVrCvpBuqPy8V8o5IZ9tDys0Py6sXXAtEVbXBYeRYzOvIBzOZkIviIyceVCU%2BlYv%2Fh9k7Fhlb1JGtKUCj3ZDg%2FvJ1Co7dOC1Ho3%2Fe0Fup7k9qgTuCvZRSHcpizaEFPNLp"; - String targetUri = "http://localhost/" + encodedToken - + "/bookstore/1234"; + Template template = new Template("http://localhost/{token}/bookstore/{bookid}"); + String encodedToken = + "FtDF91VSX%2F7AN6C39k51ZV510SW%2Fot6SIGstq8XGCcHfOfHbZOZLUD4u%2BGUNK0bBawVZ4GR5TgV7PtRbF%2Bnm9abYJN6AWycdj9J6CLyU4D7Zou36KEjkel%2B0LtlGGhFPVrCvpBuqPy8V8o5IZ9tDys0Py6sXXAtEVbXBYeRYzOvIBzOZkIviIyceVCU%2BlYv%2Fh9k7Fhlb1JGtKUCj3ZDg%2FvJ1Co7dOC1Ho3%2Fe0Fup7k9qgTuCvZRSHcpizaEFPNLp"; + String targetUri = "http://localhost/" + encodedToken + "/bookstore/1234"; Map variables1 = new HashMap<>(); int parsed1 = template.parse(targetUri, variables1); @@ -67,8 +64,7 @@ public void testPathMatching() { @Test public void testVariableNames() { - Template tpl = new Template( - "http://{userId}.restlet.com/invoices/{invoiceId}"); + Template tpl = new Template("http://{userId}.restlet.com/invoices/{invoiceId}"); tpl.setLogger(Engine.getAnonymousLogger()); List names = tpl.getVariableNames(); diff --git a/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java b/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java index ef380da58f..784b4c1c36 100644 --- a/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java +++ b/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; import org.restlet.Context; @@ -17,7 +16,7 @@ /** * Trace Restlet. - * + * * @author Jerome Louvel */ public class TraceRestlet extends Restlet { @@ -27,21 +26,20 @@ public TraceRestlet(Context context) { /** * Handles a uniform call. - * - * @param request - * The request to handle. - * @param response - * The response to update. + * + * @param request The request to handle. + * @param response The response to update. */ @Override public void handle(Request request, Response response) { - final String message = "Hello World!" - + "\nYour IP address is " - + request.getClientInfo().getAddress() - + "\nYour request URI is: " - + ((request.getResourceRef() == null) ? "?" : request - .getResourceRef().toString()); + final String message = + "Hello World!" + + "\nYour IP address is " + + request.getClientInfo().getAddress() + + "\nYour request URI is: " + + ((request.getResourceRef() == null) + ? "?" + : request.getResourceRef().toString()); response.setEntity(message, MediaType.TEXT_PLAIN); } - } diff --git a/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java b/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java index dc26ee3bb2..a45596dbcb 100644 --- a/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java @@ -1,21 +1,20 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.routing; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; import org.restlet.Request; import org.restlet.Response; import org.restlet.data.Status; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test {@link org.restlet.routing.Validator}. * diff --git a/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java b/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java index eb18476f7d..88e92fb14b 100644 --- a/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java +++ b/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import org.restlet.Request; @@ -16,7 +15,7 @@ /** * Reusable hello world Restlet. - * + * * @author Jerome Louvel */ public class HelloWorldRestlet extends Restlet { @@ -25,5 +24,4 @@ public class HelloWorldRestlet extends Restlet { public void handle(Request request, Response response) { response.setEntity("hello, world", MediaType.TEXT_PLAIN); } - } diff --git a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java index 328c2ab542..a8579c0bc6 100644 --- a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -14,7 +13,6 @@ import java.util.Arrays; import java.util.stream.Stream; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -44,145 +42,152 @@ */ public class HttpBasicTestCase { - public static class AuthenticatedRestlet extends Restlet { - @Override - public void handle(Request request, Response response) { - response.setEntity(AUTHENTICATED_MSG, MediaType.TEXT_PLAIN); - } - } - - public static class TestVerifier extends MapVerifier { - public TestVerifier() { - getLocalSecrets().put(SHORT_USERNAME, SHORT_PASSWORD.toCharArray()); - getLocalSecrets().put(LONG_USERNAME, LONG_PASSWORD.toCharArray()); - } - - @Override - public int verify(String identifier, char[] inputSecret) { - // NOTE: Allocating Strings are not really secure treatment of passwords - String almostSecret = new String(inputSecret); - - try { - return super.verify(identifier, inputSecret); - } finally { - // Clear secret from memory as soon as possible (This is better - // treatment, but useless due to our almostSecret copy) - Arrays.fill(inputSecret, '\000'); - } - } - } - - public static class BlockerVerifier implements Verifier { - @Override - public int verify(Request request, Response response) { - return RESULT_INVALID; - } - } - - public static final String AUTHENTICATED_MSG = "You are authenticated"; - - public static final String LONG_PASSWORD = "thisLongPasswordIsExtremelySecure"; - - public static final String LONG_USERNAME = "aVeryLongUsernameIsIndeedRequiredForThisTest"; - - public static final String SHORT_PASSWORD = "pw15"; - - public static final String SHORT_USERNAME = "user13"; - - public static final String WRONG_USERNAME = "wrongUser"; - - static Stream invalidCredentials() { - return Stream.of(arguments(LONG_USERNAME, SHORT_PASSWORD), arguments(SHORT_USERNAME, LONG_PASSWORD), - arguments(WRONG_USERNAME, SHORT_PASSWORD)); - } - - static Stream validCredentials() { - return Stream.of(arguments(LONG_USERNAME, LONG_PASSWORD), arguments(SHORT_USERNAME, SHORT_PASSWORD)); - } - - @Nested - class TestMapVerifier { - - private final MapVerifier verifier = new TestVerifier(); - - @ParameterizedTest - @MethodSource("org.restlet.security.HttpBasicTestCase#invalidCredentials") - void testInvalidCredentials(final String login, final String password) { - assertEquals(Verifier.RESULT_INVALID, this.verifier.verify(login, password.toCharArray())); - } - - @ParameterizedTest - @MethodSource("org.restlet.security.HttpBasicTestCase#validCredentials") - void testValidCredentials(final String login, final String password) { - assertEquals(Verifier.RESULT_VALID, this.verifier.verify(login, password.toCharArray())); - } - - } - - @Nested - class TestHttpBasicServer { - private Component component; - private Request request; - private Client client; - - @Test - public void HttpBasicNone() throws Exception { - final Response response = client.handle(request); - assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); - } - - @ParameterizedTest - @MethodSource("org.restlet.security.HttpBasicTestCase#invalidCredentials") - void testInvalidCredentials(final String login, final String password) throws Exception { - ChallengeResponse authentication = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password); - request.setChallengeResponse(authentication); - - final Response response = client.handle(request); - assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); - } - - @ParameterizedTest - @MethodSource("org.restlet.security.HttpBasicTestCase#validCredentials") - void testValidCredentials(final String login, final String password) throws Exception { - ChallengeResponse authentication = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password); - request.setChallengeResponse(authentication); - - final Response response = client.handle(request); - assertEquals(Status.SUCCESS_OK, response.getStatus()); - assertEquals(AUTHENTICATED_MSG, response.getEntity().getText()); - } - - @BeforeEach - public void makeServer() throws Exception { - final String REALM = HttpBasicTestCase.class.getSimpleName(); - this.component = new Component(); - final Server server = this.component.getServers().add(Protocol.HTTP, 0); - - final Application application = new Application() { - @Override - public Restlet createInboundRoot() { - ChallengeAuthenticator authenticator = new ChallengeAuthenticator(getContext(), - ChallengeScheme.HTTP_BASIC, REALM); - authenticator.setVerifier(new TestVerifier()); - authenticator.setNext(new AuthenticatedRestlet()); - return authenticator; - } - }; - - this.component.getDefaultHost().attach(application); - this.component.start(); - request = new Request(Method.GET, "http://localhost:" + server.getActualPort()); - client = new Client(Protocol.HTTP); - } - - @AfterEach - public void cleanup() throws Exception { - client.stop(); - if (this.component.isStarted()) { - this.component.stop(); - } - this.component = null; - } - } - + public static class AuthenticatedRestlet extends Restlet { + @Override + public void handle(Request request, Response response) { + response.setEntity(AUTHENTICATED_MSG, MediaType.TEXT_PLAIN); + } + } + + public static class TestVerifier extends MapVerifier { + public TestVerifier() { + getLocalSecrets().put(SHORT_USERNAME, SHORT_PASSWORD.toCharArray()); + getLocalSecrets().put(LONG_USERNAME, LONG_PASSWORD.toCharArray()); + } + + @Override + public int verify(String identifier, char[] inputSecret) { + // NOTE: Allocating Strings are not really secure treatment of passwords + String almostSecret = new String(inputSecret); + + try { + return super.verify(identifier, inputSecret); + } finally { + // Clear secret from memory as soon as possible (This is better + // treatment, but useless due to our almostSecret copy) + Arrays.fill(inputSecret, '\000'); + } + } + } + + public static class BlockerVerifier implements Verifier { + @Override + public int verify(Request request, Response response) { + return RESULT_INVALID; + } + } + + public static final String AUTHENTICATED_MSG = "You are authenticated"; + + public static final String LONG_PASSWORD = "thisLongPasswordIsExtremelySecure"; + + public static final String LONG_USERNAME = "aVeryLongUsernameIsIndeedRequiredForThisTest"; + + public static final String SHORT_PASSWORD = "pw15"; + + public static final String SHORT_USERNAME = "user13"; + + public static final String WRONG_USERNAME = "wrongUser"; + + static Stream invalidCredentials() { + return Stream.of( + arguments(LONG_USERNAME, SHORT_PASSWORD), + arguments(SHORT_USERNAME, LONG_PASSWORD), + arguments(WRONG_USERNAME, SHORT_PASSWORD)); + } + + static Stream validCredentials() { + return Stream.of( + arguments(LONG_USERNAME, LONG_PASSWORD), arguments(SHORT_USERNAME, SHORT_PASSWORD)); + } + + @Nested + class TestMapVerifier { + + private final MapVerifier verifier = new TestVerifier(); + + @ParameterizedTest + @MethodSource("org.restlet.security.HttpBasicTestCase#invalidCredentials") + void testInvalidCredentials(final String login, final String password) { + assertEquals( + Verifier.RESULT_INVALID, this.verifier.verify(login, password.toCharArray())); + } + + @ParameterizedTest + @MethodSource("org.restlet.security.HttpBasicTestCase#validCredentials") + void testValidCredentials(final String login, final String password) { + assertEquals( + Verifier.RESULT_VALID, this.verifier.verify(login, password.toCharArray())); + } + } + + @Nested + class TestHttpBasicServer { + private Component component; + private Request request; + private Client client; + + @Test + public void HttpBasicNone() throws Exception { + final Response response = client.handle(request); + assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); + } + + @ParameterizedTest + @MethodSource("org.restlet.security.HttpBasicTestCase#invalidCredentials") + void testInvalidCredentials(final String login, final String password) throws Exception { + ChallengeResponse authentication = + new ChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password); + request.setChallengeResponse(authentication); + + final Response response = client.handle(request); + assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); + } + + @ParameterizedTest + @MethodSource("org.restlet.security.HttpBasicTestCase#validCredentials") + void testValidCredentials(final String login, final String password) throws Exception { + ChallengeResponse authentication = + new ChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password); + request.setChallengeResponse(authentication); + + final Response response = client.handle(request); + assertEquals(Status.SUCCESS_OK, response.getStatus()); + assertEquals(AUTHENTICATED_MSG, response.getEntity().getText()); + } + + @BeforeEach + public void makeServer() throws Exception { + final String REALM = HttpBasicTestCase.class.getSimpleName(); + this.component = new Component(); + final Server server = this.component.getServers().add(Protocol.HTTP, 0); + + final Application application = + new Application() { + @Override + public Restlet createInboundRoot() { + ChallengeAuthenticator authenticator = + new ChallengeAuthenticator( + getContext(), ChallengeScheme.HTTP_BASIC, REALM); + authenticator.setVerifier(new TestVerifier()); + authenticator.setNext(new AuthenticatedRestlet()); + return authenticator; + } + }; + + this.component.getDefaultHost().attach(application); + this.component.start(); + request = new Request(Method.GET, "http://localhost:" + server.getActualPort()); + client = new Client(Protocol.HTTP); + } + + @AfterEach + public void cleanup() throws Exception { + client.stop(); + if (this.component.isStarted()) { + this.component.stop(); + } + this.component = null; + } + } } diff --git a/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java b/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java index 7800d34e49..e17d3c1c94 100644 --- a/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java +++ b/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java @@ -1,72 +1,55 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file are subject to the terms of one of the following - * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can - * select the license that you prefer but you may not use this file except in - * compliance with one of these Licenses. - * - * You can obtain a copy of the Apache 2.0 license at - * http://www.opensource.org/licenses/apache-2.0 - * - * You can obtain a copy of the EPL 1.0 license at - * http://www.opensource.org/licenses/eclipse-1.0 - * - * See the Licenses for the specific language governing permissions and - * limitations under the Licenses. - * - * Alternatively, you can obtain a royalty free commercial license with less - * limitations, transferable or non-transferable, directly at - * https://restlet.talend.com/ - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -public class MemoryRealmTest { - - @Test - public void whenUnmappingAGroupAndRoleFromAMemoryRealmThenMappingIsDropped() { - // given a Memory Realm, a Group and a Role - MemoryRealm memoryRealm = new MemoryRealm(); - Group group = new Group(); - Role role = new Role(); - - // Given the group and role are mapped - memoryRealm.map(group, role); - // Then there is a mapping for this group - assertFalse(memoryRealm.findRoles(group).isEmpty()); - - // When I remove this mapping - memoryRealm.unmap(group, role); - - // Then the memory realm has no more mapping - assertTrue(memoryRealm.findRoles(group).isEmpty()); - } - - @Test - public void whenUnmappingAUserAndRoleFromAMemoryRealmThenMappingIsDropped() { - // given a Memory Realm, a Group and a Role - MemoryRealm memoryRealm = new MemoryRealm(); - User user = new User(); - Role role = new Role(); - - // Given the user and role are mapped - memoryRealm.map(user, role); - // Then there is a mapping for this user - assertFalse(memoryRealm.findRoles(user).isEmpty()); - - // When I remove this mapping - memoryRealm.unmap(user, role); +import org.junit.jupiter.api.Test; - // Then the memory realm has no more mapping - assertTrue(memoryRealm.findRoles(user).isEmpty()); - } +public class MemoryRealmTest { + @Test + public void whenUnmappingAGroupAndRoleFromAMemoryRealmThenMappingIsDropped() { + // given a Memory Realm, a Group and a Role + MemoryRealm memoryRealm = new MemoryRealm(); + Group group = new Group(); + Role role = new Role(); + + // Given the group and role are mapped + memoryRealm.map(group, role); + // Then there is a mapping for this group + assertFalse(memoryRealm.findRoles(group).isEmpty()); + + // When I remove this mapping + memoryRealm.unmap(group, role); + + // Then the memory realm has no more mapping + assertTrue(memoryRealm.findRoles(group).isEmpty()); + } + + @Test + public void whenUnmappingAUserAndRoleFromAMemoryRealmThenMappingIsDropped() { + // given a Memory Realm, a Group and a Role + MemoryRealm memoryRealm = new MemoryRealm(); + User user = new User(); + Role role = new Role(); + + // Given the user and role are mapped + memoryRealm.map(user, role); + // Then there is a mapping for this user + assertFalse(memoryRealm.findRoles(user).isEmpty()); + + // When I remove this mapping + memoryRealm.unmap(user, role); + + // Then the memory realm has no more mapping + assertTrue(memoryRealm.findRoles(user).isEmpty()); + } } diff --git a/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java b/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java index 222f72ca41..59d19378db 100644 --- a/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java @@ -1,20 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; -import org.junit.jupiter.api.Test; -import org.restlet.Application; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import org.junit.jupiter.api.Test; +import org.restlet.Application; + /** * Suite of unit tests for the {@link Role} class. * diff --git a/org.restlet/src/test/java/org/restlet/security/SaasApplication.java b/org.restlet/src/test/java/org/restlet/security/SaasApplication.java index 75c7fcd094..7e2c825d57 100644 --- a/org.restlet/src/test/java/org/restlet/security/SaasApplication.java +++ b/org.restlet/src/test/java/org/restlet/security/SaasApplication.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import org.restlet.Application; @@ -16,9 +15,8 @@ import org.restlet.routing.Router; /** - * Sample SAAS application with a Basic authenticator guarding a hello world - * Restlet. - * + * Sample SAAS application with a Basic authenticator guarding a hello world Restlet. + * * @author Jerome Louvel */ public class SaasApplication extends Application { @@ -38,8 +36,8 @@ public Restlet createInboundRoot() { Router root = new Router(); // Attach test 1 - ChallengeAuthenticator authenticator = new ChallengeAuthenticator( - getContext(), ChallengeScheme.HTTP_BASIC, "saas"); + ChallengeAuthenticator authenticator = + new ChallengeAuthenticator(getContext(), ChallengeScheme.HTTP_BASIC, "saas"); authenticator.setNext(new HelloWorldRestlet()); root.attach("/httpBasicAuthenticator", authenticator); @@ -58,8 +56,8 @@ public Restlet createInboundRoot() { roleAuthorizer.getAuthorizedRoles().add(getRole("admin")); roleAuthorizer.setNext(new HelloWorldRestlet()); - authenticator = new ChallengeAuthenticator(getContext(), - ChallengeScheme.HTTP_BASIC, "saas"); + authenticator = + new ChallengeAuthenticator(getContext(), ChallengeScheme.HTTP_BASIC, "saas"); authenticator.setNext(roleAuthorizer); root.attach("/adminRoleAuthorizer", authenticator); @@ -68,8 +66,8 @@ public Restlet createInboundRoot() { roleAuthorizer.getForbiddenRoles().add(getRole("admin")); roleAuthorizer.setNext(new HelloWorldRestlet()); - authenticator = new ChallengeAuthenticator(getContext(), - ChallengeScheme.HTTP_BASIC, "saas"); + authenticator = + new ChallengeAuthenticator(getContext(), ChallengeScheme.HTTP_BASIC, "saas"); authenticator.setNext(roleAuthorizer); root.attach("/adminRoleForbiddenAuthorizer", authenticator); diff --git a/org.restlet/src/test/java/org/restlet/security/SaasComponent.java b/org.restlet/src/test/java/org/restlet/security/SaasComponent.java index 53ab1b98f2..2f6ecb663d 100644 --- a/org.restlet/src/test/java/org/restlet/security/SaasComponent.java +++ b/org.restlet/src/test/java/org/restlet/security/SaasComponent.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; import org.restlet.Component; @@ -16,7 +15,7 @@ /** * Sample SAAS component with declared organizations. - * + * * @author Jerome Louvel */ public class SaasComponent extends Component { @@ -34,12 +33,10 @@ public SaasComponent() { context.setDefaultVerifier(realm.getVerifier()); // Add users - User stiger = new User("stiger", "pwd", "Scott", "Tiger", - "scott.tiger@foobar.com"); + User stiger = new User("stiger", "pwd", "Scott", "Tiger", "scott.tiger@foobar.com"); realm.getUsers().add(stiger); - User larmstrong = new User("larmstrong", "pwd", "Louis", "Armstrong", - "la@foobar.com"); + User larmstrong = new User("larmstrong", "pwd", "Louis", "Armstrong", "la@foobar.com"); realm.getUsers().add(larmstrong); // Add groups diff --git a/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java b/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java index ca3e83df39..9ce338f400 100644 --- a/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java @@ -1,14 +1,16 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.security; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.restlet.data.ChallengeScheme.HTTP_BASIC; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -18,9 +20,6 @@ import org.restlet.resource.ClientResource; import org.restlet.resource.ResourceException; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.restlet.data.ChallengeScheme.HTTP_BASIC; - /** * Restlet unit tests for the security package. * @@ -28,8 +27,10 @@ */ public class SecurityTestCase { - private final ChallengeResponse lambdaUserCR = new ChallengeResponse(HTTP_BASIC, "stiger", "pwd"); - private final ChallengeResponse adminUserCR = new ChallengeResponse(HTTP_BASIC, "larmstrong", "pwd"); + private final ChallengeResponse lambdaUserCR = + new ChallengeResponse(HTTP_BASIC, "stiger", "pwd"); + private final ChallengeResponse adminUserCR = + new ChallengeResponse(HTTP_BASIC, "larmstrong", "pwd"); private SaasComponent component; private int testPort; @@ -55,14 +56,16 @@ public void stopServer() throws Exception { @Test public void withoutAuthenticationHttpBasicAuthenticatorShouldReturnUnauthorizedResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/httpBasicAuthenticator"); + ClientResource resource = + new ClientResource("http://localhost:" + testPort + "/httpBasicAuthenticator"); runClientResource(resource); assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, resource.getStatus()); } @Test public void withAuthenticationHttpBasicAuthenticatorShouldReturnOkResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/httpBasicAuthenticator"); + ClientResource resource = + new ClientResource("http://localhost:" + testPort + "/httpBasicAuthenticator"); resource.setChallengeResponse(lambdaUserCR); runClientResource(resource); assertEquals(Status.SUCCESS_OK, resource.getStatus()); @@ -70,45 +73,57 @@ public void withAuthenticationHttpBasicAuthenticatorShouldReturnOkResponse() { @Test public void withoutAuthenticationAlwaysAuthenticatorShouldReturnOkResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/alwaysAuthenticator"); + ClientResource resource = + new ClientResource("http://localhost:" + testPort + "/alwaysAuthenticator"); runClientResource(resource); assertEquals(Status.SUCCESS_OK, resource.getStatus()); } @Test public void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/neverAuthenticator"); + ClientResource resource = + new ClientResource("http://localhost:" + testPort + "/neverAuthenticator"); runClientResource(resource); assertEquals(Status.CLIENT_ERROR_FORBIDDEN, resource.getStatus()); } @Test - public void withLambdaUserAuthenticationAdminRoleAuthorizerAuthenticatorShouldReturnForbiddenResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/adminRoleAuthorizer"); + public void + withLambdaUserAuthenticationAdminRoleAuthorizerAuthenticatorShouldReturnForbiddenResponse() { + ClientResource resource = + new ClientResource("http://localhost:" + testPort + "/adminRoleAuthorizer"); resource.setChallengeResponse(lambdaUserCR); runClientResource(resource); assertEquals(Status.CLIENT_ERROR_FORBIDDEN, resource.getStatus()); } @Test - public void withAdminUserAuthenticationAdminRoleAuthorizerAuthenticatorShouldReturnOkResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/adminRoleAuthorizer"); + public void + withAdminUserAuthenticationAdminRoleAuthorizerAuthenticatorShouldReturnOkResponse() { + ClientResource resource = + new ClientResource("http://localhost:" + testPort + "/adminRoleAuthorizer"); resource.setChallengeResponse(adminUserCR); runClientResource(resource); assertEquals(Status.SUCCESS_OK, resource.getStatus()); } @Test - public void withAdminUserAuthenticationAdminRoleForbiddenAuthorizerAuthenticatorShouldReturnForbiddenResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/adminRoleForbiddenAuthorizer"); + public void + withAdminUserAuthenticationAdminRoleForbiddenAuthorizerAuthenticatorShouldReturnForbiddenResponse() { + ClientResource resource = + new ClientResource( + "http://localhost:" + testPort + "/adminRoleForbiddenAuthorizer"); resource.setChallengeResponse(adminUserCR); runClientResource(resource); assertEquals(Status.CLIENT_ERROR_FORBIDDEN, resource.getStatus()); } @Test - public void withLambdaUserAuthenticationAdminRoleForbiddenAuthorizerAuthenticatorShouldReturnOkResponse() { - ClientResource resource = new ClientResource("http://localhost:" + testPort + "/adminRoleForbiddenAuthorizer"); + public void + withLambdaUserAuthenticationAdminRoleForbiddenAuthorizerAuthenticatorShouldReturnOkResponse() { + ClientResource resource = + new ClientResource( + "http://localhost:" + testPort + "/adminRoleForbiddenAuthorizer"); resource.setChallengeResponse(lambdaUserCR); runClientResource(resource); assertEquals(Status.SUCCESS_OK, resource.getStatus()); @@ -117,8 +132,8 @@ public void withLambdaUserAuthenticationAdminRoleForbiddenAuthorizerAuthenticato private static void runClientResource(ClientResource resource) { try { resource.get(); - } catch (ResourceException e) {} + } catch (ResourceException e) { + } resource.release(); } - } diff --git a/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java index c5741f8048..df4ffab69a 100644 --- a/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java @@ -1,28 +1,28 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.Request; import org.restlet.data.MediaType; import org.restlet.data.Preference; import org.restlet.representation.Variant; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - /** * Unit tests for the content negotiation service. - * + * * @author Jerome Louvel */ public class ConnegServiceTestCase { @@ -34,31 +34,29 @@ public void testStrict() { variants.add(variant); Request request = new Request(); - request.getClientInfo().getAcceptedMediaTypes() + request.getClientInfo() + .getAcceptedMediaTypes() .add(new Preference<>(MediaType.APPLICATION_JSON)); MetadataService metadataService = new MetadataService(); ConnegService connegService = new ConnegService(); // Flexible algorithm - Variant preferedVariant = connegService.getPreferredVariant(variants, - request, metadataService); + Variant preferedVariant = + connegService.getPreferredVariant(variants, request, metadataService); assertNotNull(preferedVariant); assertEquals(MediaType.APPLICATION_XML, preferedVariant.getMediaType()); // Strict algorithm connegService.setStrict(true); - preferedVariant = connegService.getPreferredVariant(variants, request, - metadataService); + preferedVariant = connegService.getPreferredVariant(variants, request, metadataService); assertNull(preferedVariant); // Add a variant to match the strict preferences variant = new Variant(MediaType.APPLICATION_JSON); variants.add(variant); - preferedVariant = connegService.getPreferredVariant(variants, request, - metadataService); + preferedVariant = connegService.getPreferredVariant(variants, request, metadataService); assertNotNull(preferedVariant); assertEquals(MediaType.APPLICATION_JSON, preferedVariant.getMediaType()); - } } diff --git a/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java index 8200975f35..6ba57a4631 100644 --- a/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java @@ -1,19 +1,18 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import static org.junit.jupiter.api.Assertions.assertNull; + import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; -import static org.junit.jupiter.api.Assertions.assertNull; - /** * Unit tests for the metadata service. * diff --git a/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java index 96b79887d2..cad9901605 100644 --- a/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java @@ -1,14 +1,19 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; +import java.io.Serial; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,23 +27,18 @@ import org.restlet.representation.ObjectRepresentation; import org.restlet.representation.Representation; -import java.io.IOException; -import java.io.Serial; - -import static org.junit.jupiter.api.Assertions.*; - /** * Unit tests for the status service. - * + * * @author Jerome Louvel */ @SuppressWarnings("unchecked") -public class StatusServiceTestCase { +public class StatusServiceTestCase { StatusService statusService = new StatusService(); @BeforeEach - void setUp() { + void setUp() { // Restore a clean engine Engine.clearThreadLocalVariables(); Engine.register(true); @@ -53,7 +53,8 @@ void cleanUp() { @Test public void shouldConvertToStatus() { - AnnotatedNotSerializableException statusException = new AnnotatedNotSerializableException("test message", 50); + AnnotatedNotSerializableException statusException = + new AnnotatedNotSerializableException("test message", 50); Status status = statusService.toStatus(statusException, null, null); @@ -63,7 +64,8 @@ public void shouldConvertToStatus() { @Test public void exceptionShouldNotBeSerialized() throws IOException { - AnnotatedNotSerializableException statusException = new AnnotatedNotSerializableException("test message", 50); + AnnotatedNotSerializableException statusException = + new AnnotatedNotSerializableException("test message", 50); Status status = new Status(400, statusException); Representation representation = statusServiceToRepresentation(status); @@ -82,12 +84,14 @@ public void exceptionShouldNotBeSerialized() throws IOException { @Test public void shouldSerializeAnnotatedException() throws IOException { - Status status = new Status(400, AnnotatedSerializableException.withoutCause("test message", 50)); + Status status = + new Status(400, AnnotatedSerializableException.withoutCause("test message", 50)); Representation representation = statusServiceToRepresentation(status); assertEquals(MediaType.APPLICATION_JAVA_OBJECT, representation.getMediaType()); - AnnotatedSerializableException throwable = ((ObjectRepresentation) representation).getObject(); + AnnotatedSerializableException throwable = + ((ObjectRepresentation) representation).getObject(); assertEquals(50, throwable.value); assertEquals("test message", throwable.getMessage()); assertNull(throwable.getCause()); @@ -95,12 +99,14 @@ public void shouldSerializeAnnotatedException() throws IOException { @Test public void shouldSerializeAnnotatedExceptionWithCause() throws IOException { - Status status = new Status(400, AnnotatedSerializableException.withCause("test message", 50)); + Status status = + new Status(400, AnnotatedSerializableException.withCause("test message", 50)); Representation representation = statusServiceToRepresentation(status); assertEquals(MediaType.APPLICATION_JAVA_OBJECT, representation.getMediaType()); - AnnotatedSerializableException throwable = ((ObjectRepresentation) representation).getObject(); + AnnotatedSerializableException throwable = + ((ObjectRepresentation) representation).getObject(); assertEquals(50, throwable.value); assertEquals("test message", throwable.getMessage()); assertEquals(0, throwable.getStackTrace().length); @@ -109,7 +115,8 @@ public void shouldSerializeAnnotatedExceptionWithCause() throws IOException { @Test public void shouldSerializeAnnotatedExceptionWithStackTrace() throws IOException { - Status status = new Status(400, AnnotatedSerializableException.withCause("test message", 50)); + Status status = + new Status(400, AnnotatedSerializableException.withCause("test message", 50)); Request request = new Request(); Response response = new Response(request); @@ -121,7 +128,8 @@ public void shouldSerializeAnnotatedExceptionWithStackTrace() throws IOException Representation representation = statusService.toRepresentation(status, request, response); assertEquals(MediaType.APPLICATION_JAVA_OBJECT, representation.getMediaType()); - AnnotatedSerializableException throwable = ((ObjectRepresentation) representation).getObject(); + AnnotatedSerializableException throwable = + ((ObjectRepresentation) representation).getObject(); assertEquals(50, throwable.value); assertEquals("test message", throwable.getMessage()); assertEquals(1, throwable.getStackTrace().length); @@ -137,8 +145,7 @@ private Representation statusServiceToRepresentation(Status status) { @org.restlet.resource.Status(value = 400, serialize = false) private static class AnnotatedNotSerializableException extends Throwable { - @Serial - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; private final int value; @@ -148,7 +155,7 @@ public AnnotatedNotSerializableException(String message, int value) { } @SuppressWarnings("unused") - public int getValue() { + public int getValue() { return value; } } @@ -156,36 +163,39 @@ public int getValue() { @org.restlet.resource.Status(value = 401) private static class AnnotatedSerializableException extends Throwable { - @Serial - private static final long serialVersionUID = 1L; + @Serial private static final long serialVersionUID = 1L; private int value; - public static AnnotatedSerializableException withoutCause(final String message, final int value) { + public static AnnotatedSerializableException withoutCause( + final String message, final int value) { StackTraceElement[] stackTrace = new StackTraceElement[1]; stackTrace[0] = new StackTraceElement("DeclaringClass", "MethodName", "FileName", 1); - AnnotatedSerializableException annotatedSerializableException = new AnnotatedSerializableException(message, value); + AnnotatedSerializableException annotatedSerializableException = + new AnnotatedSerializableException(message, value); annotatedSerializableException.setStackTrace(stackTrace); return annotatedSerializableException; } - public static AnnotatedSerializableException withCause(final String message, final int value) { + public static AnnotatedSerializableException withCause( + final String message, final int value) { StackTraceElement[] stackTrace = new StackTraceElement[1]; stackTrace[0] = new StackTraceElement("DeclaringClass", "MethodName", "FileName", 1); Throwable rootCause = new IOException("File '/toto.txt' is not readable"); rootCause.setStackTrace(stackTrace); - AnnotatedSerializableException annotatedSerializableException = new AnnotatedSerializableException(message, value, rootCause); + AnnotatedSerializableException annotatedSerializableException = + new AnnotatedSerializableException(message, value, rootCause); annotatedSerializableException.setStackTrace(stackTrace); return annotatedSerializableException; } @SuppressWarnings("unused") - public AnnotatedSerializableException() {} + public AnnotatedSerializableException() {} - public AnnotatedSerializableException(String message, int value) { + public AnnotatedSerializableException(String message, int value) { super(message); this.value = value; } @@ -199,5 +209,4 @@ public int getValue() { return value; } } - } diff --git a/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java b/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java index 44b9c21d16..ec2a738b01 100644 --- a/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java +++ b/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; import org.restlet.data.MediaType; @@ -16,9 +15,7 @@ import org.restlet.resource.ResourceException; import org.restlet.resource.ServerResource; -/** - * Simple resource that returns at least text/html and text/xml representations. - */ +/** Simple resource that returns at least text/html and text/xml representations. */ public class UserAgentTestResource extends ServerResource { public UserAgentTestResource() { diff --git a/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java b/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java index ff3dad1a7f..158eeb0cf5 100644 --- a/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java @@ -1,14 +1,15 @@ /** * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.service; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.restlet.Application; @@ -21,11 +22,7 @@ import org.restlet.data.Status; import org.restlet.routing.Router; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Tests cases for the tunneling of preferences based on user agent. - */ +/** Tests cases for the tunneling of preferences based on user agent. */ public class UserAgentTunnelFilterTestCase { private Application application; @@ -48,14 +45,15 @@ private Request createRequest() { @BeforeEach public void setUpEach() { - this.application = new Application() { - @Override - public Restlet createInboundRoot() { - Router router = new Router(getContext()); - router.attachDefault(UserAgentTestResource.class); - return router; - } - }; + this.application = + new Application() { + @Override + public Restlet createInboundRoot() { + Router router = new Router(getContext()); + router.attachDefault(UserAgentTestResource.class); + return router; + } + }; } @Test diff --git a/pom.xml b/pom.xml index 2e2df7d70f..6969ca277c 100644 --- a/pom.xml +++ b/pom.xml @@ -62,8 +62,6 @@ 4.3.1 2.0.17 6.2.8 - 2.2.43 - 2.1.38 3.1.3.RELEASE 2.4.1 @@ -78,7 +76,6 @@ org.restlet.ext.jaas org.restlet.ext.jackson org.restlet.ext.json - org.restlet.ext.openapi org.restlet.ext.slf4j org.restlet.ext.spring org.restlet.ext.thymeleaf @@ -224,8 +221,7 @@ - + false ${release} @@ -313,6 +309,50 @@ SemVerVersionPolicy + + com.diffplug.spotless + spotless-maven-plugin + 3.3.0 + + + + + src/main/java/**/*.java + src/test/java/**/*.java + + + + + + true + + + + + + + + + * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */]]> + + + + + + + apply + check + + compile + + + From 3057ef07c18aed7987ff8a057191a196d982490b Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 29 Mar 2026 23:20:11 +0200 Subject: [PATCH 02/30] Add spotless plugin and fix grammar issues --- .../restlet/ext/crypto/AwsAuthenticator.java | 2 +- .../ext/crypto/CookieAuthenticator.java | 6 +- .../ext/crypto/DigestAuthenticator.java | 14 +-- .../org/restlet/ext/crypto/DigestUtils.java | 27 +++--- .../restlet/ext/crypto/DigestVerifier.java | 10 +-- .../restlet/ext/crypto/internal/AwsUtils.java | 24 +++-- .../ext/crypto/internal/AwsVerifier.java | 33 ++++--- .../ext/crypto/internal/CryptoUtils.java | 15 ++-- .../crypto/internal/HttpAwsQueryHelper.java | 2 +- .../internal/HttpAzureSharedKeyHelper.java | 47 +++++----- .../HttpAzureSharedKeyLiteHelper.java | 21 ++--- .../ext/crypto/internal/HttpDigestHelper.java | 89 +++++++++++-------- .../crypto/internal/HttpDigestVerifier.java | 4 +- .../ext/freemarker/FreemarkerConverter.java | 6 +- .../ext/freemarker/TemplateFilter.java | 2 +- .../org/restlet/ext/gson/GsonConverter.java | 7 +- .../restlet/ext/gson/GsonRepresentation.java | 2 +- .../org/restlet/ext/jaas/JaasVerifier.java | 2 +- .../ext/jackson/JacksonRepresentation.java | 10 +-- .../jackson/internal/XmlFactoryProvider.java | 16 ++-- .../org/restlet/ext/json/JsonConverter.java | 1 + .../restlet/ext/json/JsonRepresentation.java | 12 +-- .../org/restlet/ext/json/JsonpFilter.java | 4 +- .../restlet/ext/spring/SpringBeanRouter.java | 6 +- .../restlet/ext/spring/SpringComponent.java | 8 +- .../org/restlet/ext/spring/SpringContext.java | 4 +- .../org/restlet/ext/spring/SpringFinder.java | 2 +- .../org/restlet/ext/spring/SpringHost.java | 4 +- .../org/restlet/ext/spring/SpringRouter.java | 2 +- .../ext/thymeleaf/TemplateRepresentation.java | 4 +- .../restlet/ext/velocity/TemplateFilter.java | 6 +- .../ext/velocity/TemplateRepresentation.java | 7 +- .../restlet/ext/xml/DomRepresentation.java | 12 +-- .../restlet/ext/xml/SaxRepresentation.java | 12 +-- .../ext/xml/TransformRepresentation.java | 11 ++- .../java/org/restlet/ext/xml/Transformer.java | 6 +- .../restlet/ext/xml/XmlRepresentation.java | 16 ++-- .../java/org/restlet/ext/xml/XmlWriter.java | 56 ++++++------ .../ext/xml/internal/AbstractXmlReader.java | 18 ++-- .../ext/xml/internal/ContextResolver.java | 2 +- pom.xml | 1 + 41 files changed, 259 insertions(+), 274 deletions(-) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java index 55f8b58a7e..b3dec60f05 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java @@ -88,7 +88,7 @@ public void setMaxRequestAge(long value) { } /** - * Sets the internal verifier. In general you shouldn't replace it but instead set the {@code + * Sets the internal verifier. In general, you shouldn't replace it but instead set the {@code * wrappedVerifier} via the {@link #setWrappedVerifier(LocalVerifier)} method. */ @Override diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java index c8a9495e9d..ad18df95a2 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java @@ -97,7 +97,7 @@ public class CookieAuthenticator extends ChallengeAuthenticator { * Constructor. Use the {@link ChallengeScheme#HTTP_COOKIE} pseudo-scheme. * * @param context The parent context. - * @param optional Indicates if this authenticator is optional so alternative authenticators + * @param optional Indicates if this authenticator is optional, so alternative authenticators * down the chain can be attempted. * @param realm The name of the security realm. * @param encryptSecretKey The secret key used to encrypt the cookie value. @@ -226,10 +226,10 @@ public void challenge(Response response, boolean stale) { */ public String formatCredentials(ChallengeResponse challenge) throws GeneralSecurityException { // Data buffer - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); // Indexes buffer - StringBuffer isb = new StringBuffer(); + StringBuilder isb = new StringBuilder(); String timeIssued = Long.toString(System.currentTimeMillis()); int i = timeIssued.length(); sb.append(timeIssued); diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java index 96a4cec08f..03659e87b1 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java @@ -66,7 +66,7 @@ public DigestAuthenticator( } /** - * Constructor. By default, it set the "optional" property to 'false' and the "domainUris" + * Constructor. By default, it sets the "optional" property to 'false' and the "domainUris" * property to a single '/' URI. * * @param context The context. @@ -87,9 +87,9 @@ protected ChallengeRequest createChallengeRequest(boolean stale) { } /** - * Generates a server nonce. + * Generates server nonce. * - * @return A new server nonce. + * @return New server nonce. */ public String generateServerNonce() { return CryptoUtils.makeNonce(getServerKey()); @@ -108,7 +108,7 @@ public List getDomainRefs() { synchronized (this) { r = this.domainRefs; if (r == null) { - this.domainRefs = r = new CopyOnWriteArrayList(); + this.domainRefs = r = new CopyOnWriteArrayList<>(); this.domainRefs.add(new Reference("/")); } } @@ -122,7 +122,7 @@ public List getDomainRefs() { * * @param identifier The user identifier to hash. * @param secret The user secret. - * @return A hash of the user name, realm, and password. + * @return A hash of the username, realm, and password. */ public String getHashedSecret(String identifier, char[] secret) { if (ChallengeScheme.HTTP_DIGEST.equals(getScheme())) { @@ -175,7 +175,7 @@ public void setMaxServerNonceAge(long maxServerNonceAge) { } /** - * Sets the secret key known only by server. + * Sets the secret key known only by the server. * * @param serverKey The server secret key. */ @@ -184,7 +184,7 @@ public void setServerKey(String serverKey) { } /** - * Set the internal verifier. In general you shouldn't replace it and instead use the {@link + * Set the internal verifier. In general, you shouldn't replace it and instead use the {@link * #setWrappedVerifier(LocalVerifier)} method. * * @param verifier The internal verifier. diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java index 882cb5879a..8f1236b389 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java @@ -9,6 +9,7 @@ package org.restlet.ext.crypto; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -26,11 +27,11 @@ public class DigestUtils { /** * General regex pattern to extract comma separated name-value components. This pattern captures - * one name and value per match(), and is repeatedly applied to the input string to extract all + * one name and value per match() and is repeatedly applied to the input string to extract all * components. Must handle both quoted and unquoted values as RFC2617 isn't consistent in this - * respect. Pattern is immutable and thread-safe so reuse one static instance. + * respect. Pattern is immutable and thread-safe, so reuse one static instance. */ - private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray(); + private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); /** * Returns the digest of the target string. Target is decoded to bytes using the US-ASCII @@ -124,7 +125,7 @@ public static byte[] toHMacSha256(String source, byte[] secretKey) { mac.init(signingKey); // Compute the HMAC value - result = mac.doFinal(source.getBytes("UTF-8")); + result = mac.doFinal(source.getBytes(StandardCharsets.UTF_8)); } catch (NoSuchAlgorithmException nsae) { throw new RuntimeException( "Could not find the SHA256 algorithm. HMac conversion failed.", nsae); @@ -134,8 +135,6 @@ public static byte[] toHMacSha256(String source, byte[] secretKey) { } catch (IllegalStateException ise) { throw new RuntimeException( "IIllegal state exception detected. HMac conversion failed.", ise); - } catch (UnsupportedEncodingException uee) { - throw new RuntimeException("Unsuported encoding UTF-8. HMac conversion failed.", uee); } return result; @@ -154,12 +153,12 @@ public static byte[] toHMacSha256(String source, String secretKey) { /** * Return the HTTP DIGEST hashed secret. It concatenates the identifier, realm and secret, - * separated by a comma and digest them using MD5. + * separated by a comma and digests them using MD5. * * @param identifier The user identifier to hash. * @param secret The user secret. * @param realm The authentication realm. - * @return A hash of the user name, realm, and password, specified as A1 in section 3.2.2.2 of + * @return A hash of the username, realm, and password, specified as A1 in section 3.2.2.2 of * RFC2617, or null if the identifier has no corresponding secret. */ public static String toHttpDigest(String identifier, char[] secret, String realm) { @@ -173,7 +172,7 @@ public static String toHttpDigest(String identifier, char[] secret, String realm /** * Returns the MD5 digest of the target string. Target is decoded to bytes using the US-ASCII * charset. The returned hexadecimal String always contains 32 lowercase alphanumeric - * characters. For example, if target is "HelloWorld", this method returns + * characters. For example, if the target is "HelloWorld", this method returns * "68e109f0f40ca72a15e05cc22786f8e6". * * @param target The string to encode. @@ -190,9 +189,9 @@ public static String toMd5(String target) { } /** - * Returns the MD5 digest of target string. Target is decoded to bytes using the named charset. + * Returns the MD5 digest of the target string. Target is decoded to bytes using the named charset. * The returned hexadecimal String always contains 32 lowercase alphanumeric characters. For - * example, if target is "HelloWorld", this method returns "68e109f0f40ca72a15e05cc22786f8e6". + * example, if the target is "HelloWorld", this method returns "68e109f0f40ca72a15e05cc22786f8e6". * * @param target The string to encode. * @param charsetName The character set. @@ -207,8 +206,8 @@ public static String toMd5(String target, String charsetName) final char[] md5Chars = new char[32]; int i = 0; for (final byte b : md5) { - md5Chars[i++] = HEXDIGITS[(b >> 4) & 0xF]; - md5Chars[i++] = HEXDIGITS[b & 0xF]; + md5Chars[i++] = HEX_DIGITS[(b >> 4) & 0xF]; + md5Chars[i++] = HEX_DIGITS[b & 0xF]; } return new String(md5Chars); } catch (NoSuchAlgorithmException nsae) { @@ -234,7 +233,7 @@ public static String toSha1(String target) { } /** - * Returns the SHA1 digest of target string. Target is decoded to bytes using the named charset. + * Returns the SHA1 digest of the target string. Target is decoded to bytes using the named charset. * * @param target The string to encode. * @param charsetName The character set. diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java index eaac7ae0c1..6a53821371 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java @@ -28,7 +28,7 @@ */ public class DigestVerifier extends SecretVerifier { - /** The digest algorithm of provided secrets. */ + /** The digest algorithm. */ private String algorithm; /** The digest algorithm of secrets returned by the wrapped verifier. */ @@ -40,7 +40,7 @@ public class DigestVerifier extends SecretVerifier { /** * Constructor. * - * @param algorithm The digest algorithm of provided secrets. + * @param algorithm The digest algorithm. * @param wrappedVerifier The wrapped secret verifier. * @param wrappedAlgorithm The digest algorithm of secrets provided by the wrapped verifier. * @see Digest @@ -54,7 +54,7 @@ public DigestVerifier(String algorithm, T wrappedVerifier, String wrappedAlgorit /** * Computes the digest of a secret according to a specified algorithm. By default, MD5 hashes * (represented as a sequence of 32 hexadecimal digits) and SHA-1 hashes are supported. For - * additional algorithm, override this method. + * an additional algorithm, override this method. * * @param identifier The user identifier. * @param secret The regular secret to digest. @@ -134,7 +134,7 @@ public char[] getWrappedSecret(String identifier) { /** * Returns the digest of the wrapped secret associated with a given identifier. If the wrapped - * algorithm is null it returns the digest of the wrapped secret, otherwise the algorithms must + * algorithm is null, it returns the digest of the wrapped secret; otherwise the algorithms must * be identical. This method can only be called if the wrapped verifier is a {@link * LocalVerifier}. * @@ -183,7 +183,7 @@ public int verify(String identifier, char[] secret) { if (getWrappedAlgorithm() != null) { secretDigest = digest(identifier, secret, getWrappedAlgorithm()); } else { - // Both secrets should be in clear + // Both secrets should be clear } result = getWrappedVerifier().verify(identifier, secretDigest); diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java index 182895ff1f..78dd580ea3 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java @@ -48,8 +48,8 @@ public static String getCanonicalizedAmzHeaders(Series

requestHeaders) { StringBuilder sb = new StringBuilder(); Pattern spacePattern = Pattern.compile("\\s+"); - // Create a lexographically sorted list of headers that begin with x-amz - SortedMap amzHeaders = new TreeMap(); + // Create a lexicographically sorted list of headers that begin with x-amz + SortedMap amzHeaders = new TreeMap<>(); if (requestHeaders != null) { for (Header header : requestHeaders) { @@ -267,8 +267,7 @@ public static String getS3StringToSign(Request request, Series
headers) String date = (headers == null) ? null : headers.getFirstValue("X-Amz-Date", true); String method = request.getMethod().getName(); - // If amazon's date header wasn't found, try to grab the regular date - // header + // If amazon's date header wasn't found, try to grab the regular date header if (date == null || (date.isEmpty())) { date = (headers == null) @@ -278,7 +277,7 @@ public static String getS3StringToSign(Request request, Series
headers) // If no date header exists, make one if (date == null || (date.isEmpty())) { - date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.get(0)); + date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.getFirst()); if (headers != null) { headers.add(HeaderConstants.HEADER_DATE, date); } @@ -307,14 +306,11 @@ public static String getS3StringToSign(Request request, Series
headers) contentType = "application/x-www-form-urlencoded"; } - StringBuilder toSign = new StringBuilder(); - toSign.append(method != null ? method : "").append("\n"); - toSign.append(contentMD5 != null ? contentMD5 : "").append("\n"); - toSign.append(contentType != null ? contentType : "").append("\n"); - toSign.append(date != null ? date : "").append("\n"); - toSign.append(canonicalizedAmzHeaders != null ? canonicalizedAmzHeaders : ""); - toSign.append(canonicalizedResource != null ? canonicalizedResource : ""); - - return toSign.toString(); + return (method != null ? method : "") + "\n" + + (contentMD5 != null ? contentMD5 : "") + "\n" + + (contentType != null ? contentType : "") + "\n" + + (date != null ? date : "") + "\n" + + (canonicalizedAmzHeaders != null ? canonicalizedAmzHeaders : "") + + (canonicalizedResource != null ? canonicalizedResource : ""); } } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java index 859620d4f8..c06cb21898 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.ext.crypto.internal; @@ -29,7 +29,7 @@ * * @author Jean-Philippe Steinmetz * @see - * Authenticating REST Requests + * Authenticating REST Requests */ public class AwsVerifier extends SecretVerifier { /** Default maximum request age (15 minutes) */ @@ -44,7 +44,8 @@ public class AwsVerifier extends SecretVerifier { /** * Creates a new HttpAwsS3Verifier instance. * - * @param wrappedVerifier The wrapped verifier containing local identifier/secret couples + * @param wrappedVerifier + * The wrapped verifier containing local identifier/secret couples */ public AwsVerifier(LocalVerifier wrappedVerifier) { this(wrappedVerifier, DEFAULT_MAX_REQUEST_AGE); @@ -53,9 +54,11 @@ public AwsVerifier(LocalVerifier wrappedVerifier) { /** * Creates a new HttpAwsS3Verifier instance. * - * @param wrappedVerifier The wrapped verifier containing local identifier/secret couples - * @param maxRequestAge The maximum age of a request, in milliseconds, before it is considered - * stale + * @param wrappedVerifier + * The wrapped verifier containing local identifier/secret couples + * @param maxRequestAge + * The maximum age of a request, in milliseconds, before it is considered + * stale */ public AwsVerifier(LocalVerifier wrappedVerifier, long maxRequestAge) { super(); @@ -76,14 +79,16 @@ protected String getIdentifier(Request request, Response response) { String[] parts = request.getChallengeResponse().getRawValue().split(":"); - if (parts != null && parts.length == 2) return parts[0]; - else return null; + return (parts.length == 2) + ? parts[0] + : null; } /** * Returns the local secret associated with a given identifier. * - * @param identifier The identifier to lookup. + * @param identifier + * The identifier to lookup. * @return The secret associated with the identifier or null. */ public char[] getLocalSecret(String identifier) { @@ -114,8 +119,9 @@ protected char[] getSecret(Request request, Response response) { String[] parts = request.getChallengeResponse().getRawValue().split(":"); - if (parts != null && parts.length == 2) return parts[1].toCharArray(); - else return null; + return (parts.length == 2) + ? parts[1].toCharArray() + : null; } /** @@ -140,7 +146,8 @@ public void setMaxRequestAge(long value) { /** * Sets the wrapped local secret verifier. * - * @param wrappedVerifier The local secret verifier. + * @param wrappedVerifier + * The local secret verifier. */ public void setWrappedVerifier(LocalVerifier wrappedVerifier) { this.wrappedVerifier = wrappedVerifier; diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java index 67451b910c..5be882ae6c 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java @@ -1,18 +1,21 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.ext.crypto.internal; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Base64; + import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; + import org.restlet.ext.crypto.DigestUtils; /** @@ -52,7 +55,7 @@ private static Cipher createCipher(String algorithm, byte[] secretKey, int mode) public static String decrypt(String algo, byte[] secretKey, byte[] encrypted) throws GeneralSecurityException { byte[] original = doFinal(algo, secretKey, Cipher.DECRYPT_MODE, encrypted); - return new String(original, Charset.forName("UTF-8")); + return new String(original, StandardCharsets.UTF_8); } /** @@ -97,7 +100,7 @@ private static byte[] doFinal(String algo, byte[] secretKey, int mode, byte[] wh public static byte[] encrypt(String algo, byte[] secretKey, String content) throws GeneralSecurityException { return doFinal( - algo, secretKey, Cipher.ENCRYPT_MODE, content.getBytes(Charset.forName("UTF-8"))); + algo, secretKey, Cipher.ENCRYPT_MODE, content.getBytes(StandardCharsets.UTF_8)); } /** @@ -115,14 +118,14 @@ public static byte[] encrypt(String algo, String base64Secret, String content) } /** - * Generates a nonce as recommended in section 3.2.1 of RFC-2617, but without the ETag field. + * Generates nonce as recommended in section 3.2.1 of RFC-2617, but without the ETag field. * The format is:

      * Base64.encodeBytes(currentTimeMS + ":"
      *         + md5String(currentTimeMS + ":" + secretKey))
      * 
* * @param secretKey a secret value known only to the creator of the nonce. It's inserted into - * the nonce, and can be used later to validate the nonce. + * the nonce and can be used later to validate the nonce. */ public static String makeNonce(String secretKey) { final long currentTimeMS = System.currentTimeMillis(); diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java index bbaf095d79..2297026d01 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java @@ -40,7 +40,7 @@ public Reference updateReference( query.add("SignatureMethod", "HmacSHA256"); query.add("SignatureVersion", "2"); query.add("Version", "2009-04-15"); - String df = DateUtils.format(new Date(), DateUtils.FORMAT_ISO_8601.get(0)); + String df = DateUtils.format(new Date(), DateUtils.FORMAT_ISO_8601.getFirst()); query.add("Timestamp", df); // Compute then add the signature parameter diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java index 333ffb2bd2..b200481872 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java @@ -50,7 +50,7 @@ public class HttpAzureSharedKeyHelper extends AuthenticatorHelper { private static String getCanonicalizedAzureHeaders(Series
requestHeaders) { // Filter out all the Azure headers required for SharedKey // authentication - SortedMap azureHeaders = new TreeMap(); + SortedMap azureHeaders = new TreeMap<>(); String headerName; for (Header header : requestHeaders) { @@ -66,8 +66,7 @@ private static String getCanonicalizedAzureHeaders(Series
requestHeaders // Concatenate all Azure headers StringBuilder sb = new StringBuilder(); - for (Iterator iterator = azureHeaders.keySet().iterator(); iterator.hasNext(); ) { - String key = iterator.next(); + for (String key : azureHeaders.keySet()) { sb.append(key).append(':').append(azureHeaders.get(key)).append("\n"); } @@ -85,8 +84,7 @@ private static String getCanonicalizedResourceName(Reference resourceRef) { Parameter param = form.getFirst("comp", true); if (param != null) { - StringBuilder sb = new StringBuilder(resourceRef.getPath()); - return sb.append("?").append("comp=").append(param.getValue()).toString(); + return resourceRef.getPath() + "?" + "comp=" + param.getValue(); } return resourceRef.getPath(); @@ -104,10 +102,10 @@ public void formatResponse( Request request, Series
httpHeaders) { - // Setup the method name + // Set up the method name final String methodName = request.getMethod().getName(); - // Setup the Date header + // Set up the Date header String date = ""; if (httpHeaders.getFirstValue("x-ms-date", true) == null) { @@ -115,17 +113,17 @@ public void formatResponse( date = httpHeaders.getFirstValue(HeaderConstants.HEADER_DATE, true); if (date == null) { // Add a fresh Date header - date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.get(0)); + date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.getFirst()); httpHeaders.add(HeaderConstants.HEADER_DATE, date); } } - // Setup the ContentType header + // Set up the ContentType header String contentMd5 = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_MD5, true); if (contentMd5 == null) { contentMd5 = ""; } - // Setup the ContentType header + // Set up the ContentType header String contentType = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE, true); if (contentType == null) { boolean applyPatch = false; @@ -153,26 +151,21 @@ public void formatResponse( } } - // Setup the canonicalized AzureHeaders + // Set up the canonicalized AzureHeaders final String canonicalizedAzureHeaders = getCanonicalizedAzureHeaders(httpHeaders); - // Setup the canonicalized path + // Set up the canonicalized path final String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); - // Setup the message part - final StringBuilder rest = new StringBuilder(); - rest.append(methodName) - .append('\n') - .append(contentMd5) - .append('\n') - .append(contentType) - .append('\n') - .append(date) - .append('\n') - .append(canonicalizedAzureHeaders) - .append('/') - .append(challenge.getIdentifier()) - .append(canonicalizedResource); + // Set up the message part + final String rest = methodName + '\n' + + contentMd5 + '\n' + + contentType + '\n' + + date + '\n' + + canonicalizedAzureHeaders + + '/' + + challenge.getIdentifier() + + canonicalizedResource; // Append the SharedKey credentials cw.append(challenge.getIdentifier()) @@ -181,7 +174,7 @@ public void formatResponse( Base64.getEncoder() .encodeToString( DigestUtils.toHMacSha256( - rest.toString(), + rest, Base64.getDecoder() .decode( IoUtils.toByteArray( diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java index 835d39cca7..e7c0edf7de 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java @@ -47,8 +47,7 @@ private static String getCanonicalizedResourceName(Reference resourceRef) { Parameter param = form.getFirst("comp", true); if (param != null) { - StringBuilder sb = new StringBuilder(resourceRef.getPath()); - return sb.append("?").append("comp=").append(param.getValue()).toString(); + return resourceRef.getPath() + "?" + "comp=" + param.getValue(); } return resourceRef.getPath(); @@ -66,7 +65,7 @@ public void formatResponse( Request request, Series
httpHeaders) { - // Setup the Date header + // Set up the Date header String date = ""; if (httpHeaders.getFirstValue("x-ms-date", true) == null) { @@ -82,16 +81,14 @@ public void formatResponse( date = httpHeaders.getFirstValue("x-ms-date", true); } - // Setup the canonicalized path + // Set up the canonicalized path String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); - // Setup the message part - StringBuilder rest = new StringBuilder(); - rest.append(date) - .append('\n') - .append('/') - .append(challenge.getIdentifier()) - .append(canonicalizedResource); + // Set up the message part + final String rest = date + '\n' + + '/' + + challenge.getIdentifier() + + canonicalizedResource; // Append the SharedKey credentials cw.append(challenge.getIdentifier()) @@ -100,7 +97,7 @@ public void formatResponse( Base64.getEncoder() .encodeToString( DigestUtils.toHMacSha256( - rest.toString(), + rest, Base64.getDecoder() .decode( IoUtils.toByteArray( diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java index 94fb549138..b4e4ff58af 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java @@ -36,8 +36,21 @@ */ public class HttpDigestHelper extends AuthenticatorHelper { + private static final String ALGORITHM = "algorithm"; + private static final String CNONCE = "cnonce"; + private static final String DOMAIN = "domain"; + private static final String NC = "nc"; + private static final String NONCE = "nonce"; + private static final String OPAQUE = "opaque"; + private static final String QUALITY_OPTION = "qop"; + private static final String REALM = "realm"; + private static final String RESPONSE = "response"; + private static final String STALE = "stale"; + private static final String URI = "uri"; + private static final String USERNAME = "username"; + /** - * Checks whether the specified nonce is valid with respect to the specified secretKey, and + * Checks whether the specified nonce is valid with respect to the specified secretKey and * further confirms that the nonce was generated less than lifespanMillis milliseconds ago * * @param nonce The nonce value. @@ -79,7 +92,7 @@ public void formatRequest( throws IOException { if (challenge.getRealm() != null) { - cw.appendQuotedChallengeParameter("realm", challenge.getRealm()); + cw.appendQuotedChallengeParameter(REALM, challenge.getRealm()); } else { getLogger() .warning( @@ -87,7 +100,7 @@ public void formatRequest( } if (!challenge.getDomainRefs().isEmpty()) { - cw.append(", domain=\""); + cw.append(", " + DOMAIN + "=\""); for (int i = 0; i < challenge.getDomainRefs().size(); i++) { if (i > 0) { @@ -101,23 +114,23 @@ public void formatRequest( } if (challenge.getServerNonce() != null) { - cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce()); + cw.appendQuotedChallengeParameter(NONCE, challenge.getServerNonce()); } if (challenge.getOpaque() != null) { - cw.appendQuotedChallengeParameter("opaque", challenge.getOpaque()); + cw.appendQuotedChallengeParameter(OPAQUE, challenge.getOpaque()); } if (challenge.isStale()) { - cw.appendChallengeParameter("stale", "true"); + cw.appendChallengeParameter(STALE, "true"); } if (challenge.getDigestAlgorithm() != null) { - cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm()); + cw.appendChallengeParameter(ALGORITHM, challenge.getDigestAlgorithm()); } if (!challenge.getQualityOptions().isEmpty()) { - cw.append(", qop=\""); + cw.append(", " + QUALITY_OPTION + "=\""); for (int i = 0; i < challenge.getQualityOptions().size(); i++) { if (i > 0) { @@ -147,47 +160,47 @@ public void formatResponse( Series
httpHeaders) { if (challenge.getIdentifier() != null) { - cw.appendQuotedChallengeParameter("username", challenge.getIdentifier()); + cw.appendQuotedChallengeParameter(USERNAME, challenge.getIdentifier()); } if (challenge.getRealm() != null) { - cw.appendQuotedChallengeParameter("realm", challenge.getRealm()); + cw.appendQuotedChallengeParameter(REALM, challenge.getRealm()); } if (challenge.getServerNonce() != null) { - cw.appendQuotedChallengeParameter("nonce", challenge.getServerNonce()); + cw.appendQuotedChallengeParameter(NONCE, challenge.getServerNonce()); } if (challenge.getDigestRef() != null) { challenge.setDigestRef(new Reference(request.getResourceRef().getPath())); - cw.appendQuotedChallengeParameter("uri", challenge.getDigestRef().toString()); + cw.appendQuotedChallengeParameter(URI, challenge.getDigestRef().toString()); } char[] responseDigest = formatResponseDigest(challenge, request); if (responseDigest != null) { - cw.appendQuotedChallengeParameter("response", new String(responseDigest)); + cw.appendQuotedChallengeParameter(RESPONSE, new String(responseDigest)); } if ((challenge.getDigestAlgorithm() != null) && !Digest.ALGORITHM_MD5.equals(challenge.getDigestAlgorithm())) { - cw.appendChallengeParameter("algorithm", challenge.getDigestAlgorithm()); + cw.appendChallengeParameter(ALGORITHM, challenge.getDigestAlgorithm()); } if (challenge.getClientNonce() != null) { - cw.appendQuotedChallengeParameter("cnonce", challenge.getClientNonce()); + cw.appendQuotedChallengeParameter(CNONCE, challenge.getClientNonce()); } if (challenge.getOpaque() != null) { - cw.appendQuotedChallengeParameter("opaque", challenge.getOpaque()); + cw.appendQuotedChallengeParameter(OPAQUE, challenge.getOpaque()); } if (challenge.getQuality() != null) { - cw.appendChallengeParameter("qop", challenge.getQuality()); + cw.appendChallengeParameter(QUALITY_OPTION, challenge.getQuality()); } if ((challenge.getQuality() != null) && (challenge.getServerNonceCount() > 0)) { - cw.appendChallengeParameter("nc", challenge.getServerNonceCountAsHex()); + cw.appendChallengeParameter(NC, challenge.getServerNonceCountAsHex()); } for (Parameter param : challenge.getParameters()) { @@ -264,26 +277,26 @@ public char[] formatResponseDigest(ChallengeResponse challengeResponse, Request public void parseRequest( ChallengeRequest challenge, Response response, Series
httpHeaders) { if (challenge.getRawValue() != null) { - HeaderReader hr = new HeaderReader(challenge.getRawValue()); + HeaderReader hr = new HeaderReader<>(challenge.getRawValue()); try { Parameter param = hr.readParameter(); while (param != null) { try { - if ("realm".equals(param.getName())) { + if (REALM.equals(param.getName())) { challenge.setRealm(param.getValue()); - } else if ("domain".equals(param.getName())) { + } else if (DOMAIN.equals(param.getName())) { challenge.getDomainRefs().add(new Reference(param.getValue())); - } else if ("nonce".equals(param.getName())) { + } else if (NONCE.equals(param.getName())) { challenge.setServerNonce(param.getValue()); - } else if ("opaque".equals(param.getName())) { + } else if (OPAQUE.equals(param.getName())) { challenge.setOpaque(param.getValue()); - } else if ("stale".equals(param.getName())) { - challenge.setStale(Boolean.valueOf(param.getValue())); - } else if ("algorithm".equals(param.getName())) { + } else if (STALE.equals(param.getName())) { + challenge.setStale(Boolean.parseBoolean(param.getValue())); + } else if (ALGORITHM.equals(param.getName())) { challenge.setDigestAlgorithm(param.getValue()); - } else if ("qop".equals(param.getName())) { + } else if (QUALITY_OPTION.equals(param.getName())) { // challenge.setDigestAlgorithm(param.getValue()); } else { challenge.getParameters().add(param); @@ -316,32 +329,32 @@ public void parseRequest( public void parseResponse( ChallengeResponse challenge, Request request, Series
httpHeaders) { if (challenge.getRawValue() != null) { - HeaderReader hr = new HeaderReader(challenge.getRawValue()); + HeaderReader hr = new HeaderReader<>(challenge.getRawValue()); try { Parameter param = hr.readParameter(); while (param != null) { try { - if ("username".equals(param.getName())) { + if (USERNAME.equals(param.getName())) { challenge.setIdentifier(param.getValue()); - } else if ("realm".equals(param.getName())) { + } else if (REALM.equals(param.getName())) { challenge.setRealm(param.getValue()); - } else if ("nonce".equals(param.getName())) { + } else if (NONCE.equals(param.getName())) { challenge.setServerNonce(param.getValue()); - } else if ("uri".equals(param.getName())) { + } else if (URI.equals(param.getName())) { challenge.setDigestRef(new Reference(param.getValue())); - } else if ("response".equals(param.getName())) { + } else if (RESPONSE.equals(param.getName())) { challenge.setSecret(param.getValue()); - } else if ("algorithm".equals(param.getName())) { + } else if (ALGORITHM.equals(param.getName())) { challenge.setDigestAlgorithm(param.getValue()); - } else if ("cnonce".equals(param.getName())) { + } else if (CNONCE.equals(param.getName())) { challenge.setClientNonce(param.getValue()); - } else if ("opaque".equals(param.getName())) { + } else if (OPAQUE.equals(param.getName())) { challenge.setOpaque(param.getValue()); - } else if ("qop".equals(param.getName())) { + } else if (QUALITY_OPTION.equals(param.getName())) { challenge.setQuality(param.getValue()); - } else if ("nc".equals(param.getName())) { + } else if (NC.equals(param.getName())) { challenge.setServerNonceCount(Integer.valueOf(param.getValue(), 16)); } else { challenge.getParameters().add(param); diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java index 1fe159b728..2321d54c0f 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java @@ -46,7 +46,7 @@ public HttpDigestVerifier( } /** - * If the algorithm is {@link Digest#ALGORITHM_HTTP_DIGEST}, then is retrieves the realm for + * If the algorithm is {@link Digest#ALGORITHM_HTTP_DIGEST}, then it retrieves the realm for * {@link #getDigestAuthenticator()} to compute the digest, otherwise, it keeps the default * behavior. */ @@ -112,7 +112,7 @@ public int verify(Request request, Response response) { serverNonce, getDigestAuthenticator().getServerKey(), getDigestAuthenticator().getMaxServerNonceAge())) { - // Nonce expired, send challenge request with stale=true + // Nonce expired, send a challenge request with stale=true result = RESULT_STALE; } } catch (Exception ce) { diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java index 7e823de0ea..03eda3590b 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java @@ -52,14 +52,12 @@ public float score(Representation source, Class target, Resource resource } @Override - public T toObject(Representation source, Class target, Resource resource) - throws IOException { + public T toObject(Representation source, Class target, Resource resource) { return null; } @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) - throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) { if (source instanceof Template) { return new TemplateRepresentation( diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java index dd3a4ce46c..ac487d13cf 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java @@ -20,7 +20,7 @@ import org.restlet.util.Resolver; /** - * Filter response's entity and wrap it with a FreeMarker's template representation. By default, the + * Filter the response's entity and wrap it with a FreeMarker's template representation. By default, the * template representation provides a data model based on the request and response objects. In order * for the wrapping to happen, the representations must have the {@link Encoding#FREEMARKER} * encoding set.
diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java index b5a9560143..db97e03e20 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java @@ -19,7 +19,7 @@ import org.restlet.resource.Resource; /** - * Converter between the JSON and Representation classe based on Gson library. + * Converter between the JSON and Representation classes based on the Gson library. * * @author Neal Mi */ @@ -76,7 +76,7 @@ public List getVariants(Class source) { @Override public float score(Object source, Variant target, Resource resource) { - float result = -1.0F; + final float result; if (source instanceof GsonRepresentation) { result = 1.0F; @@ -148,8 +148,7 @@ public Representation toRepresentation(Object source, Variant target, Resource r } if (VARIANT_JSON.isCompatible(target)) { - GsonRepresentation gsonRepresentation = create(source); - result = gsonRepresentation; + result = create(source); } } diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java index 446fb95e84..b2a41da8ac 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java @@ -67,7 +67,7 @@ public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext private GsonBuilder builder; /** The JSON representation to parse. */ - private Representation jsonRepresentation; + private final Representation jsonRepresentation; /** The (parsed) object to format. */ private T object; diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java index 899c9ffec1..f06d6622f6 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java @@ -160,7 +160,7 @@ public int verify(Request request, Response response) { request.getClientInfo().getPrincipals().add(principal); } /* - * If no user has been set yet and if this principal if of the + * If no user has been set yet and if this principal is of the * type we expect for a user, we create a new User based on the * principal's name. */ diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java index d71a07b5e5..54be58860c 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java @@ -30,7 +30,7 @@ /** * Representation based on the Jackson library. It can serialize and deserialize automatically in - * JSON, JSON binary (Smile), XML, YAML and CSV.
+ * JSON, JSON binary (Smile), XML, YAML, and CSV.
*
* SECURITY WARNING: Using XML parsers configured to not prevent nor limit document type definition * (DTD) entity resolution can expose the parser to an XML Entity Expansion injection attack. @@ -153,12 +153,12 @@ protected CsvSchema createCsvSchema(CsvMapper csvMapper) { /** * Creates a Jackson object mapper based on a media type. It supports JSON, JSON Smile, XML, - * YAML and CSV. + * YAML, and CSV. * * @return The Jackson object mapper. */ protected ObjectMapper createObjectMapper() { - ObjectMapper result = null; + final ObjectMapper result; if (MediaType.APPLICATION_JSON.isCompatible(getMediaType())) { JsonFactory jsonFactory = new JsonFactory(); @@ -218,7 +218,7 @@ protected ObjectMapper createObjectMapper() { * @return The Jackson object reader. */ protected ObjectReader createObjectReader() { - ObjectReader result = null; + final ObjectReader result; if (MediaType.TEXT_CSV.isCompatible(getMediaType())) { CsvMapper csvMapper = (CsvMapper) getObjectMapper(); @@ -238,7 +238,7 @@ protected ObjectReader createObjectReader() { * @return The Jackson object writer. */ protected ObjectWriter createObjectWriter() { - ObjectWriter result = null; + final ObjectWriter result; if (MediaType.TEXT_CSV.isCompatible(getMediaType())) { CsvMapper csvMapper = (CsvMapper) getObjectMapper(); diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java index a26c49389f..05b9a453c0 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java @@ -24,24 +24,20 @@ public class XmlFactoryProvider { /** - * Allow to explicitly set the Stax2InputFactory instance in OSGI context. In a no-OSGI context, - * the factory is retrieved with java service loader. + * Allow explicitly setting the Stax2InputFactory instance in OSGI context. In a no-OSGI context, + * the factory is retrieved with a java service loader. * - *

Note: Stax2 implementation is provided by woodstox library which is a dependency of + *

Note: Stax2 implementation is provided by woodstox library, which is a dependency of * Jackson. - * - * @see org.restlet.ext.jackson.internal.Activator */ public static Stax2InputFactoryProvider inputFactoryProvider = null; /** - * Allow to explicitly set the Stax2OutputFactoryProvider instance in OSGI context. In a no-OSGI - * context, the factory is retrieved with java service loader. + * Allow explicitly setting the Stax2OutputFactoryProvider instance in OSGI context. In a no-OSGI + * context, the factory is retrieved with a java service loader. * - *

Note: Stax2 implementation is provided by woodstox library which is a dependency of + *

Note: Stax2 implementation is provided by woodstox library, which is a dependency of * Jackson. - * - * @see org.restlet.ext.jackson.internal.Activator */ public static Stax2OutputFactoryProvider outputFactoryProvider = null; diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java index a08024fa08..c3784a40e4 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java @@ -128,6 +128,7 @@ public T toObject(Representation source, Class target, Resource resource) } catch (JSONException e) { IOException ioe = new IOException("Unable to convert to JSON array"); ioe.initCause(e); + throw ioe; } } else if (JSONObject.class.isAssignableFrom(target)) { try { diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java index 40fc922a2b..0956e66e7f 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java @@ -171,27 +171,23 @@ private String getJsonText() throws JSONException { String result = null; if (this.jsonValue != null) { - if (this.jsonValue instanceof JSONArray) { - JSONArray jsonArray = (JSONArray) this.jsonValue; + if (this.jsonValue instanceof final JSONArray jsonArray) { if (isIndenting()) { result = jsonArray.toString(getIndentingSize()); } else { result = jsonArray.toString(); } - } else if (this.jsonValue instanceof JSONObject) { - JSONObject jsonObject = (JSONObject) this.jsonValue; + } else if (this.jsonValue instanceof final JSONObject jsonObject) { if (isIndenting()) { result = jsonObject.toString(getIndentingSize()); } else { result = jsonObject.toString(); } - } else if (this.jsonValue instanceof JSONStringer) { - JSONStringer jsonStringer = (JSONStringer) this.jsonValue; + } else if (this.jsonValue instanceof final JSONStringer jsonStringer) { result = jsonStringer.toString(); - } else if (this.jsonValue instanceof JSONTokener) { - JSONTokener jsonTokener = (JSONTokener) this.jsonValue; + } else if (this.jsonValue instanceof final JSONTokener jsonTokener) { result = jsonTokener.toString(); } } else if (this.jsonRepresentation != null) { diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java index 8ad1e043df..953e5efe4b 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java @@ -17,9 +17,9 @@ import org.restlet.routing.Filter; /** - * Filter that converts response entity of the JSON media type into a JSONP callback document. Make + * Filter that converts the response entity of the JSON media type into a JSONP callback document. Make * sure that you properly pass a "callback" query parameter in the URI query string with the name of - * your JavaScrip callback method. + * your JavaScript callback method. * *

See {@link JsonpRepresentation} for the actual wrapper representation used internally. * diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java index 702f97d7e7..3b00b3cba8 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java @@ -185,7 +185,7 @@ private boolean isAvailableUri(String name) { } /** - * Returns true if bean names will be searched for higher up in the BeanFactory hierarchy. + * Returns true if bean names are searched for higher up in the BeanFactory hierarchy. * Default is true. * * @return True if bean names will be searched for higher up in the BeanFactory hierarchy. @@ -197,7 +197,7 @@ public boolean isFindingInAncestors() { /** * Attaches all {@link ServerResource} and {@link Restlet} beans found in the surrounding bean * factory for which {@link #resolveUri} finds a usable URI. Also attaches everything explicitly - * routed in the attachments property. + * routed in the attachments' property. * * @param beanFactory The Spring bean factory. * @see #setAttachments @@ -274,7 +274,7 @@ public void setAttachments(Map attachments) { } /** - * Sets if bean names will be searched for higher up in the BeanFactory hierarchy. + * Sets if bean names are searched for higher up in the BeanFactory hierarchy. * * @param findingInAncestors Search for beans higher up in the BeanFactory hierarchy. */ diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java index cfb211f3d0..ce3a116d0c 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java @@ -61,7 +61,7 @@ public class SpringComponent extends org.restlet.Component { /** * Adds a client to the list of connectors. The value can be either a protocol name, a Protocol - * instance or a Client instance. + * instance, or a Client instance. * * @param clientInfo The client info. */ @@ -72,7 +72,7 @@ public void setClient(Object clientInfo) { } /** - * Sets the list of clients, either as protocol names, Protocol instances or Client instances. + * Sets the list of clients, either as protocol names, Protocol instances, or Client instances. * * @param clients The list of clients. */ @@ -103,7 +103,7 @@ public void setDefaultTarget(Restlet target) { /** * Adds a server to the list of connectors. The value can be either a protocol name, a Protocol - * instance or a Server instance. + * instance, or a Server instance. * * @param serverInfo The server info. */ @@ -114,7 +114,7 @@ public void setServer(Object serverInfo) { } /** - * Sets the list of servers, either as protocol names, Protocol instances or Server instances. + * Sets the list of servers, either as protocol names, Protocol instances, or Server instances. * * @param serversInfo The list of servers. */ diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java index 34a3dc7950..10ebf62cd0 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java @@ -74,7 +74,7 @@ public List getPropertyConfigRefs() { synchronized (this) { p = this.propertyConfigRefs; if (p == null) { - this.propertyConfigRefs = p = new ArrayList(); + this.propertyConfigRefs = p = new ArrayList<>(); } } } @@ -103,7 +103,7 @@ public List getXmlConfigRefs() { synchronized (this) { x = this.xmlConfigRefs; if (x == null) { - this.xmlConfigRefs = x = new ArrayList(); + this.xmlConfigRefs = x = new ArrayList<>(); } } } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java index 7d87e744cd..af4bc1ce9d 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java @@ -105,7 +105,7 @@ public ServerResource create() { /** * Calls the {@link #create()} method that can be configured as a lookup method in Spring. - * Overriding this method was necessary for direct calls to it, for example by unit tests. + * Overriding this method was necessary for direct calls to it, for example, by unit tests. */ @Override public ServerResource create( diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java index 568dad342f..3fbf1e5acf 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java @@ -60,7 +60,7 @@ public SpringHost(Context context) { } /** - * Sets a route to attach. The keys is the URI template and the value can be either Restlet + * Sets a route to attach. The keys are the URI template, and the value can be either Restlet * instance, {@link ServerResource} subclasse (as {@link Class} instances or as qualified class * names). * @@ -76,7 +76,7 @@ public void setAttachment(String path, Object route) { } /** - * Sets the map of routes to attach. The map keys are the URI templates and the values can be + * Sets the map of routes to attach. The map keys are the URI templates, and the values can be * either Restlet instances, {@link ServerResource} subclasses (as {@link Class} instances or as * qualified class names). * diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java index db64f50a74..ce9a84891d 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java @@ -113,7 +113,7 @@ public SpringRouter(Restlet parent) { } /** - * Sets the map of routes to attach. The map keys are the URI templates and the values can be + * Sets the map of routes to attach. The map keys are the URI templates, and the values can be * either Restlet instances, {@link ServerResource} subclasses (as {@link Class} instances or as * qualified class names). * diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java index fc7fdc2806..febe93ef3e 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java @@ -164,7 +164,7 @@ public TemplateRepresentation( * @param mediaType The representation's media-type. */ public TemplateRepresentation(String templateName, Locale locale, MediaType mediaType) { - this(templateName, locale, new ConcurrentHashMap(), mediaType); + this(templateName, locale, new ConcurrentHashMap<>(), mediaType); } /** @@ -200,7 +200,7 @@ public TemplateRepresentation( */ public TemplateRepresentation( String templateName, TemplateEngine engine, Locale locale, MediaType mediaType) { - this(templateName, engine, locale, new ConcurrentHashMap(), mediaType); + this(templateName, engine, locale, new ConcurrentHashMap<>(), mediaType); } /** diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java index 421d3ed829..ce99153566 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java @@ -22,7 +22,7 @@ import org.restlet.util.Resolver; /** - * Filter response's entity and wrap it with a Velocity's template representation. By default, the + * Filter the response's entity and wrap it with a Velocity's template representation. By default, the * template representation provides a data model based on the request and response objects. In order * for the wrapping to happen, the representations must have the {@link Encoding#VELOCITY} encoding * set.
@@ -115,9 +115,7 @@ protected void afterHandle(Request request, Response response) { response.setEntity(representation); } catch (ResourceNotFoundException e) { response.setStatus(Status.CLIENT_ERROR_NOT_FOUND, e); - } catch (ParseErrorException e) { - response.setStatus(Status.SERVER_ERROR_INTERNAL, e); - } catch (IOException e) { + } catch (ParseErrorException | IOException e) { response.setStatus(Status.SERVER_ERROR_INTERNAL, e); } } diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java index e9dc44e4d0..da8131b040 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java @@ -89,7 +89,7 @@ public Object put(String key, Object value) { /** * Does nothing since resolver as a data model cannot be updated. * - * @param value The name of the value to remove. + * @param key The name of the value to remove. * @return null. */ @Override @@ -116,7 +116,6 @@ public Object remove(String key) { * @param templateRepresentation The representation to 'decode'. * @param dataModel The Velocity template's data model. * @param mediaType The representation's media-type. - * @throws IOException * @throws ParseErrorException * @throws ResourceNotFoundException */ @@ -124,7 +123,7 @@ public TemplateRepresentation( Representation templateRepresentation, Map dataModel, MediaType mediaType) - throws ResourceNotFoundException, ParseErrorException, IOException { + throws ResourceNotFoundException, ParseErrorException { super(mediaType); setDataModel(dataModel); this.engine = null; @@ -208,7 +207,7 @@ public TemplateRepresentation( * @param mediaType The representation's media-type. */ public TemplateRepresentation(String templateName, MediaType mediaType) { - this(templateName, new ConcurrentHashMap(), mediaType); + this(templateName, new ConcurrentHashMap<>(), mediaType); } /** diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java index 50f00d76de..c4fd431cc9 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java @@ -11,6 +11,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.Writer; + +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactoryConfigurationError; + import org.restlet.data.CharacterSet; import org.restlet.data.MediaType; import org.restlet.representation.Representation; @@ -68,7 +72,7 @@ public DomRepresentation(MediaType mediaType, Document xmlDocument) { */ public DomRepresentation(Representation xmlRepresentation) { super((xmlRepresentation == null) ? null : xmlRepresentation.getMediaType()); - this.setAvailable((xmlRepresentation == null) ? false : xmlRepresentation.isAvailable()); + this.setAvailable(xmlRepresentation != null && xmlRepresentation.isAvailable()); this.xmlRepresentation = xmlRepresentation; } @@ -210,12 +214,8 @@ public void write(Writer writer) throws IOException { new javax.xml.transform.dom.DOMSource(getDocument()), new javax.xml.transform.stream.StreamResult(writer)); } - } catch (javax.xml.transform.TransformerConfigurationException tce) { - throw new IOException("Couldn't write the XML representation: " + tce.getMessage()); - } catch (javax.xml.transform.TransformerException te) { + } catch (TransformerException | TransformerFactoryConfigurationError te) { throw new IOException("Couldn't write the XML representation: " + te.getMessage()); - } catch (javax.xml.transform.TransformerFactoryConfigurationError tfce) { - throw new IOException("Couldn't write the XML representation: " + tfce.getMessage()); } } } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java index eafcc02705..53d7b5817a 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java @@ -44,7 +44,7 @@ public class SaxRepresentation extends XmlRepresentation { /** - * True for turning on secure parsing XML representations; default value provided by system + * True for turning on secure parsing XML representations; the default value provided by system * property "org.restlet.ext.xml.secureProcessing", true by default. */ public static final boolean XML_SECURE_PROCESSING = @@ -196,15 +196,9 @@ public void parse(ContentHandler contentHandler) throws IOException { try { Result result = new SAXResult(contentHandler); TransformerFactory.newInstance().newTransformer().transform(getSaxSource(), result); - } catch (TransformerConfigurationException tce) { - throw new IOException( - "Couldn't parse the source representation: " + tce.getMessage(), tce); - } catch (TransformerException te) { + } catch (TransformerException | TransformerFactoryConfigurationError te) { throw new IOException( "Couldn't parse the source representation: " + te.getMessage(), te); - } catch (TransformerFactoryConfigurationError tfce) { - throw new IOException( - "Couldn't parse the source representation: " + tfce.getMessage(), tfce); } } else { throw new IOException( @@ -250,7 +244,7 @@ public void write(Writer writer) throws IOException { } /** - * Writes the representation to a XML writer. The default implementation calls {@link + * Writes the representation to an XML writer. The default implementation calls {@link * #parse(ContentHandler)} using the {@link XmlWriter} parameter as the content handler. This * behavior is intended to be overridden. * diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java index ab7b3bf4e9..0959bfef03 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java @@ -65,8 +65,7 @@ public static SAXSource toSaxSource(Representation representation) throws IOExce if (representation instanceof XmlRepresentation) { result = ((XmlRepresentation) representation).getSaxSource(); - } else if (representation instanceof TransformRepresentation) { - final TransformRepresentation source = (TransformRepresentation) representation; + } else if (representation instanceof final TransformRepresentation source) { XMLReader reader = new AbstractXmlReader() { @@ -87,7 +86,7 @@ public void parse(InputSource input) throws IOException, SAXException { } } - public void parse(String systemId) throws IOException, SAXException { + public void parse(String systemId) { throw new IllegalStateException("Not implemented"); } }; @@ -178,8 +177,8 @@ private TransformRepresentation( this.templates = templates; this.transformSheet = transformSheet; this.uriResolver = uriResolver; - this.parameters = new HashMap(); - this.outputProperties = new HashMap(); + this.parameters = new HashMap<>(); + this.outputProperties = new HashMap<>(); this.errorListener = null; } @@ -196,7 +195,7 @@ public TransformRepresentation( } /** - * Returns the transformer's error listener. Default value is null, leaving the original + * Returns the transformer's error listener. The default value is null, leaving the original * listener intact. * * @return The transformer's error listener. diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java index cb35eab1d6..3c9c79b1cd 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java @@ -21,7 +21,7 @@ /** * Filter that can transform XML representations by applying an XSLT transform sheet. It uses the - * {@link org.restlet.representation.TransformRepresentation} to actually transform the XML + * {@link org.restlet.representation.Representation} to actually transform the XML * entities.
*
* Concurrency note: instances of this class or its subclasses can be invoked by several threads at @@ -125,7 +125,7 @@ public List getResultEncodings() { synchronized (this) { re = this.resultEncodings; if (re == null) { - this.resultEncodings = re = new CopyOnWriteArrayList(); + this.resultEncodings = re = new CopyOnWriteArrayList<>(); } } } @@ -144,7 +144,7 @@ public List getResultLanguages() { synchronized (this) { v = this.resultLanguages; if (v == null) { - this.resultLanguages = v = new CopyOnWriteArrayList(); + this.resultLanguages = v = new CopyOnWriteArrayList<>(); } } } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java index d96d86bdcf..d040f42b0f 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java @@ -549,7 +549,7 @@ private Object internalEval(String expression, javax.xml.namespace.QName returnT } /** - * Indicates if the parser should be coalescing text. If true the parser will convert CDATA + * Indicates if the parser should be coalescing text. If true, the parser will convert CDATA * nodes to text nodes and append it to the adjacent (if any) text node. By default, the value * of this is set to false. * @@ -570,7 +570,7 @@ public boolean isExpandingEntityRefs() { } /** - * Indicates if the parser will ignore comments. By default, the value of this is set to false. + * Indicates if the parser ignores comments. By default, the value of this is set to false. * * @return True if the parser will ignore comments. */ @@ -590,9 +590,9 @@ public boolean isIgnoringExtraWhitespaces() { } /** - * Indicates if processing is namespace aware. + * Indicates if processing is namespace-aware. * - * @return True if processing is namespace aware. + * @return True if processing is namespace-aware. */ public boolean isNamespaceAware() { return this.namespaceAware; @@ -629,7 +629,7 @@ public void release() { } /** - * Indicates if the parser should be coalescing text. If true the parser will convert CDATA + * Indicates if the parser should be coalescing text. If true, the parser will convert CDATA * nodes to text nodes and append it to the adjacent (if any) text node. By default, the value * of this is set to false. * @@ -669,7 +669,7 @@ public void setExpandingEntityRefs(boolean expandEntityRefs) { } /** - * Indicates if the parser will ignore comments. By default, the value of this is set to false. + * Indicates if the parser ignores comments. By default, the value of this is set to false. * * @param ignoringComments True if the parser will ignore comments. */ @@ -696,9 +696,9 @@ public void setIgnoringExtraWhitespaces(boolean ignoringExtraWhitespaces) { } /** - * Indicates if processing is namespace aware. + * Indicates if processing is namespace-aware. * - * @param namespaceAware Indicates if processing is namespace aware. + * @param namespaceAware Indicates if processing is namespace-aware. */ public void setNamespaceAware(boolean namespaceAware) { this.namespaceAware = namespaceAware; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java index c9d1311c97..9c36189cd3 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java @@ -29,7 +29,7 @@ /** * XML writer doing the opposite work of a SAX-based XML reader. The implementation is based on the - * work of David Megginson, the creator of SAX who placed the original code in the public domain. + * work of David Megginson, the creator of SAX, who placed the original code in the public domain. * *

This class can be used by itself or as part of a SAX event stream: it takes as input a series * of SAX2 ContentHandler events and uses the information in those events to write an XML document. @@ -39,7 +39,7 @@ * *

The client creates a document by invoking the methods for standard SAX2 events, always * beginning with the {@link #startDocument startDocument} method and ending with the {@link - * #endDocument endDocument} method. There are convenience methods provided so that clients to not + * #endDocument endDocument} method. There are convenience methods provided so that clients do not * have to create empty attribute lists or provide empty strings as parameters; for example, the * method invocation * @@ -106,7 +106,7 @@ * *

You need to invoke one of the characters methods explicitly to add newlines or * indentation. Alternatively, you can use the data format mode (set the "dataFormat" property) - * which is optimized for writing purely data-oriented (or field-oriented) XML, and does automatic + * which is optimized for writing purely data-oriented (or field-oriented) XML and does automatic * linebreaks and indentation (but does not support mixed content properly). See details below. * *

Namespace Support

@@ -192,9 +192,9 @@ * </rdf:RDF> * * - *

The "rdf" prefix is declared only once, because the RDF Namespace is used by the root element + *

The "rdf" prefix is declared only once because the RDF Namespace is used by the root element * and can be inherited by all of its descendants; the "dc" prefix, on the other hand, is declared - * three times, because no higher element uses the Namespace. To solve this problem, you can + * three times because no higher element uses the Namespace. To solve this problem, you can * instruct the XML writer to predeclare Namespaces on the root element even if they are not used * there: * @@ -234,7 +234,7 @@ * element contains either character data or other elements, but not both. For this special case, it * is possible for a writing tool to provide automatic indentation and newlines without requiring * extra work from the user. Note that this class will likely not yield appropriate results for - * document-oriented XML like XHTML pages, which mix character data and elements together. + * document-oriented XML like XHTML pages, which mix character data and elements. * *

This writer mode will automatically place each start tag on a new line, optionally indented if * an indent step is provided (by default, there is no indentation). If an element contains other @@ -382,7 +382,7 @@ public XmlWriter(XMLReader xmlreader) { /** * Create a new XML writer. * - *

Use the specified XML reader as the parent, and write to the specified writer. + *

Use the specified XML reader as the parent and write to the specified writer. * * @param xmlreader The parent in the filter chain, or null for no parent. * @param writer The output destination, or null to use standard output. @@ -427,7 +427,7 @@ private void characters(boolean dataFormat, char ch[], int start, int len) throw * @see #characters(char[], int, int) */ private void characters(boolean dataFormat, String data) throws SAXException { - final char ch[] = data.toCharArray(); + final char[] ch = data.toCharArray(); characters(dataFormat, ch, 0, ch.length); } @@ -568,7 +568,7 @@ private String doPrefix(String uri, String qName, boolean isElement) { return null; } String prefix; - if (isElement && (defaultNS != null) && uri.equals(defaultNS)) { + if (isElement && uri.equals(defaultNS)) { prefix = ""; } else { prefix = this.nsSupport.getPrefix(uri); @@ -616,7 +616,7 @@ private String doPrefix(String uri, String qName, boolean isElement) { // ////////////////////////////////////////////////////////////////// /** - * Add an empty element without a Namespace URI, qname or attributes. + * Add an empty element without a Namespace URI, qname, or attributes. * *

This method will supply an empty string for the qname, and empty string for the Namespace * URI, and an empty attribute list. It invokes {@link #emptyElement(String, String, String, @@ -802,7 +802,7 @@ public void flush() throws IOException { * element. * *

This method forces a Namespace to be declared on the root element even if it is not used - * there, and reduces the number of xmlns attributes in the document. + * there and reduces the number of xmlns attributes in the document. * * @param uri The Namespace URI to declare. * @see #forceNSDecl(java.lang.String,java.lang.String) @@ -893,16 +893,16 @@ public void ignorableWhitespace(char ch[], int start, int length) throws SAXExce /** * Internal initialization method. * - *

All of the public constructors invoke this method. + *

All the public constructors invoke this method. * * @param writer The output destination, or null to use standard output. */ private void init(Writer writer) { setOutput(writer); this.nsSupport = new NamespaceSupport(); - this.prefixTable = new ConcurrentHashMap(); - this.forcedDeclTable = new ConcurrentHashMap(); - this.doneDeclTable = new ConcurrentHashMap(); + this.prefixTable = new ConcurrentHashMap<>(); + this.forcedDeclTable = new ConcurrentHashMap<>(); + this.doneDeclTable = new ConcurrentHashMap<>(); } public boolean isDataFormat() { @@ -951,7 +951,7 @@ public void reset() { if (isDataFormat()) { this.depth = 0; this.state = SEEN_NOTHING; - this.stateStack = new Stack(); + this.stateStack = new Stack<>(); } this.elementLevel = 0; @@ -993,7 +993,7 @@ public void setOutput(Writer writer) { /** * Specify a preferred prefix for a Namespace URI. * - *

Note that this method does not actually force the Namespace to be declared; to do that, + *

Note that this method does not force the Namespace to be declared; to do that, * use the {@link #forceNSDecl(java.lang.String) forceNSDecl} method as well. * * @param uri The Namespace URI. @@ -1022,7 +1022,7 @@ public void startDocument() throws SAXException { } /** - * Start a new element without a qname, attributes or a Namespace URI. + * Start a new element without a qname, attributes, or a Namespace URI. * *

This method will provide an empty string for the Namespace URI, and empty string for the * qualified name, and a default empty attribute list. It invokes #startElement(String, String, @@ -1129,23 +1129,23 @@ private void write(String s) throws SAXException { /** * Write out an attribute list, escaping values. The names will have prefixes added to them. * - * @param atts The attribute list to write. + * @param attributes The attribute list to write. * @exception org.xml.SAXException If there is an error writing the attribute list, this method * will throw an IOException wrapped in a SAXException. */ - private void writeAttributes(Attributes atts) throws SAXException { - final int len = atts.getLength(); + private void writeAttributes(Attributes attributes) throws SAXException { + final int len = attributes.getLength(); for (int i = 0; i < len; i++) { - if ("xmlns".equals(atts.getQName(i))) { + if ("xmlns".equals(attributes.getQName(i))) { // Redefines the default namespace. - forceNSDecl(atts.getValue(i)); - } else if (atts.getQName(i) != null && atts.getQName(i).startsWith("xmlns")) { + forceNSDecl(attributes.getValue(i)); + } else if (attributes.getQName(i) != null && attributes.getQName(i).startsWith("xmlns")) { // Defines the namespace using its prefix. - forceNSDecl(atts.getValue(i), atts.getLocalName(i)); + forceNSDecl(attributes.getValue(i), attributes.getLocalName(i)); } else { - final char ch[] = atts.getValue(i).toCharArray(); + final char[] ch = attributes.getValue(i).toCharArray(); write(' '); - writeName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i), false); + writeName(attributes.getURI(i), attributes.getLocalName(i), attributes.getQName(i), false); write("=\""); writeEsc(ch, 0, ch.length, true); write('"'); @@ -1163,7 +1163,7 @@ private void writeAttributes(Attributes atts) throws SAXException { * @exception org.xml.SAXException If there is an error writing the characters, this method will * throw an IOException wrapped in a SAXException. */ - private void writeEsc(char ch[], int start, int length, boolean isAttVal) throws SAXException { + private void writeEsc(char[] ch, int start, int length, boolean isAttVal) throws SAXException { for (int i = start; i < start + length; i++) { switch (ch[i]) { case '&': diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java index 928c2f5d06..41ea230b5b 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java @@ -44,8 +44,8 @@ public abstract class AbstractXmlReader implements XMLReader { /** Default constructor. */ public AbstractXmlReader() { - this.features = new HashMap(); - this.properties = new HashMap(); + this.features = new HashMap<>(); + this.properties = new HashMap<>(); this.contentHandler = null; this.entityResolver = null; this.errorHandler = null; @@ -99,10 +99,9 @@ public ErrorHandler getErrorHandler() { * @return The feature. * @see XMLReader#getFeature(String) */ - public boolean getFeature(String name) - throws SAXNotRecognizedException, SAXNotSupportedException { + public boolean getFeature(String name) { final Boolean result = features.get(name); - return result == null ? false : result.booleanValue(); + return result != null && result; } /** @@ -112,8 +111,7 @@ public boolean getFeature(String name) * @return The property. * @see XMLReader#getProperty(String) */ - public Object getProperty(String name) - throws SAXNotRecognizedException, SAXNotSupportedException { + public Object getProperty(String name) { return properties.get(name); } @@ -164,8 +162,7 @@ public void setErrorHandler(ErrorHandler errorHandler) { * @param value The feature value. * @see XMLReader#setFeature(String, boolean) */ - public void setFeature(String name, boolean value) - throws SAXNotRecognizedException, SAXNotSupportedException { + public void setFeature(String name, boolean value) { this.features.put(name, value); } @@ -176,8 +173,7 @@ public void setFeature(String name, boolean value) * @param value The property value. * @see XMLReader#setProperty(String, Object) */ - public void setProperty(String name, Object value) - throws SAXNotRecognizedException, SAXNotSupportedException { + public void setProperty(String name, Object value) { this.properties.put(name, value); } } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java index c74217ce5a..da827c8b77 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java @@ -43,7 +43,7 @@ public ContextResolver(Context context) { * * @see javax.xml.transform.URIResolver#resolve(java.lang.String, java.lang.String) */ - public Source resolve(String href, String base) throws TransformerException { + public Source resolve(String href, String base) { Source result = null; if (this.context != null) { diff --git a/pom.xml b/pom.xml index 6969ca277c..f047249b33 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ org.restlet.ext.jaas org.restlet.ext.jackson org.restlet.ext.json + org.restlet.ext.openapi org.restlet.ext.slf4j org.restlet.ext.spring org.restlet.ext.thymeleaf From 1c380f1a8f0036c894a06f0b172e3d8dcccbb235 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 29 Mar 2026 23:22:29 +0200 Subject: [PATCH 03/30] Add spotless plugin and fix grammar issues --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index f047249b33..c9503bd4ba 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,8 @@ 4.3.1 2.0.17 6.2.8 + 2.2.43 + 2.1.38 3.1.3.RELEASE 2.4.1 From 6d3e409f6b40de39bbf7ef30a6d249944ef08be8 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 29 Mar 2026 23:28:28 +0200 Subject: [PATCH 04/30] Add spotless plugin and fix grammar issues --- .../src/main/java/org/restlet/ext/xml/SaxRepresentation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java index 53d7b5817a..92e8883cae 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java @@ -135,8 +135,8 @@ public InputSource getInputSource() throws IOException { @Override public SAXSource getSaxSource() throws IOException { if (this.source == null && this.xmlRepresentation != null) { - if (xmlRepresentation instanceof XmlRepresentation) { - this.source = ((XmlRepresentation) xmlRepresentation).getSaxSource(); + if (xmlRepresentation instanceof XmlRepresentation xmlRepresentationCast) { + this.source = xmlRepresentationCast.getSaxSource(); } else { try { SAXParserFactory spf = SAXParserFactory.newInstance(); From 27dd767ac770d29ebdd9f35fe8a9601bf2874d8d Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Fri, 3 Apr 2026 17:44:20 +0200 Subject: [PATCH 05/30] Take into account Sonar comments --- .../org/restlet/ext/crypto/DigestUtils.java | 10 +- .../restlet/ext/crypto/DigestVerifier.java | 4 +- .../restlet/ext/crypto/internal/AwsUtils.java | 16 +- .../ext/crypto/internal/AwsVerifier.java | 31 +-- .../ext/crypto/internal/CryptoUtils.java | 11 +- .../internal/HttpAzureSharedKeyHelper.java | 22 +- .../HttpAzureSharedKeyLiteHelper.java | 5 +- .../ext/freemarker/FreemarkerConverter.java | 1 - .../ext/freemarker/TemplateFilter.java | 6 +- .../ext/jackson/JacksonRepresentation.java | 12 -- .../jackson/internal/XmlFactoryProvider.java | 32 ++- .../org/restlet/ext/json/JsonpFilter.java | 6 +- .../ext/openapi/OpenApiApplication.java | 26 +-- .../openapi/OpenApiSpecificationRestlet.java | 59 +++--- .../internal/OpenApiAnnotationProcessor.java | 92 ++++----- .../internal/OpenApiApplicationException.java | 9 +- .../ext/openapi/internal/Operations.java | 25 +-- .../ext/openapi/internal/PathItems.java | 21 +- .../internal/RestletOpenApiContext.java | 24 ++- .../RestletOpenApiContextBuilder.java | 34 ++-- .../internal/RestletOpenApiReader.java | 188 ++++++++---------- .../restlet/ext/openapi/LibraryExample.java | 70 +++---- .../ext/openapi/OpenApiGenerationTest.java | 31 +-- .../ext/openapi/OpenApiSpecifications.java | 22 +- .../restlet/ext/spring/SpringBeanRouter.java | 4 +- .../RepresentationResourceLoader.java | 4 +- .../restlet/ext/velocity/TemplateFilter.java | 8 +- .../restlet/ext/xml/DomRepresentation.java | 2 - .../restlet/ext/xml/SaxRepresentation.java | 1 - .../java/org/restlet/ext/xml/Transformer.java | 3 +- .../java/org/restlet/ext/xml/XmlWriter.java | 20 +- .../ext/xml/internal/AbstractXmlReader.java | 2 - .../ext/xml/internal/ContextResolver.java | 1 - .../main/java/org/restlet/Application.java | 16 +- .../src/main/java/org/restlet/Context.java | 14 +- .../src/main/java/org/restlet/Message.java | 6 +- .../src/main/java/org/restlet/Request.java | 12 +- .../src/main/java/org/restlet/Response.java | 18 +- .../src/main/java/org/restlet/Restlet.java | 4 +- .../main/java/org/restlet/security/Role.java | 4 +- .../java/org/restlet/service/CorsService.java | 7 +- .../org/restlet/service/EncoderService.java | 8 +- .../java/org/restlet/service/LogService.java | 5 +- .../org/restlet/service/MetadataService.java | 4 +- .../main/java/org/restlet/util/Resolver.java | 6 +- .../java/org/restlet/util/WrapperMap.java | 8 +- .../restlet/util/WrapperRepresentation.java | 4 +- .../java/org/restlet/util/WrapperRequest.java | 6 +- .../org/restlet/util/WrapperResponse.java | 9 +- 49 files changed, 441 insertions(+), 492 deletions(-) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java index 8f1236b389..bb5a41eb17 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java @@ -189,9 +189,10 @@ public static String toMd5(String target) { } /** - * Returns the MD5 digest of the target string. Target is decoded to bytes using the named charset. - * The returned hexadecimal String always contains 32 lowercase alphanumeric characters. For - * example, if the target is "HelloWorld", this method returns "68e109f0f40ca72a15e05cc22786f8e6". + * Returns the MD5 digest of the target string. Target is decoded to bytes using the named + * charset. The returned hexadecimal String always contains 32 lowercase alphanumeric + * characters. For example, if the target is "HelloWorld", this method returns + * "68e109f0f40ca72a15e05cc22786f8e6". * * @param target The string to encode. * @param charsetName The character set. @@ -233,7 +234,8 @@ public static String toSha1(String target) { } /** - * Returns the SHA1 digest of the target string. Target is decoded to bytes using the named charset. + * Returns the SHA1 digest of the target string. Target is decoded to bytes using the named + * charset. * * @param target The string to encode. * @param charsetName The character set. diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java index 6a53821371..a411cd9724 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java @@ -53,8 +53,8 @@ public DigestVerifier(String algorithm, T wrappedVerifier, String wrappedAlgorit /** * Computes the digest of a secret according to a specified algorithm. By default, MD5 hashes - * (represented as a sequence of 32 hexadecimal digits) and SHA-1 hashes are supported. For - * an additional algorithm, override this method. + * (represented as a sequence of 32 hexadecimal digits) and SHA-1 hashes are supported. For an + * additional algorithm, override this method. * * @param identifier The user identifier. * @param secret The regular secret to digest. diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java index 78dd580ea3..be42c85cc7 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java @@ -306,11 +306,15 @@ public static String getS3StringToSign(Request request, Series

headers) contentType = "application/x-www-form-urlencoded"; } - return (method != null ? method : "") + "\n" + - (contentMD5 != null ? contentMD5 : "") + "\n" + - (contentType != null ? contentType : "") + "\n" + - (date != null ? date : "") + "\n" + - (canonicalizedAmzHeaders != null ? canonicalizedAmzHeaders : "") + - (canonicalizedResource != null ? canonicalizedResource : ""); + return (method != null ? method : "") + + "\n" + + (contentMD5 != null ? contentMD5 : "") + + "\n" + + (contentType != null ? contentType : "") + + "\n" + + (date != null ? date : "") + + "\n" + + (canonicalizedAmzHeaders != null ? canonicalizedAmzHeaders : "") + + (canonicalizedResource != null ? canonicalizedResource : ""); } } diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java index c06cb21898..d75e94a8a5 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.ext.crypto.internal; @@ -29,7 +29,7 @@ * * @author Jean-Philippe Steinmetz * @see - * Authenticating REST Requests + * Authenticating REST Requests */ public class AwsVerifier extends SecretVerifier { /** Default maximum request age (15 minutes) */ @@ -44,8 +44,7 @@ public class AwsVerifier extends SecretVerifier { /** * Creates a new HttpAwsS3Verifier instance. * - * @param wrappedVerifier - * The wrapped verifier containing local identifier/secret couples + * @param wrappedVerifier The wrapped verifier containing local identifier/secret couples */ public AwsVerifier(LocalVerifier wrappedVerifier) { this(wrappedVerifier, DEFAULT_MAX_REQUEST_AGE); @@ -54,11 +53,9 @@ public AwsVerifier(LocalVerifier wrappedVerifier) { /** * Creates a new HttpAwsS3Verifier instance. * - * @param wrappedVerifier - * The wrapped verifier containing local identifier/secret couples - * @param maxRequestAge - * The maximum age of a request, in milliseconds, before it is considered - * stale + * @param wrappedVerifier The wrapped verifier containing local identifier/secret couples + * @param maxRequestAge The maximum age of a request, in milliseconds, before it is considered + * stale */ public AwsVerifier(LocalVerifier wrappedVerifier, long maxRequestAge) { super(); @@ -79,16 +76,13 @@ protected String getIdentifier(Request request, Response response) { String[] parts = request.getChallengeResponse().getRawValue().split(":"); - return (parts.length == 2) - ? parts[0] - : null; + return (parts.length == 2) ? parts[0] : null; } /** * Returns the local secret associated with a given identifier. * - * @param identifier - * The identifier to lookup. + * @param identifier The identifier to lookup. * @return The secret associated with the identifier or null. */ public char[] getLocalSecret(String identifier) { @@ -119,9 +113,7 @@ protected char[] getSecret(Request request, Response response) { String[] parts = request.getChallengeResponse().getRawValue().split(":"); - return (parts.length == 2) - ? parts[1].toCharArray() - : null; + return (parts.length == 2) ? parts[1].toCharArray() : null; } /** @@ -146,8 +138,7 @@ public void setMaxRequestAge(long value) { /** * Sets the wrapped local secret verifier. * - * @param wrappedVerifier - * The local secret verifier. + * @param wrappedVerifier The local secret verifier. */ public void setWrappedVerifier(LocalVerifier wrappedVerifier) { this.wrappedVerifier = wrappedVerifier; diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java index 5be882ae6c..9684a6a7e0 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java @@ -1,21 +1,18 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.ext.crypto.internal; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Base64; - import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; - import org.restlet.ext.crypto.DigestUtils; /** @@ -118,8 +115,8 @@ public static byte[] encrypt(String algo, String base64Secret, String content) } /** - * Generates nonce as recommended in section 3.2.1 of RFC-2617, but without the ETag field. - * The format is:

+     * Generates nonce as recommended in section 3.2.1 of RFC-2617, but without the ETag field. The
+     * format is: 
      * Base64.encodeBytes(currentTimeMS + ":"
      *         + md5String(currentTimeMS + ":" + secretKey))
      * 
diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java index b200481872..017099b390 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java @@ -10,7 +10,6 @@ import java.util.Base64; import java.util.Date; -import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; import org.restlet.Request; @@ -158,14 +157,19 @@ public void formatResponse( final String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); // Set up the message part - final String rest = methodName + '\n' + - contentMd5 + '\n' + - contentType + '\n' + - date + '\n' + - canonicalizedAzureHeaders + - '/' + - challenge.getIdentifier() + - canonicalizedResource; + final String rest = + methodName + + '\n' + + contentMd5 + + '\n' + + contentType + + '\n' + + date + + '\n' + + canonicalizedAzureHeaders + + '/' + + challenge.getIdentifier() + + canonicalizedResource; // Append the SharedKey credentials cw.append(challenge.getIdentifier()) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java index e7c0edf7de..fc55fcebda 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java @@ -85,10 +85,7 @@ public void formatResponse( String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); // Set up the message part - final String rest = date + '\n' + - '/' + - challenge.getIdentifier() + - canonicalizedResource; + final String rest = date + '\n' + '/' + challenge.getIdentifier() + canonicalizedResource; // Append the SharedKey credentials cw.append(challenge.getIdentifier()) diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java index 03eda3590b..610a9dd96a 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java @@ -9,7 +9,6 @@ package org.restlet.ext.freemarker; import freemarker.template.Template; -import java.io.IOException; import java.util.List; import org.restlet.engine.converter.ConverterHelper; import org.restlet.engine.resource.VariantInfo; diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java index ac487d13cf..287477a760 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java @@ -20,9 +20,9 @@ import org.restlet.util.Resolver; /** - * Filter the response's entity and wrap it with a FreeMarker's template representation. By default, the - * template representation provides a data model based on the request and response objects. In order - * for the wrapping to happen, the representations must have the {@link Encoding#FREEMARKER} + * Filter the response's entity and wrap it with a FreeMarker's template representation. By default, + * the template representation provides a data model based on the request and response objects. In + * order for the wrapping to happen, the representations must have the {@link Encoding#FREEMARKER} * encoding set.
*
* Concurrency note: instances of this class or its subclasses can be invoked by several threads at diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java index 54be58860c..2a02815499 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.OutputStream; import org.restlet.data.MediaType; -import org.restlet.engine.Edition; import org.restlet.ext.jackson.internal.XmlFactoryProvider; import org.restlet.representation.OutputRepresentation; import org.restlet.representation.Representation; @@ -171,17 +170,6 @@ protected ObjectMapper createObjectMapper() { } else if (MediaType.APPLICATION_XML.isCompatible(getMediaType()) || MediaType.TEXT_XML.isCompatible(getMediaType())) { - if (Edition.ANDROID.isCurrentEdition() - && XmlFactoryProvider.inputFactoryProvider == null) { - XmlFactoryProvider.inputFactoryProvider = - new com.ctc.wstx.osgi.InputFactoryProviderImpl(); - } - if (Edition.ANDROID.isCurrentEdition() - && XmlFactoryProvider.outputFactoryProvider == null) { - XmlFactoryProvider.outputFactoryProvider = - new com.ctc.wstx.osgi.OutputFactoryProviderImpl(); - } - javax.xml.stream.XMLInputFactory xif = XmlFactoryProvider.newInputFactory(); xif.setProperty( javax.xml.stream.XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java index 05b9a453c0..3a5ad836ba 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java @@ -12,6 +12,7 @@ import javax.xml.stream.XMLOutputFactory; import org.codehaus.stax2.osgi.Stax2InputFactoryProvider; import org.codehaus.stax2.osgi.Stax2OutputFactoryProvider; +import org.restlet.engine.Edition; /** * Provides {@link javax.xml.stream.XMLInputFactory} and {@link javax.xml.stream.XMLOutputFactory} @@ -24,8 +25,8 @@ public class XmlFactoryProvider { /** - * Allow explicitly setting the Stax2InputFactory instance in OSGI context. In a no-OSGI context, - * the factory is retrieved with a java service loader. + * Allow explicitly setting the Stax2InputFactory instance in OSGI context. In a no-OSGI + * context, the factory is retrieved with a java service loader. * *

Note: Stax2 implementation is provided by woodstox library, which is a dependency of * Jackson. @@ -33,8 +34,8 @@ public class XmlFactoryProvider { public static Stax2InputFactoryProvider inputFactoryProvider = null; /** - * Allow explicitly setting the Stax2OutputFactoryProvider instance in OSGI context. In a no-OSGI - * context, the factory is retrieved with a java service loader. + * Allow explicitly setting the Stax2OutputFactoryProvider instance in OSGI context. In a + * no-OSGI context, the factory is retrieved with a java service loader. * *

Note: Stax2 implementation is provided by woodstox library, which is a dependency of * Jackson. @@ -45,17 +46,28 @@ public class XmlFactoryProvider { * Returns an instance of {@link javax.xml.stream.XMLInputFactory} according to the classpath. */ public static XMLInputFactory newInputFactory() { - return inputFactoryProvider != null - ? inputFactoryProvider.createInputFactory() - : XMLInputFactory.newFactory(); + if (inputFactoryProvider == null) { + if (Edition.ANDROID.isCurrentEdition()) { + inputFactoryProvider = new com.ctc.wstx.osgi.InputFactoryProviderImpl(); + } else { + return XMLInputFactory.newFactory(); + } + } + return inputFactoryProvider.createInputFactory(); } /** * Returns an instance of {@link javax.xml.stream.XMLInputFactory} according to the classpath. */ public static XMLOutputFactory newOutputFactory() { - return outputFactoryProvider != null - ? outputFactoryProvider.createOutputFactory() - : XMLOutputFactory.newFactory(); + if (outputFactoryProvider == null) { + if (Edition.ANDROID.isCurrentEdition()) { + outputFactoryProvider = new com.ctc.wstx.osgi.OutputFactoryProviderImpl(); + } else { + return XMLOutputFactory.newFactory(); + } + } + + return outputFactoryProvider.createOutputFactory(); } } diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java index 953e5efe4b..58d71d5860 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java @@ -17,9 +17,9 @@ import org.restlet.routing.Filter; /** - * Filter that converts the response entity of the JSON media type into a JSONP callback document. Make - * sure that you properly pass a "callback" query parameter in the URI query string with the name of - * your JavaScript callback method. + * Filter that converts the response entity of the JSON media type into a JSONP callback document. + * Make sure that you properly pass a "callback" query parameter in the URI query string with the + * name of your JavaScript callback method. * *

See {@link JsonpRepresentation} for the actual wrapper representation used internally. * diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiApplication.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiApplication.java index 5e1713cfdd..90c2a58553 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiApplication.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiApplication.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open source license available at - * http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi; import org.restlet.Application; @@ -16,14 +15,12 @@ public class OpenApiApplication extends Application { /** - * Default path for the OpenAPI specification. Can be overridden by overriding the - * {@link #getOpenApiSpecificationPath()} method. + * Default path for the OpenAPI specification. Can be overridden by overriding the {@link + * #getOpenApiSpecificationPath()} method. */ static final String OPENAPI_SPECIFICATION_DEFAULT_PATH = "/openapi"; - /** - * Indicates if this application has already been documented or not. - */ + /** Indicates if this application has already been documented or not. */ private volatile boolean documented; @Override @@ -39,7 +36,6 @@ public Restlet getInboundRoot() { attachOpenApiSpecificationRestlet(rootRouter); documented = true; } - } } } @@ -47,9 +43,7 @@ public Restlet getInboundRoot() { return inboundRoot; } - /** - * Path where the OpenAPI specification will be available. By default, it is "/openapi". - */ + /** Path where the OpenAPI specification will be available. By default, it is "/openapi". */ protected String getOpenApiSpecificationPath() { return OPENAPI_SPECIFICATION_DEFAULT_PATH; } @@ -82,9 +76,7 @@ private void attachOpenApiSpecificationRestlet(Router router) { * * @return The {@link Restlet} able to generate the Swagger specification formats. */ - OpenApiSpecificationRestlet getOpenApiSpecificationRestlet( - Router router - ) { + OpenApiSpecificationRestlet getOpenApiSpecificationRestlet(Router router) { return new OpenApiSpecificationRestlet(router); } } diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiSpecificationRestlet.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiSpecificationRestlet.java index da171b7c86..bf3039f0c7 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiSpecificationRestlet.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/OpenApiSpecificationRestlet.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open source license available at - * http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi; import com.fasterxml.jackson.core.JsonProcessingException; @@ -14,6 +13,7 @@ import io.swagger.v3.oas.integration.OpenApiConfigurationException; import io.swagger.v3.oas.integration.SwaggerConfiguration; import io.swagger.v3.oas.integration.api.OpenAPIConfiguration; +import java.util.List; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; @@ -28,16 +28,11 @@ import org.restlet.representation.Variant; import org.restlet.routing.Router; -import java.util.List; - public class OpenApiSpecificationRestlet extends Restlet { - private static final VariantInfo VARIANT_JSON = new VariantInfo( - MediaType.APPLICATION_JSON - ); + private static final VariantInfo VARIANT_JSON = new VariantInfo(MediaType.APPLICATION_JSON); - private static final VariantInfo VARIANT_APPLICATION_YAML = new VariantInfo( - MediaType.APPLICATION_YAML - ); + private static final VariantInfo VARIANT_APPLICATION_YAML = + new VariantInfo(MediaType.APPLICATION_YAML); private final Router router; @@ -60,35 +55,35 @@ public void handle(Request request, Response response) { private Representation getOpenApiDefinitionAsRepresentation(Request request) { List allowedVariants = List.of(VARIANT_APPLICATION_YAML, VARIANT_JSON); - Variant preferredVariant = getApplication() - .getConnegService() - .getPreferredVariant( - allowedVariants, - request, - getApplication().getMetadataService() - ); + Variant preferredVariant = + getApplication() + .getConnegService() + .getPreferredVariant( + allowedVariants, request, getApplication().getMetadataService()); - OpenAPIConfiguration oasConfig = new SwaggerConfiguration() - .prettyPrint(true); + OpenAPIConfiguration oasConfig = new SwaggerConfiguration().prettyPrint(true); try { - var context = new RestletOpenApiContextBuilder() - .router(router) - .openApiConfiguration(oasConfig) - .buildContext(true); + var context = + new RestletOpenApiContextBuilder() + .router(router) + .openApiConfiguration(oasConfig) + .buildContext(true); var openApiRead = context.read(); if (VARIANT_JSON.isCompatible(preferredVariant)) { - var openApiAsJson = context.getOutputJsonMapper() - .writer(new DefaultPrettyPrinter()) - .writeValueAsString(openApiRead); + var openApiAsJson = + context.getOutputJsonMapper() + .writer(new DefaultPrettyPrinter()) + .writeValueAsString(openApiRead); return new StringRepresentation(openApiAsJson, MediaType.APPLICATION_JSON); } else { - var openApiAsYaml = context.getOutputYamlMapper() - .writer(new DefaultPrettyPrinter()) - .writeValueAsString(openApiRead); + var openApiAsYaml = + context.getOutputYamlMapper() + .writer(new DefaultPrettyPrinter()) + .writeValueAsString(openApiRead); return new StringRepresentation(openApiAsYaml, MediaType.APPLICATION_YAML); } diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiAnnotationProcessor.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiAnnotationProcessor.java index 21070c20a7..ff5bc45f67 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiAnnotationProcessor.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiAnnotationProcessor.java @@ -1,12 +1,11 @@ -/* - * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * - * Restlet is a registered trademark of QlikTech International AB. +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi.internal; import static org.restlet.engine.util.StringUtils.isNullOrEmpty; @@ -18,39 +17,36 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.parameters.Parameter; - import java.util.Optional; public class OpenApiAnnotationProcessor { - private OpenApiAnnotationProcessor () {} // utility class + private OpenApiAnnotationProcessor() {} // utility class public static void documentOpenApiDefinition( - OpenAPI openAPIDefinition, - OpenAPIDefinition openAPIDefinitionAnnotation - ) { + OpenAPI openAPIDefinition, OpenAPIDefinition openAPIDefinitionAnnotation) { if (openAPIDefinitionAnnotation.info() != null) { AnnotationsUtils.getInfo(openAPIDefinitionAnnotation.info()) - .ifPresent(openAPIDefinition::setInfo); + .ifPresent(openAPIDefinition::setInfo); } } public static void documentOperation( - Operation operation, - io.swagger.v3.oas.annotations.Operation operationAnnotation - ) { + Operation operation, io.swagger.v3.oas.annotations.Operation operationAnnotation) { if (operationAnnotation.summary() != null && !operationAnnotation.summary().isEmpty()) { operation.setSummary(operationAnnotation.summary()); } - if (operationAnnotation.description() != null && !operationAnnotation.description().isEmpty()) { + if (operationAnnotation.description() != null + && !operationAnnotation.description().isEmpty()) { operation.setDescription(operationAnnotation.description()); } if (operationAnnotation.parameters() != null) { - for (io.swagger.v3.oas.annotations.Parameter parameterAnnotation : operationAnnotation.parameters()) { + for (io.swagger.v3.oas.annotations.Parameter parameterAnnotation : + operationAnnotation.parameters()) { resolveParameterFromAnnotation(parameterAnnotation) - .ifPresent(operation::addParametersItem); + .ifPresent(operation::addParametersItem); } } @@ -62,50 +58,45 @@ public static void documentOperation( } public static void documentOperationResponse( - Operation operation, - ApiResponse apiResponseAnnotation - ) { + Operation operation, ApiResponse apiResponseAnnotation) { var defaultDescription = apiResponseAnnotation.responseCode() + " response"; io.swagger.v3.oas.models.responses.ApiResponse apiResponse = - new io.swagger.v3.oas.models.responses.ApiResponse() - .description(isNullOrEmpty(apiResponseAnnotation.description()) - ? defaultDescription - : apiResponseAnnotation.description() - ); + new io.swagger.v3.oas.models.responses.ApiResponse() + .description( + isNullOrEmpty(apiResponseAnnotation.description()) + ? defaultDescription + : apiResponseAnnotation.description()); if (!isContentEmpty(apiResponseAnnotation)) { AnnotationsUtils.getContent( - apiResponseAnnotation.content(), - null, - null, - null, - null, - null - ).ifPresent(apiResponse::content); + apiResponseAnnotation.content(), null, null, null, null, null) + .ifPresent(apiResponse::content); } AnnotationsUtils.getHeaders(apiResponseAnnotation.headers(), null) - .ifPresent(apiResponse::headers); + .ifPresent(apiResponse::headers); Operations.addApiResponse(operation, apiResponseAnnotation.responseCode(), apiResponse); } - private static Optional resolveParameterFromAnnotation(io.swagger.v3.oas.annotations.Parameter parameterAnnotation) { + private static Optional resolveParameterFromAnnotation( + io.swagger.v3.oas.annotations.Parameter parameterAnnotation) { return switch (parameterAnnotation.in()) { case DEFAULT, PATH -> Optional.empty(); case COOKIE, HEADER, QUERY -> { - Parameter parameter = new Parameter() - .name(parameterAnnotation.name()) - .description(isNullOrEmpty(parameterAnnotation.description()) - ? null - : parameterAnnotation.description() - ) - .in(parameterAnnotation.in().name().toLowerCase()) - .required(parameterAnnotation.required()); + Parameter parameter = + new Parameter() + .name(parameterAnnotation.name()) + .description( + isNullOrEmpty(parameterAnnotation.description()) + ? null + : parameterAnnotation.description()) + .in(parameterAnnotation.in().name().toLowerCase()) + .required(parameterAnnotation.required()); AnnotationsUtils.getSchemaFromAnnotation(parameterAnnotation.schema(), null) - .ifPresent(parameter::schema); + .ifPresent(parameter::schema); yield Optional.of(parameter); } @@ -113,14 +104,15 @@ private static Optional resolveParameterFromAnnotation(io.swagger.v3. } private static boolean isContentEmpty(ApiResponse apiResponseAnnotation) { - if (apiResponseAnnotation.content() == null || apiResponseAnnotation.content().length == 0) { + if (apiResponseAnnotation.content() == null + || apiResponseAnnotation.content().length == 0) { return true; } Content content = apiResponseAnnotation.content()[0]; - return content.mediaType().isEmpty() && - content.schema().implementation() == Void.class && - content.schema().ref().isEmpty(); + return content.mediaType().isEmpty() + && content.schema().implementation() == Void.class + && content.schema().ref().isEmpty(); } } diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiApplicationException.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiApplicationException.java index e73243409e..99cb35c65c 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiApplicationException.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/OpenApiApplicationException.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open source license available at - * http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi.internal; import org.restlet.resource.Status; diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/Operations.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/Operations.java index 2f3fcadc44..15ab1ddd72 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/Operations.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/Operations.java @@ -1,12 +1,11 @@ -/* - * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * - * Restlet is a registered trademark of QlikTech International AB. +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi.internal; import io.swagger.v3.oas.models.Operation; @@ -14,14 +13,12 @@ import io.swagger.v3.oas.models.responses.ApiResponses; public class Operations { - private Operations() { - } + private Operations() {} - public static void addApiResponse(Operation operation, String apiResponseName, ApiResponse apiResponse) { + public static void addApiResponse( + Operation operation, String apiResponseName, ApiResponse apiResponse) { if (operation.getResponses() == null) { - operation.responses( - new ApiResponses().addApiResponse(apiResponseName, apiResponse) - ); + operation.responses(new ApiResponses().addApiResponse(apiResponseName, apiResponse)); } else { operation.getResponses().addApiResponse(apiResponseName, apiResponse); } diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/PathItems.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/PathItems.java index cfe7048c47..2288afe302 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/PathItems.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/PathItems.java @@ -1,12 +1,11 @@ -/* - * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * - * Restlet is a registered trademark of QlikTech International AB. +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi.internal; import io.swagger.v3.oas.models.Operation; @@ -14,8 +13,7 @@ import org.restlet.data.Method; public class PathItems { - private PathItems() { - } + private PathItems() {} public static void setOperation(PathItem pathItem, Method restletMethod, Operation operation) { if (restletMethod.equals(Method.POST)) { @@ -31,7 +29,8 @@ public static void setOperation(PathItem pathItem, Method restletMethod, Operati } else if (restletMethod.equals(Method.OPTIONS)) { pathItem.setOptions(operation); } else { - throw new IllegalArgumentException("Unsupported Restlet Method: " + restletMethod.getName()); + throw new IllegalArgumentException( + "Unsupported Restlet Method: " + restletMethod.getName()); } } } diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContext.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContext.java index 0e58dae99f..8391f48230 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContext.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContext.java @@ -1,12 +1,11 @@ -/* - * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * - * Restlet is a registered trademark of QlikTech International AB. +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi.internal; import io.swagger.v3.oas.integration.GenericOpenApiContext; @@ -16,7 +15,8 @@ import org.apache.commons.lang3.StringUtils; import org.restlet.routing.Router; -public class RestletOpenApiContext extends GenericOpenApiContext implements OpenApiContext { +public class RestletOpenApiContext extends GenericOpenApiContext + implements OpenApiContext { private final Router router; public RestletOpenApiContext(Router router) { @@ -24,11 +24,13 @@ public RestletOpenApiContext(Router router) { } @Override - protected OpenApiReader buildReader(OpenAPIConfiguration openApiConfiguration) throws Exception { + protected OpenApiReader buildReader(OpenAPIConfiguration openApiConfiguration) + throws Exception { OpenApiReader reader; if (StringUtils.isNotBlank(openApiConfiguration.getReaderClass())) { - Class cls = getClass().getClassLoader().loadClass(openApiConfiguration.getReaderClass()); + Class cls = + getClass().getClassLoader().loadClass(openApiConfiguration.getReaderClass()); reader = (OpenApiReader) cls.getDeclaredConstructor().newInstance(); } else { reader = new RestletOpenApiReader(); diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContextBuilder.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContextBuilder.java index e48379bad6..1fbe5ca200 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContextBuilder.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiContextBuilder.java @@ -1,12 +1,11 @@ -/* - * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * - * Restlet is a registered trademark of QlikTech International AB. +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi.internal; import io.swagger.v3.oas.integration.GenericOpenApiContextBuilder; @@ -16,7 +15,8 @@ import org.apache.commons.lang3.StringUtils; import org.restlet.routing.Router; -public class RestletOpenApiContextBuilder extends GenericOpenApiContextBuilder { +public class RestletOpenApiContextBuilder + extends GenericOpenApiContextBuilder { private Router router; public RestletOpenApiContextBuilder router(Router router) { @@ -33,13 +33,15 @@ public OpenApiContext buildContext(boolean init) throws OpenApiConfigurationExce OpenApiContext ctx = OpenApiContextLocator.getInstance().getOpenApiContext(ctxId); if (ctx == null) { - OpenApiContext rootCtx = OpenApiContextLocator.getInstance() - .getOpenApiContext(OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT); - - ctx = new RestletOpenApiContext(router) - .id(ctxId) - .openApiConfiguration(openApiConfiguration) - .parent(rootCtx); + OpenApiContext rootCtx = + OpenApiContextLocator.getInstance() + .getOpenApiContext(OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT); + + ctx = + new RestletOpenApiContext(router) + .id(ctxId) + .openApiConfiguration(openApiConfiguration) + .parent(rootCtx); if (init) { ctx.init(); diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java index c25b5d3619..cb47a73eb7 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java @@ -1,12 +1,11 @@ -/* - * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * - * Restlet is a registered trademark of QlikTech International AB. +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi.internal; import io.swagger.v3.core.converter.AnnotatedType; @@ -27,6 +26,13 @@ import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.responses.ApiResponse; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import org.restlet.Context; import org.restlet.engine.resource.AnnotationInfo; import org.restlet.engine.resource.AnnotationUtils; @@ -40,14 +46,6 @@ import org.restlet.routing.TemplateRoute; import org.restlet.service.MetadataService; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - public class RestletOpenApiReader implements OpenApiReader { private final MetadataService metadataService = new MetadataService(); @@ -74,24 +72,26 @@ public void setConfiguration(OpenAPIConfiguration openApiConfiguration) { @Override public OpenAPI read(Set> classes, Map resources) { if (config == null) { - throw new IllegalStateException("configuration must be set before processing OpenAPI definition"); + throw new IllegalStateException( + "configuration must be set before processing OpenAPI definition"); } if (router == null) { - throw new IllegalStateException("router must be set before processing OpenAPI definition"); + throw new IllegalStateException( + "router must be set before processing OpenAPI definition"); } return processRouter(router); } private OpenAPI processRouter(Router router) { - OpenAPIDefinition openAPIDefinitionAnnotation = ReflectionUtils.getAnnotation( - router.getApplication().getClass(), - OpenAPIDefinition.class - ); + OpenAPIDefinition openAPIDefinitionAnnotation = + ReflectionUtils.getAnnotation( + router.getApplication().getClass(), OpenAPIDefinition.class); if (openAPIDefinitionAnnotation != null) { - OpenApiAnnotationProcessor.documentOpenApiDefinition(openApi, openAPIDefinitionAnnotation); + OpenApiAnnotationProcessor.documentOpenApiDefinition( + openApi, openAPIDefinitionAnnotation); } completeOpenApiInfo(router); @@ -131,13 +131,11 @@ private void processRoute(Route route) { } private void processServerResource( - ServerResource serverResource, - String operationPath, - List pathVariableNames - ) { - List annotations = serverResource.isAnnotated() - ? AnnotationUtils.getInstance().getAnnotations(serverResource.getClass()) - : null; + ServerResource serverResource, String operationPath, List pathVariableNames) { + List annotations = + serverResource.isAnnotated() + ? AnnotationUtils.getInstance().getAnnotations(serverResource.getClass()) + : null; if (annotations == null) { return; @@ -151,11 +149,13 @@ private void processServerResource( completePathParameters(operation, pathVariableNames); completeOperation(serverResource, operation, methodAnnotationInfo); - PathItem pathItem = Optional.ofNullable(openApi.getPaths()) - .map(openApiPaths -> openApiPaths.get(operationPath)) - .orElseGet(PathItem::new); + PathItem pathItem = + Optional.ofNullable(openApi.getPaths()) + .map(openApiPaths -> openApiPaths.get(operationPath)) + .orElseGet(PathItem::new); - PathItems.setOperation(pathItem, methodAnnotationInfo.getRestletMethod(), operation); + PathItems.setOperation( + pathItem, methodAnnotationInfo.getRestletMethod(), operation); paths.addPathItem(operationPath, pathItem); if (openApi.getPaths() != null) { @@ -170,17 +170,16 @@ private void processServerResource( private void completeOpenApiInfo(Router router) { var applicationClassName = router.getApplication().getClass().getSimpleName(); - var applicationName = applicationClassName.endsWith("Application") - ? applicationClassName.substring(0, applicationClassName.length() - "Application".length()) - : applicationClassName; + var applicationName = + applicationClassName.endsWith("Application") + ? applicationClassName.substring( + 0, applicationClassName.length() - "Application".length()) + : applicationClassName; var defaultTitle = applicationName + " REST API"; if (openApi.getInfo() == null) { - openApi.setInfo(new Info() - .title(defaultTitle) - .version("1.0.0") - ); + openApi.setInfo(new Info().title(defaultTitle).version("1.0.0")); } else if (openApi.getInfo().getTitle() == null) { openApi.getInfo().setTitle(defaultTitle); } else if (openApi.getInfo().getVersion() == null) { @@ -188,41 +187,35 @@ private void completeOpenApiInfo(Router router) { } } - private void completePathParameters( - Operation operation, - List pathVariableNames - ) { + private void completePathParameters(Operation operation, List pathVariableNames) { if (pathVariableNames != null) { for (String pathVariableName : pathVariableNames) { operation.addParametersItem( - new io.swagger.v3.oas.models.parameters.Parameter() - .name(pathVariableName) - .in("path") - .required(true) - .schema(new Schema<>().type("string")) - ); + new io.swagger.v3.oas.models.parameters.Parameter() + .name(pathVariableName) + .in("path") + .required(true) + .schema(new Schema<>().type("string"))); } } } private Operation buildOperationFromRestletMethod( - final MethodAnnotationInfo methodAnnotationInfo - ) { + final MethodAnnotationInfo methodAnnotationInfo) { Operation operation = new Operation(); operation.setOperationId(methodAnnotationInfo.getJavaMethod().getName()); return operation; } private void completeOperation( - final ServerResource serverResource, - final Operation operation, - MethodAnnotationInfo methodAnnotationInfo - ) { + final ServerResource serverResource, + final Operation operation, + MethodAnnotationInfo methodAnnotationInfo) { try { - var methodOperationAnnotation = ReflectionUtils.getAnnotation( - methodAnnotationInfo.getJavaMethod(), - io.swagger.v3.oas.annotations.Operation.class - ); + var methodOperationAnnotation = + ReflectionUtils.getAnnotation( + methodAnnotationInfo.getJavaMethod(), + io.swagger.v3.oas.annotations.Operation.class); if (methodOperationAnnotation != null) { OpenApiAnnotationProcessor.documentOperation(operation, methodOperationAnnotation); @@ -237,10 +230,10 @@ private void completeOperation( } private void completeOperationInput( - ServerResource serverResource, - Operation operation, - MethodAnnotationInfo methodAnnotationInfo - ) throws IOException { + ServerResource serverResource, + Operation operation, + MethodAnnotationInfo methodAnnotationInfo) + throws IOException { Type[] parameterTypes = methodAnnotationInfo.getJavaMethod().getGenericParameterTypes(); if (parameterTypes.length == 0) { return; @@ -248,10 +241,9 @@ private void completeOperationInput( Type firstParameterType = parameterTypes[0]; - List requestVariants = methodAnnotationInfo.getRequestVariants( - metadataService, - serverResource.getConverterService() - ); + List requestVariants = + methodAnnotationInfo.getRequestVariants( + metadataService, serverResource.getConverterService()); if (requestVariants == null || requestVariants.isEmpty()) { return; @@ -260,29 +252,31 @@ private void completeOperationInput( Variant firstVariant = requestVariants.getFirst(); processTypeToContent(firstParameterType, List.of(firstVariant)) - .ifPresent(content -> operation.requestBody( - new io.swagger.v3.oas.models.parameters.RequestBody() - .content(content) - )); + .ifPresent( + content -> + operation.requestBody( + new io.swagger.v3.oas.models.parameters.RequestBody() + .content(content))); } private void completeOperationSuccessfulOutput( - ServerResource serverResource, - Operation operation, - MethodAnnotationInfo methodAnnotationInfo - ) throws IOException { - List responseVariants = methodAnnotationInfo.getResponseVariants( - metadataService, - serverResource.getConverterService() - ); + ServerResource serverResource, + Operation operation, + MethodAnnotationInfo methodAnnotationInfo) + throws IOException { + List responseVariants = + methodAnnotationInfo.getResponseVariants( + metadataService, serverResource.getConverterService()); Type javaMethodReturnType = methodAnnotationInfo.getJavaMethod().getGenericReturnType(); if (responseVariants == null || responseVariants.isEmpty()) { - var hasResponsesDefined = operation.getResponses() != null && !operation.getResponses().isEmpty(); + var hasResponsesDefined = + operation.getResponses() != null && !operation.getResponses().isEmpty(); if (!hasResponsesDefined) { - Operations.addApiResponse(operation, "200", new ApiResponse().description("Success")); + Operations.addApiResponse( + operation, "200", new ApiResponse().description("Success")); } } else { processMethodReturnType(operation, javaMethodReturnType, responseVariants); @@ -290,29 +284,23 @@ private void completeOperationSuccessfulOutput( } private void processMethodReturnType( - Operation operation, - Type returnType, - List responseVariants - ) { + Operation operation, Type returnType, List responseVariants) { Variant firstVariant = responseVariants.getFirst(); processTypeToContent(returnType, List.of(firstVariant)) - .ifPresent(content -> Operations.addApiResponse( - operation, - "200", - new ApiResponse() - .content(content) - .description("Success") - )); + .ifPresent( + content -> + Operations.addApiResponse( + operation, + "200", + new ApiResponse().content(content).description("Success"))); } private Optional processTypeToContent(Type type, List variants) { - ResolvedSchema resolvedSchema = ModelConverters.getInstance(config.toConfiguration()) - .resolveAsResolvedSchema( - new AnnotatedType(type) - .resolveAsRef(true) - .components(components) - ); + ResolvedSchema resolvedSchema = + ModelConverters.getInstance(config.toConfiguration()) + .resolveAsResolvedSchema( + new AnnotatedType(type).resolveAsRef(true).components(components)); if (resolvedSchema.schema == null) { return Optional.empty(); diff --git a/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/LibraryExample.java b/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/LibraryExample.java index 2619cee4ab..c852ba075b 100644 --- a/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/LibraryExample.java +++ b/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/LibraryExample.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open source license available at - * http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi; import io.swagger.v3.oas.annotations.Operation; @@ -15,6 +14,8 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import java.util.List; +import java.util.Optional; import org.restlet.data.Reference; import org.restlet.data.Status; import org.restlet.resource.Delete; @@ -23,33 +24,25 @@ import org.restlet.resource.ServerResource; import org.restlet.routing.Router; -import java.util.List; -import java.util.Optional; - @SuppressWarnings("unused") public class LibraryExample { - private static final List BOOKS = List.of( - new Book("1", "The Great Gatsby", "F. Scott Fitzgerald"), - new Book("2", "To Kill a Mockingbird", "Harper Lee") - ); + private static final List BOOKS = + List.of( + new Book("1", "The Great Gatsby", "F. Scott Fitzgerald"), + new Book("2", "To Kill a Mockingbird", "Harper Lee")); - public record Book(String id, String title, String author) { - } + public record Book(String id, String title, String author) {} public static class BookResource extends ServerResource { @Delete - @Operation( - summary = "Delete a book by ID" - ) + @Operation(summary = "Delete a book by ID") public void deleteBook() { Optional.ofNullable(getBook()) .ifPresent(BOOKS::remove); // Yes, this is an immutable list :) } @Get - @Operation( - summary = "Get a book by ID" - ) + @Operation(summary = "Get a book by ID") public Book getBook() { String bookId = getAttribute("bookId"); getContext().getLogger().info("Retrieving book with ID: " + bookId); @@ -60,34 +53,27 @@ public Book getBook() { public static class BooksResource extends ServerResource { @Get @Operation( - summary = "Get a list of all books", - parameters = { - @Parameter( - name = "filter", - description = "Filter books", - in = io.swagger.v3.oas.annotations.enums.ParameterIn.QUERY, - schema = @Schema(type = "string") - ) - } - ) + summary = "Get a list of all books", + parameters = { + @Parameter( + name = "filter", + description = "Filter books", + in = io.swagger.v3.oas.annotations.enums.ParameterIn.QUERY, + schema = @Schema(type = "string")) + }) public List getBooks() { return BOOKS; } @Post @Operation( - summary = "Add a new book", - responses = { - @ApiResponse( - responseCode = "201", - headers = @Header( - name = "Location", - schema = @Schema(type = "string") - ), - content = @Content() - ) - } - ) + summary = "Add a new book", + responses = { + @ApiResponse( + responseCode = "201", + headers = @Header(name = "Location", schema = @Schema(type = "string")), + content = @Content()) + }) public void addBook(Book book) { getResponse().setStatus(Status.SUCCESS_CREATED); Reference locationRef = getRequest().getResourceRef().addSegment(book.id); diff --git a/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiGenerationTest.java b/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiGenerationTest.java index b927cdff3d..155d9a4bda 100644 --- a/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiGenerationTest.java +++ b/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiGenerationTest.java @@ -1,12 +1,11 @@ /** * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open source license available at - * http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,10 +30,12 @@ void testLibraryApplicationOpenApi() throws Exception { var application = new LibraryExample.LibraryApplication(); var component = new Component(); - var server = component.getServers().add( - Protocol.HTTP, - 0 // 0 = let the OS find an ephemeral port - ); + var server = + component + .getServers() + .add( + Protocol.HTTP, 0 // 0 = let the OS find an ephemeral port + ); component.getDefaultHost().attach(application); component.start(); @@ -43,9 +44,9 @@ void testLibraryApplicationOpenApi() throws Exception { System.out.println("Server started on: http://localhost:" + actualPort); - ClientResource clientResource = new ClientResource( - "http://localhost:" + actualPort + OPENAPI_SPECIFICATION_DEFAULT_PATH - ); + ClientResource clientResource = + new ClientResource( + "http://localhost:" + actualPort + OPENAPI_SPECIFICATION_DEFAULT_PATH); Representation representation = clientResource.get(); @@ -54,9 +55,9 @@ void testLibraryApplicationOpenApi() throws Exception { } String actualYamlResponse = parseAndFormatYaml(representation.getText()); - String expectedYamlResponse = parseAndFormatYaml( - OpenApiSpecifications.readFromClasspath("/library-openapi.yaml") - ); + String expectedYamlResponse = + parseAndFormatYaml( + OpenApiSpecifications.readFromClasspath("/library-openapi.yaml")); assertEquals(expectedYamlResponse, actualYamlResponse); diff --git a/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiSpecifications.java b/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiSpecifications.java index efed16b141..9bb450fbb8 100644 --- a/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiSpecifications.java +++ b/org.restlet.ext.openapi/src/test/java/org/restlet/ext/openapi/OpenApiSpecifications.java @@ -1,17 +1,15 @@ /** * Copyright 2005-2026 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open source license available at - * http://www.opensource.org/licenses/apache-2.0 - * + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

* Restlet is a registered trademark of QlikTech International AB. */ - package org.restlet.ext.openapi; import io.swagger.v3.parser.OpenAPIV3Parser; import io.swagger.v3.parser.core.models.SwaggerParseResult; - import java.util.List; class OpenApiSpecifications { @@ -20,14 +18,16 @@ class OpenApiSpecifications { static String readFromClasspath(String resourcePath) throws Exception { try (var inputStream = OpenApiSpecifications.class.getResourceAsStream(resourcePath)) { if (inputStream == null) { - throw new IllegalArgumentException("Resource not found in classpath: " + resourcePath); + throw new IllegalArgumentException( + "Resource not found in classpath: " + resourcePath); } return new String(inputStream.readAllBytes()); } } static ValidationResult validate(String yamlSpecification) { - SwaggerParseResult result = new OpenAPIV3Parser().readContents(yamlSpecification, null, null); + SwaggerParseResult result = + new OpenAPIV3Parser().readContents(yamlSpecification, null, null); if (result.getMessages().isEmpty()) { return new ValidationResult.Valid(); @@ -37,10 +37,8 @@ static ValidationResult validate(String yamlSpecification) { } sealed interface ValidationResult { - record Valid() implements ValidationResult { - } + record Valid() implements ValidationResult {} - record Invalid(List errors) implements ValidationResult { - } + record Invalid(List errors) implements ValidationResult {} } } diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java index 3b00b3cba8..b0226063e8 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java @@ -185,8 +185,8 @@ private boolean isAvailableUri(String name) { } /** - * Returns true if bean names are searched for higher up in the BeanFactory hierarchy. - * Default is true. + * Returns true if bean names are searched for higher up in the BeanFactory hierarchy. Default + * is true. * * @return True if bean names will be searched for higher up in the BeanFactory hierarchy. */ diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java index 556c2c16ed..6ec34f9644 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java @@ -82,5 +82,7 @@ public Reader getResourceReader(String source, String encoding) } @Override - public void init(ExtProperties configuration) {} + public void init(ExtProperties configuration) { + // No-op + } } diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java index ce99153566..13606a61ff 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java @@ -22,10 +22,10 @@ import org.restlet.util.Resolver; /** - * Filter the response's entity and wrap it with a Velocity's template representation. By default, the - * template representation provides a data model based on the request and response objects. In order - * for the wrapping to happen, the representations must have the {@link Encoding#VELOCITY} encoding - * set.
+ * Filter the response's entity and wrap it with a Velocity's template representation. By default, + * the template representation provides a data model based on the request and response objects. In + * order for the wrapping to happen, the representations must have the {@link Encoding#VELOCITY} + * encoding set.
*
* Concurrency note: instances of this class or its subclasses can be invoked by several threads at * the same time and therefore must be thread-safe. You should be especially careful when storing diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java index c4fd431cc9..9e38350132 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java @@ -11,10 +11,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.Writer; - import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactoryConfigurationError; - import org.restlet.data.CharacterSet; import org.restlet.data.MediaType; import org.restlet.representation.Representation; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java index 92e8883cae..dcba96dd9d 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java @@ -13,7 +13,6 @@ import javax.xml.XMLConstants; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Result; -import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java index 3c9c79b1cd..930edba0a1 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java @@ -21,8 +21,7 @@ /** * Filter that can transform XML representations by applying an XSLT transform sheet. It uses the - * {@link org.restlet.representation.Representation} to actually transform the XML - * entities.
+ * {@link org.restlet.representation.Representation} to actually transform the XML entities.
*
* Concurrency note: instances of this class or its subclasses can be invoked by several threads at * the same time and therefore must be thread-safe. You should be especially careful when storing diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java index 9c36189cd3..b83d888802 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java @@ -194,9 +194,8 @@ * *

The "rdf" prefix is declared only once because the RDF Namespace is used by the root element * and can be inherited by all of its descendants; the "dc" prefix, on the other hand, is declared - * three times because no higher element uses the Namespace. To solve this problem, you can - * instruct the XML writer to predeclare Namespaces on the root element even if they are not used - * there: + * three times because no higher element uses the Namespace. To solve this problem, you can instruct + * the XML writer to predeclare Namespaces on the root element even if they are not used there: * *

  * w.forceNSDecl("http://www.purl.org/dc/");
@@ -802,7 +801,7 @@ public void flush() throws IOException {
      * element.
      *
      * 

This method forces a Namespace to be declared on the root element even if it is not used - * there and reduces the number of xmlns attributes in the document. + * there and reduces the number of xmlns attributes in the document. * * @param uri The Namespace URI to declare. * @see #forceNSDecl(java.lang.String,java.lang.String) @@ -993,8 +992,8 @@ public void setOutput(Writer writer) { /** * Specify a preferred prefix for a Namespace URI. * - *

Note that this method does not force the Namespace to be declared; to do that, - * use the {@link #forceNSDecl(java.lang.String) forceNSDecl} method as well. + *

Note that this method does not force the Namespace to be declared; to do that, use the + * {@link #forceNSDecl(java.lang.String) forceNSDecl} method as well. * * @param uri The Namespace URI. * @param prefix The preferred prefix, or "" to select the default Namespace. @@ -1139,13 +1138,18 @@ private void writeAttributes(Attributes attributes) throws SAXException { if ("xmlns".equals(attributes.getQName(i))) { // Redefines the default namespace. forceNSDecl(attributes.getValue(i)); - } else if (attributes.getQName(i) != null && attributes.getQName(i).startsWith("xmlns")) { + } else if (attributes.getQName(i) != null + && attributes.getQName(i).startsWith("xmlns")) { // Defines the namespace using its prefix. forceNSDecl(attributes.getValue(i), attributes.getLocalName(i)); } else { final char[] ch = attributes.getValue(i).toCharArray(); write(' '); - writeName(attributes.getURI(i), attributes.getLocalName(i), attributes.getQName(i), false); + writeName( + attributes.getURI(i), + attributes.getLocalName(i), + attributes.getQName(i), + false); write("=\""); writeEsc(ch, 0, ch.length, true); write('"'); diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java index 41ea230b5b..fc7c348184 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java @@ -13,8 +13,6 @@ import org.xml.sax.DTDHandler; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; import org.xml.sax.XMLReader; /** diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java index da827c8b77..8a0d505964 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.util.logging.Level; import javax.xml.transform.Source; -import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; import javax.xml.transform.stream.StreamSource; import org.restlet.Context; diff --git a/org.restlet/src/main/java/org/restlet/Application.java b/org.restlet/src/main/java/org/restlet/Application.java index f47e2e59a2..ad5ed4c35d 100644 --- a/org.restlet/src/main/java/org/restlet/Application.java +++ b/org.restlet/src/main/java/org/restlet/Application.java @@ -115,8 +115,8 @@ public Application() { * Constructor. * * @param context The context to use based on parent component context. This context should be - * created using the {@link Context#createChildContext()} method to ensure proper - * isolation with the other applications. + * created using the {@link Context#createChildContext()} method to ensure proper isolation + * with the other applications. */ public Application(Context context) { super(context); @@ -150,8 +150,8 @@ public Application(Context context) { } /** - * Creates an inbound root Restlet that will receive all incoming calls. In general, instances of - * Router, Filter, or Finder classes will be used as initial application Restlet. The default + * Creates an inbound root Restlet that will receive all incoming calls. In general, instances + * of Router, Filter, or Finder classes will be used as initial application Restlet. The default * implementation returns null by default. This method is intended to be overridden by * subclasses. * @@ -164,12 +164,12 @@ public Restlet createInboundRoot() { /** * Creates an outbound root Restlet that will receive all outgoing calls from ClientResource. In * general, instances of {@link Router} and {@link Filter} classes will be used. The default - * implementation returns a Restlet giving access to the outbound service layer and finally - * to the {@link Context#getClientDispatcher()}. + * implementation returns a Restlet giving access to the outbound service layer and finally to + * the {@link Context#getClientDispatcher()}. * *

This method is intended to be overridden by subclasses, but to benefit from the outbound - * service filtering layer, the original outbound root must be carefully attached again at the end - * of the user filtering layer. + * service filtering layer, the original outbound root must be carefully attached again at the + * end of the user filtering layer. * * @return The outbound root Restlet. */ diff --git a/org.restlet/src/main/java/org/restlet/Context.java b/org.restlet/src/main/java/org/restlet/Context.java index e2fe6e711c..86b727ccd9 100644 --- a/org.restlet/src/main/java/org/restlet/Context.java +++ b/org.restlet/src/main/java/org/restlet/Context.java @@ -143,13 +143,13 @@ public Context createChildContext() { } /** - * Returns a modifiable attributes map that developers can use to save information - * relative to the context. This is a convenient means to provide common objects to all the - * Restlets and Resources composing an Application.
+ * Returns a modifiable attributes map that developers can use to save information relative to + * the context. This is a convenient means to provide common objects to all the Restlets and + * Resources composing an Application.
*
* In addition, this map is a shared space between the developer and the Restlet implementation. - * For this purpose, all attribute names starting with "org.restlet" are reserved. Currently, the - * following attributes are used: + * For this purpose, all attribute names starting with "org.restlet" are reserved. Currently, + * the following attributes are used: * * * @@ -239,8 +239,8 @@ public Series getParameters() { * component's server connectors. It first must match one of the registered virtual hosts. Then * it can be routed to one of the attached Restlets, typically an Application.
*
- * Note that this dispatcher doesn't support the RIAP pseudo protocol, you should instead - * rely on the {@link #getClientDispatcher()} method. + * Note that this dispatcher doesn't support the RIAP pseudo protocol, you should instead rely + * on the {@link #getClientDispatcher()} method. * * @return A request dispatcher to the server connectors' router. */ diff --git a/org.restlet/src/main/java/org/restlet/Message.java b/org.restlet/src/main/java/org/restlet/Message.java index 1f5bad8c0c..2269c9cb39 100644 --- a/org.restlet/src/main/java/org/restlet/Message.java +++ b/org.restlet/src/main/java/org/restlet/Message.java @@ -109,9 +109,9 @@ && getEntity().isAvailable()) { public void flushBuffers() throws IOException {} /** - * Returns the modifiable map of attributes that developers can use to save information - * relative to the message. Creates a new instance if no one has been set. This is an easier - * alternative to the creation of a wrapper instance around the whole message.
+ * Returns the modifiable map of attributes that developers can use to save information relative + * to the message. Creates a new instance if no one has been set. This is an easier alternative + * to the creation of a wrapper instance around the whole message.
*
* In addition, this map is a shared space between the developer and the connectors. In this * case, it is used to exchange information that is not uniform across all protocols and diff --git a/org.restlet/src/main/java/org/restlet/Request.java b/org.restlet/src/main/java/org/restlet/Request.java index e57b42152a..cff7a06339 100644 --- a/org.restlet/src/main/java/org/restlet/Request.java +++ b/org.restlet/src/main/java/org/restlet/Request.java @@ -309,7 +309,9 @@ public boolean abort() { * * @param response The response to commit. */ - public void commit(Response response) {} + public void commit(Response response) { + // No-op by default + } /** * Returns the modifiable set of headers the client is willing to send in future request to this @@ -637,13 +639,13 @@ public boolean isSynchronous() { /** * Sets the set of headers the client is willing to use in future request to this resource. Used - * when issuing a preflight CORS request to let the origin server know what headers will be - * sent later.
+ * when issuing a preflight CORS request to let the origin server know what headers will be sent + * later.
* Note that when used with HTTP connectors, this property maps to the * "Access-Control-Request-Method" header. * - * @param accessControlRequestHeaders The set of headers the client is willing to send in a future - * request to this resource. Useful for CORS support. + * @param accessControlRequestHeaders The set of headers the client is willing to send in a + * future request to this resource. Useful for CORS support. */ public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { synchronized (getAccessControlRequestHeaders()) { diff --git a/org.restlet/src/main/java/org/restlet/Response.java b/org.restlet/src/main/java/org/restlet/Response.java index a9f1e12261..f1e5819ef3 100644 --- a/org.restlet/src/main/java/org/restlet/Response.java +++ b/org.restlet/src/main/java/org/restlet/Response.java @@ -62,8 +62,8 @@ public static void setCurrent(Response response) { } /** - * When used as part of a response to a preflight CORS request, this indicates whether - * the actual request can be made using credentials. + * When used as part of a response to a preflight CORS request, this indicates whether the + * actual request can be made using credentials. */ private volatile Boolean accessControlAllowCredentials; @@ -181,9 +181,9 @@ public void abort() { * feature.
*
* When the response is in autoCommit mode (see related property), then calling this method - * isn't necessary. Also, be aware that committing the response doesn't necessarily mean that - * it will immediately be written on the network as some buffering can occur. If you want to - * ensure that response buffers are flushed.
+ * isn't necessary. Also, be aware that committing the response doesn't necessarily mean that it + * will immediately be written on the network as some buffering can occur. If you want to ensure + * that response buffers are flushed.
*
* Note that this calls back {@link Request#commit(Response)} on the parent request which holds * the link with the underlying network connection. @@ -205,8 +205,8 @@ public void flushBuffers() throws IOException { } /** - * When used as part of a response to a preflight CORS request, this indicates whether - * the actual request can be made using credentials.
+ * When used as part of a response to a preflight CORS request, this indicates whether the + * actual request can be made using credentials.
* Note that when used with HTTP connectors, this property maps to the * "Access-Control-Allow-Credentials" header. * @@ -465,8 +465,8 @@ public Request getRequest() { } /** - * Indicates how long the service is expected to be unavailable to the requesting client. - * The default value is null.
+ * Indicates how long the service is expected to be unavailable to the requesting client. The + * default value is null.
*
* Note that when used with HTTP connectors, this property maps to the "Retry-After" header. * diff --git a/org.restlet/src/main/java/org/restlet/Restlet.java b/org.restlet/src/main/java/org/restlet/Restlet.java index 98c2598a78..161daa97d0 100644 --- a/org.restlet/src/main/java/org/restlet/Restlet.java +++ b/org.restlet/src/main/java/org/restlet/Restlet.java @@ -404,8 +404,8 @@ public synchronized void start() throws Exception { /** * Stops the Restlet. By default, it only sets the "started" internal property to false. * - *

WARNING: this method must be called at the beginning of the stopping process by subclasses, - * otherwise concurrent threads could continue to (improperly) handle calls. + *

WARNING: this method must be called at the beginning of the stopping process by + * subclasses, otherwise concurrent threads could continue to (improperly) handle calls. * * @throws Exception */ diff --git a/org.restlet/src/main/java/org/restlet/security/Role.java b/org.restlet/src/main/java/org/restlet/security/Role.java index df63883017..e490ebf50f 100644 --- a/org.restlet/src/main/java/org/restlet/security/Role.java +++ b/org.restlet/src/main/java/org/restlet/security/Role.java @@ -17,8 +17,8 @@ /** * Application-specific role. Common examples are "administrator", "user", "anonymous", - * "supervisor". Note that for reusability purposes, it is recommended that those roles don't reflect - * an actual organization, but more the functional requirements of your application. + * "supervisor". Note that for reusability purposes, it is recommended that those roles don't + * reflect an actual organization, but more the functional requirements of your application. * *

Two roles are considered equals if they belong to the same parent application and have the * same name and child roles. The description isn't used for equality assessment. diff --git a/org.restlet/src/main/java/org/restlet/service/CorsService.java b/org.restlet/src/main/java/org/restlet/service/CorsService.java index 831b0ffb8a..3b27d99512 100644 --- a/org.restlet/src/main/java/org/restlet/service/CorsService.java +++ b/org.restlet/src/main/java/org/restlet/service/CorsService.java @@ -18,8 +18,8 @@ import org.restlet.routing.Filter; /** - * Application service that adds support of CORS. This service lets the target resource specify - * the allowed methods. + * Application service that adds support of CORS. This service lets the target resource specify the + * allowed methods. * *

Example: * @@ -65,7 +65,8 @@ public class CorsService extends Service { private Set exposedHeaders = null; /** - * The value of the 'Access-Control-Max-Age' response header. Default is that the header is not set. + * The value of the 'Access-Control-Max-Age' response header. Default is that the header is not + * set. */ private int maxAge = -1; diff --git a/org.restlet/src/main/java/org/restlet/service/EncoderService.java b/org.restlet/src/main/java/org/restlet/service/EncoderService.java index c3a413de40..69c08a99ed 100644 --- a/org.restlet/src/main/java/org/restlet/service/EncoderService.java +++ b/org.restlet/src/main/java/org/restlet/service/EncoderService.java @@ -33,8 +33,8 @@ public class EncoderService extends Service { public static final int DEFAULT_MINIMUM_SIZE = 1000; /** - * Returns the list of default encoded media types. Subclasses can override this. By - * default, all media types are encoded (except those explicitly ignored). + * Returns the list of default encoded media types. Subclasses can override this. By default, + * all media types are encoded (except those explicitly ignored). * * @return The list of default encoded media types. */ @@ -43,8 +43,8 @@ public static List getDefaultAcceptedMediaTypes() { } /** - * Returns the list of default ignored media types. Subclasses can override this. By - * default, all archive, audio, image, and video media types are ignored. + * Returns the list of default ignored media types. Subclasses can override this. By default, + * all archive, audio, image, and video media types are ignored. * * @return The list of default ignored media types. */ diff --git a/org.restlet/src/main/java/org/restlet/service/LogService.java b/org.restlet/src/main/java/org/restlet/service/LogService.java index fbad40c7e4..b9d5e44b42 100644 --- a/org.restlet/src/main/java/org/restlet/service/LogService.java +++ b/org.restlet/src/main/java/org/restlet/service/LogService.java @@ -239,7 +239,7 @@ protected String getDefaultResponseLogMessage(Response response, int duration) { /** * Returns the URI template of loggable resource references. Returns null by default, meaning - * all requests are loggable, independent of their target resource URI reference. + * all requests are loggable, independent of their target resource URI reference. * * @return The URI template of loggable resource references. * @see Request#getResourceRef() @@ -301,7 +301,8 @@ public String getResponseLogMessage(Response response, int duration) { } /** - * Indicates if the identity check (as specified by RFC1413) is enabled. The default value is false. + * Indicates if the identity check (as specified by RFC1413) is enabled. The default value is + * false. * * @return True if the identity check is enabled. */ diff --git a/org.restlet/src/main/java/org/restlet/service/MetadataService.java b/org.restlet/src/main/java/org/restlet/service/MetadataService.java index cb04b12f18..6b2ed24fd3 100644 --- a/org.restlet/src/main/java/org/restlet/service/MetadataService.java +++ b/org.restlet/src/main/java/org/restlet/service/MetadataService.java @@ -611,8 +611,8 @@ public List getAllMetadata(String extension) { /** * Returns the character set associated with this extension. It returns null if the extension * was not declared of it is corresponding to another type of metadata such as a media type. If - * some metadata is associated with the same extension, then only the first matching metadata - * is returned. + * some metadata is associated with the same extension, then only the first matching metadata is + * returned. * * @param extension The extension name without any delimiter. * @return The character set associated with this extension. diff --git a/org.restlet/src/main/java/org/restlet/util/Resolver.java b/org.restlet/src/main/java/org/restlet/util/Resolver.java index 00086b74bf..698d00c4e5 100644 --- a/org.restlet/src/main/java/org/restlet/util/Resolver.java +++ b/org.restlet/src/main/java/org/restlet/util/Resolver.java @@ -279,9 +279,9 @@ public static Resolver createResolver(Map map) { } /** - * Creates a resolver based on a call (request, response couple). It first looks up the - * response attributes, then the request attributes, and finally the variables listed in this - * class Javadocs above. + * Creates a resolver based on a call (request, response couple). It first looks up the response + * attributes, then the request attributes, and finally the variables listed in this class + * Javadocs above. * * @param request The request. * @param response The response. diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java index 209783bec2..3fcedc41ca 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java @@ -138,8 +138,8 @@ public Set keySet() { * * @param key Key with which the specified value is to be associated. * @param value Value to be associated with the specified key. - * @return The previous value associated with a specified key, or null if there was no mapping for - * key. A null return can also indicate that the map previously associated null with the + * @return The previous value associated with a specified key, or null if there was no mapping + * for key. A null return can also indicate that the map previously associated null with the * specified key, if the implementation supports null values. */ public V put(K key, V value) { @@ -159,8 +159,8 @@ public void putAll(Map t) { * Removes the mapping for this key from this map if it is present (optional operation). * * @param key Key whose mapping is to be removed from the map. - * @return The previous value associated with a specified key, or null if there was no mapping for - * key. + * @return The previous value associated with a specified key, or null if there was no mapping + * for key. */ public V remove(Object key) { return getDelegate().remove(key); diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java b/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java index 9bfb2b3c0d..7f7acf46e2 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java @@ -23,8 +23,8 @@ import org.restlet.representation.Representation; /** - * Representation wrapper. Useful for developers who need to enrich the representation - * with application-related properties and behavior. + * Representation wrapper. Useful for developers who need to enrich the representation with + * application-related properties and behavior. * * @see The decorator (aka wrapper) pattern * @author Jerome Louvel diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java index 4561c0f3be..2c17e7fa7b 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java @@ -56,9 +56,9 @@ public void commit(Response response) { } /** - * Returns a modifiable attributes map that developers can use to save information - * relative to the message. This is an easier alternative to the creation of a wrapper instance - * around the whole message.
+ * Returns a modifiable attributes map that developers can use to save information relative to + * the message. This is an easier alternative to the creation of a wrapper instance around the + * whole message.
*
* In addition, this map is a shared space between the developer and the connectors. In this * case, it is used to exchange information that is not uniform across all protocols and diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java b/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java index ff75756b5a..625ef6a2aa 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java @@ -26,7 +26,8 @@ import org.restlet.representation.Representation; /** - * Request wrapper. Useful for developers who need to enrich the request with application-related properties and behavior. + * Request wrapper. Useful for developers who need to enrich the request with application-related + * properties and behavior. * * @see The decorator (aka wrapper) pattern * @author Jerome Louvel @@ -72,9 +73,9 @@ public Set getAllowedMethods() { } /** - * Returns a modifiable attributes map that developers can use to save information - * relative to the message. This is an easier alternative to the creation of a wrapper instance - * around the whole message.
+ * Returns a modifiable attributes map that developers can use to save information relative to + * the message. This is an easier alternative to the creation of a wrapper instance around the + * whole message.
*
* In addition, this map is a shared space between the developer and the connectors. In this * case, it is used to exchange information that is not uniform across all protocols and From 89324513a02b4f45197d53d771625ad3d51d0654 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Fri, 3 Apr 2026 23:16:22 +0200 Subject: [PATCH 06/30] Take into account Sonar comments --- .../src/main/java/org/restlet/Request.java | 2 +- .../org/restlet/data/AuthenticationInfo.java | 73 +++------- .../org/restlet/data/ChallengeMessage.java | 2 +- .../org/restlet/data/ChallengeRequest.java | 12 +- .../java/org/restlet/data/CharacterSet.java | 6 +- .../java/org/restlet/data/ClientInfo.java | 126 +++++++++--------- .../java/org/restlet/data/Conditions.java | 55 ++++---- .../main/java/org/restlet/data/Digest.java | 7 +- .../java/org/restlet/data/Disposition.java | 2 +- .../main/java/org/restlet/data/Encoding.java | 4 +- .../java/org/restlet/data/Expectation.java | 2 +- .../main/java/org/restlet/data/Language.java | 31 ++--- .../java/org/restlet/data/LocalReference.java | 2 +- .../main/java/org/restlet/data/MediaType.java | 4 +- .../main/java/org/restlet/data/Metadata.java | 2 +- .../main/java/org/restlet/data/Method.java | 4 +- .../java/org/restlet/data/Preference.java | 2 +- .../main/java/org/restlet/data/Protocol.java | 4 +- .../src/main/java/org/restlet/data/Range.java | 6 +- .../main/java/org/restlet/data/Reference.java | 2 +- .../java/org/restlet/data/ReferenceList.java | 2 +- .../main/java/org/restlet/data/Status.java | 10 +- .../main/java/org/restlet/engine/Engine.java | 31 +++-- .../java/org/restlet/engine/adapter/Call.java | 7 +- .../restlet/engine/adapter/ClientAdapter.java | 41 +++--- .../engine/adapter/JettyClientCall.java | 11 +- .../engine/application/ApplicationHelper.java | 28 ++-- .../application/DecodeRepresentation.java | 3 +- .../restlet/engine/application/Encoder.java | 12 +- .../engine/application/FlexibleConneg.java | 20 +-- .../engine/application/StrictConneg.java | 20 ++- .../engine/application/TunnelFilter.java | 86 ++++++------ .../restlet/engine/component/ClientRoute.java | 9 -- .../engine/component/ClientRouter.java | 4 +- .../component/ComponentClientDispatcher.java | 8 +- .../restlet/engine/component/HostRoute.java | 11 +- .../engine/component/ServerRouter.java | 4 +- .../engine/connector/ConnectorHelper.java | 13 +- .../engine/connector/HttpClientHelper.java | 65 ++++----- .../engine/connector/JettyServerHelper.java | 3 +- .../engine/connector/ServerHelper.java | 16 ++- .../engine/converter/ConverterHelper.java | 2 +- .../engine/converter/DefaultConverter.java | 88 ++++++------ .../engine/header/DimensionWriter.java | 1 + .../engine/header/HeaderConstants.java | 3 + .../restlet/engine/header/HeaderReader.java | 2 +- .../engine/header/PreferenceReader.java | 7 +- .../engine/header/PreferenceWriter.java | 2 - .../restlet/engine/header/ProductReader.java | 2 +- .../restlet/engine/internal/Activator.java | 20 ++- .../java/org/restlet/engine/io/IoUtils.java | 15 +-- .../restlet/engine/io/RangeInputStream.java | 2 +- .../restlet/engine/io/ReaderInputStream.java | 10 +- .../engine/io/UnclosableInputStream.java | 4 +- .../engine/io/UnclosableOutputStream.java | 4 +- .../engine/local/DirectoryServerResource.java | 26 ++-- .../java/org/restlet/engine/local/Entity.java | 20 +-- .../engine/local/FileClientHelper.java | 15 +-- .../restlet/engine/log/SimplerFormatter.java | 15 ++- .../restlet/engine/log/SimplestFormatter.java | 11 +- .../engine/resource/AnnotationInfo.java | 8 +- .../engine/resource/AnnotationUtils.java | 32 ++--- .../resource/ClientInvocationHandler.java | 28 ++-- .../engine/resource/MethodAnnotationInfo.java | 12 +- .../engine/security/AuthenticatorUtils.java | 2 +- .../engine/security/HttpBasicHelper.java | 2 +- .../engine/ssl/DefaultSslContextFactory.java | 4 +- .../java/org/restlet/engine/ssl/SslUtils.java | 3 +- .../engine/ssl/WrapperSslSocketFactory.java | 5 +- .../engine/util/AlphaNumericComparator.java | 18 +-- .../restlet/engine/util/BeanInfoUtils.java | 3 +- .../engine/util/CaseInsensitiveHashSet.java | 2 +- .../engine/util/ChildClientDispatcher.java | 16 +-- .../engine/util/EngineClassLoader.java | 2 +- .../org/restlet/engine/util/ListUtils.java | 2 +- .../java/org/restlet/engine/util/Pool.java | 2 +- .../org/restlet/engine/util/SetUtils.java | 2 +- .../engine/util/TemplateDispatcher.java | 1 + .../representation/FileRepresentation.java | 1 + 79 files changed, 524 insertions(+), 622 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/Request.java b/org.restlet/src/main/java/org/restlet/Request.java index cff7a06339..3ef45c3d71 100644 --- a/org.restlet/src/main/java/org/restlet/Request.java +++ b/org.restlet/src/main/java/org/restlet/Request.java @@ -590,7 +590,7 @@ public boolean isAsynchronous() { */ @Override public boolean isConfidential() { - return (getProtocol() == null) ? false : getProtocol().isConfidential(); + return getProtocol() != null && getProtocol().isConfidential(); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java b/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java index d602849a91..50759d3c47 100644 --- a/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java @@ -8,6 +8,7 @@ */ package org.restlet.data; +import java.util.Objects; import org.restlet.engine.util.SystemUtils; /** @@ -38,15 +39,6 @@ public class AuthenticationInfo { /** The optional response digest for mutual authentication. */ private volatile String responseDigest; - /** - * Default constructor. - * - * @param nextNonce The next nonce value. - */ - // public AuthenticationInfo(String nextNonce) { - // this(nextNonce, 0, ); - // } - /** * Constructor. * @@ -72,51 +64,30 @@ public AuthenticationInfo( /** {@inheritDoc} */ @Override public final boolean equals(final Object obj) { - boolean result = (obj == this); - - // if obj == this no need to go further - if (!result) { - // if obj isn't a challenge request or is null don't evaluate - // further - if (obj instanceof AuthenticationInfo) { - final AuthenticationInfo that = (AuthenticationInfo) obj; - if (getNextServerNonce() != null) { - result = getNextServerNonce().equals(that.getNextServerNonce()); - } else { - result = (that.getNextServerNonce() == null); - } - - if (result) { - result = (getNonceCount() == that.getNonceCount()); - } - - if (result) { - if (getClientNonce() != null) { - result = getClientNonce().equals(that.getClientNonce()); - } else { - result = (that.getClientNonce() == null); - } - } - - if (result) { - if (getQuality() != null) { - result = getQuality().equals(that.getQuality()); - } else { - result = (that.getQuality() == null); - } - } - - if (result) { - if (getResponseDigest() != null) { - result = getResponseDigest().equals(that.getResponseDigest()); - } else { - result = (that.getResponseDigest() == null); - } - } + if (obj == this) { + return true; + } + if (obj == null) { + return false; + } + + if (obj instanceof final AuthenticationInfo that) { + if (!Objects.equals(getNextServerNonce(), that.getNextServerNonce())) { + return false; + } + if (getNonceCount() != that.getNonceCount()) { + return false; + } + if (!Objects.equals(getClientNonce(), that.getClientNonce())) { + return false; + } + if (!Objects.equals(getQuality(), that.getQuality())) { + return false; } + return Objects.equals(getResponseDigest(), that.getResponseDigest()); } - return result; + return false; } /** diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java index 2f4e57e4d7..1791b0a2b8 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java @@ -158,7 +158,7 @@ public String getOpaque() { */ public Series getParameters() { if (this.parameters == null) { - this.parameters = new Series(Parameter.class); + this.parameters = new Series<>(Parameter.class); } return this.parameters; diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java index c82bcb579b..461c2029ce 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java @@ -83,7 +83,7 @@ public List getDomainRefs() { synchronized (this) { r = this.domainRefs; if (r == null) { - this.domainRefs = r = new CopyOnWriteArrayList(); + this.domainRefs = r = new CopyOnWriteArrayList<>(); this.domainRefs.add(new Reference("/")); } } @@ -104,7 +104,7 @@ public List getQualityOptions() { synchronized (this) { r = this.qualityOptions; if (r == null) { - this.qualityOptions = r = new CopyOnWriteArrayList(); + this.qualityOptions = r = new CopyOnWriteArrayList<>(); this.qualityOptions.add(QUALITY_AUTHENTICATION); } } @@ -143,17 +143,17 @@ public void setDomainRefs(List domainRefs) { * @see #setDomainRefs(List) */ public void setDomainUris(Collection domainUris) { - List domainRefs = null; + List newDomainRefs = null; if (domainUris != null) { - domainRefs = new CopyOnWriteArrayList(); + newDomainRefs = new CopyOnWriteArrayList<>(); for (String domainUri : domainUris) { - domainRefs.add(new Reference(domainUri)); + newDomainRefs.add(new Reference(domainUri)); } } - setDomainRefs(domainRefs); + setDomainRefs(newDomainRefs); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java index 4677831492..f9377b1b44 100644 --- a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java +++ b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java @@ -249,8 +249,10 @@ public CharacterSet(String name, String description) { /** {@inheritDoc} */ @Override public boolean equals(Object object) { - return (object instanceof CharacterSet) - && getName().equalsIgnoreCase(((CharacterSet) object).getName()); + if (object instanceof CharacterSet characterSet) { + return getName().equalsIgnoreCase(characterSet.getName()); + } + return false; } @Override diff --git a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java index 18ed80a6c7..f3b30c1026 100644 --- a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java @@ -8,8 +8,10 @@ */ package org.restlet.data; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.net.URL; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -150,41 +152,9 @@ private static List getUserAgentTemplates() { synchronized (ClientInfo.class) { u = ClientInfo.userAgentTemplates; if (u == null) { - // Load from the "agent.properties" file - java.net.URL userAgentPropertiesUrl = - Engine.getResource("org/restlet/data/agent.properties"); - if (userAgentPropertiesUrl != null) { - java.io.BufferedReader reader; - try { - reader = - new java.io.BufferedReader( - new InputStreamReader( - userAgentPropertiesUrl.openStream(), - CharacterSet.UTF_8.getName()), - IoUtils.BUFFER_SIZE); - String line = reader.readLine(); - for (; line != null; line = reader.readLine()) { - final String trim = line.trim(); - if ((!trim.isEmpty()) && !trim.startsWith("#")) { - if (u == null) { - u = new CopyOnWriteArrayList<>(); - } - u.add(line); - } - } - reader.close(); - } catch (IOException e) { - if (Context.getCurrent() != null) { - Context.getCurrent() - .getLogger() - .warning( - "Cannot read '" - + userAgentPropertiesUrl.toString() - + "' due to: " - + e.getMessage()); - } - } - } + u = + loadUserAgentTemplates( + Engine.getResource("org/restlet/data/agent.properties")); ClientInfo.userAgentTemplates = u; } } @@ -192,6 +162,47 @@ private static List getUserAgentTemplates() { return u; } + /** + * Returns the list of user-agent templates defined in the given url. + * + * @return The list of user-agent templates defined in the given url. + */ + private static List loadUserAgentTemplates(final URL userAgentPropertiesUrl) { + if (userAgentPropertiesUrl == null) { + return null; + } + + List u = null; + try (final InputStreamReader in = + new InputStreamReader( + userAgentPropertiesUrl.openStream(), CharacterSet.UTF_8.getName()); + BufferedReader reader = new BufferedReader(in, IoUtils.BUFFER_SIZE)) { + + String line; + while ((line = reader.readLine()) != null) { + final String trim = line.trim(); + if ((!trim.isEmpty()) && !trim.startsWith("#")) { + if (u == null) { + u = new CopyOnWriteArrayList<>(); + } + u.add(line); + } + } + } catch (IOException e) { + if (Context.getCurrent() != null) { + Context.getCurrent() + .getLogger() + .warning( + "Cannot read '" + + userAgentPropertiesUrl + + "' due to: " + + e.getMessage()); + } + } + + return u; + } + /** The character set preferences. */ private volatile List> acceptedCharacterSets; @@ -282,7 +293,7 @@ public ClientInfo() { public ClientInfo(List variants) { if (variants != null) { for (org.restlet.representation.Variant variant : variants) { - getAcceptedMediaTypes().add(new Preference(variant.getMediaType())); + getAcceptedMediaTypes().add(new Preference<>(variant.getMediaType())); } } } @@ -293,7 +304,7 @@ public ClientInfo(List variants) { * @param mediaType The preferred media type. */ public ClientInfo(MediaType mediaType) { - getAcceptedMediaTypes().add(new Preference(mediaType)); + getAcceptedMediaTypes().add(new Preference<>(mediaType)); } /** @@ -346,8 +357,7 @@ public List> getAcceptedCharacterSets() { synchronized (this) { a = this.acceptedCharacterSets; if (a == null) { - this.acceptedCharacterSets = - a = new CopyOnWriteArrayList>(); + this.acceptedCharacterSets = a = new CopyOnWriteArrayList<>(); } } } @@ -369,7 +379,7 @@ public List> getAcceptedEncodings() { synchronized (this) { a = this.acceptedEncodings; if (a == null) { - this.acceptedEncodings = a = new CopyOnWriteArrayList>(); + this.acceptedEncodings = a = new CopyOnWriteArrayList<>(); } } } @@ -391,7 +401,7 @@ public List> getAcceptedLanguages() { synchronized (this) { a = this.acceptedLanguages; if (a == null) { - this.acceptedLanguages = a = new CopyOnWriteArrayList>(); + this.acceptedLanguages = a = new CopyOnWriteArrayList<>(); } } } @@ -413,7 +423,7 @@ public List> getAcceptedMediaTypes() { synchronized (this) { a = this.acceptedMediaTypes; if (a == null) { - this.acceptedMediaTypes = a = new CopyOnWriteArrayList>(); + this.acceptedMediaTypes = a = new CopyOnWriteArrayList<>(); } } } @@ -435,7 +445,7 @@ public List> getAcceptedPatches() { synchronized (this) { a = this.acceptedPatches; if (a == null) { - this.acceptedPatches = a = new CopyOnWriteArrayList>(); + this.acceptedPatches = a = new CopyOnWriteArrayList<>(); } } } @@ -472,8 +482,8 @@ public String getAgent() { */ public Map getAgentAttributes() { if (this.agentAttributes == null) { - this.agentAttributes = new ConcurrentHashMap(); - Map map = new ConcurrentHashMap(); + this.agentAttributes = new ConcurrentHashMap<>(); + Map map = new ConcurrentHashMap<>(); // Loop on a list of user-agent templates until a template match // the current user-agent string. The list of templates is @@ -582,8 +592,7 @@ public List getCertificates() { synchronized (this) { a = this.certificates; if (a == null) { - this.certificates = - a = new CopyOnWriteArrayList(); + this.certificates = a = new CopyOnWriteArrayList<>(); } } } @@ -612,8 +621,7 @@ public List getExpectations() { synchronized (this) { a = this.expectations; if (a == null) { - this.expectations = - a = new CopyOnWriteArrayList(); + this.expectations = a = new CopyOnWriteArrayList<>(); } } } @@ -648,7 +656,7 @@ public List getForwardedAddresses() { synchronized (this) { a = this.forwardedAddresses; if (a == null) { - this.forwardedAddresses = a = new CopyOnWriteArrayList(); + this.forwardedAddresses = a = new CopyOnWriteArrayList<>(); } } } @@ -671,14 +679,12 @@ public String getFrom() { * @return A Product object based on the name of the user agent. */ public Product getMainAgentProduct() { - if (this.agentMainProduct == null) { - if (getAgentAttributes() != null) { - this.agentMainProduct = - new Product( - getAgentAttributes().get("agentName"), - getAgentAttributes().get("agentVersion"), - getAgentAttributes().get("agentComment")); - } + if (this.agentMainProduct == null && getAgentAttributes() != null) { + this.agentMainProduct = + new Product( + getAgentAttributes().get("agentName"), + getAgentAttributes().get("agentVersion"), + getAgentAttributes().get("agentComment")); } return this.agentMainProduct; @@ -760,7 +766,7 @@ public List getPrincipals() { synchronized (this) { a = this.principals; if (a == null) { - this.principals = a = new CopyOnWriteArrayList(); + this.principals = a = new CopyOnWriteArrayList<>(); } } } @@ -779,7 +785,7 @@ public List getRoles() { synchronized (this) { a = this.roles; if (a == null) { - this.roles = a = new CopyOnWriteArrayList(); + this.roles = a = new CopyOnWriteArrayList<>(); } } } diff --git a/org.restlet/src/main/java/org/restlet/data/Conditions.java b/org.restlet/src/main/java/org/restlet/data/Conditions.java index cd551a8b8a..4d41a8fb5c 100644 --- a/org.restlet/src/main/java/org/restlet/data/Conditions.java +++ b/org.restlet/src/main/java/org/restlet/data/Conditions.java @@ -48,7 +48,9 @@ public final class Conditions { private volatile Date unmodifiedSince; /** Constructor. */ - public Conditions() {} + public Conditions() { + // Nothing to do + } /** * Returns the modifiable list of tags that must be matched. Creates a new instance if no one @@ -65,7 +67,7 @@ public List getMatch() { synchronized (this) { m = this.match; if (m == null) { - this.match = m = new ArrayList(); + this.match = m = new ArrayList<>(); } } } @@ -99,7 +101,7 @@ public List getNoneMatch() { synchronized (this) { n = this.noneMatch; if (n == null) { - this.noneMatch = n = new ArrayList(); + this.noneMatch = n = new ArrayList<>(); } } } @@ -142,16 +144,12 @@ public Status getRangeStatus(Tag tag, Date modificationDate) { boolean all = getRangeTag().equals(Tag.ALL); // If a tag exists - if (tag != null) { - if (all || getRangeTag().equals(tag)) { - result = Status.SUCCESS_OK; - } - } - } else if (getRangeDate() != null) { - // If a modification date exists - if (getRangeDate().equals(modificationDate)) { + if (tag != null && (all || getRangeTag().equals(tag))) { result = Status.SUCCESS_OK; } + + } else if (getRangeDate() != null && getRangeDate().equals(modificationDate)) { + result = Status.SUCCESS_OK; } return result; @@ -247,14 +245,7 @@ public Status getStatus(Method method, boolean entityExists, Tag tag, Date modif // Check if the current representation has been updated // since the "if-modified-since" date. In this case, the // rule is followed. - Date modifiedSince = getModifiedSince(); - boolean isModifiedSince = - (modifiedSince != null) - && (DateUtils.after(new Date(), modifiedSince) - || (modificationDate == null) - || DateUtils.after( - modifiedSince, modificationDate)); - matched = !isModifiedSince; + matched = isNotModifiedSince(modificationDate); } } else { matched = @@ -273,19 +264,11 @@ public Status getStatus(Method method, boolean entityExists, Tag tag, Date modif } // Is the "if-Modified-Since" rule followed or not? - if ((result == null) && (getModifiedSince() != null)) { - Date modifiedSince = getModifiedSince(); - boolean isModifiedSince = - (DateUtils.after(new Date(), modifiedSince) - || (modificationDate == null) - || DateUtils.after(modifiedSince, modificationDate)); - - if (!isModifiedSince) { - if (Method.GET.equals(method) || Method.HEAD.equals(method)) { - result = Status.REDIRECTION_NOT_MODIFIED; - } else { - result = Status.CLIENT_ERROR_PRECONDITION_FAILED; - } + if (result == null && isNotModifiedSince(modificationDate)) { + if (Method.GET.equals(method) || Method.HEAD.equals(method)) { + result = Status.REDIRECTION_NOT_MODIFIED; + } else { + result = Status.CLIENT_ERROR_PRECONDITION_FAILED; } } @@ -305,6 +288,14 @@ public Status getStatus(Method method, boolean entityExists, Tag tag, Date modif return result; } + private boolean isNotModifiedSince(final Date modificationDate) { + Date since = getModifiedSince(); + return (since == null) + || (!DateUtils.after(new Date(), since) + && (modificationDate != null) + && !DateUtils.after(since, modificationDate)); + } + /** * Returns the conditional status of a variant using a given method. * diff --git a/org.restlet/src/main/java/org/restlet/data/Digest.java b/org.restlet/src/main/java/org/restlet/data/Digest.java index 5ce6fd62f3..70ee94ac84 100644 --- a/org.restlet/src/main/java/org/restlet/data/Digest.java +++ b/org.restlet/src/main/java/org/restlet/data/Digest.java @@ -81,11 +81,10 @@ public Digest(String algorithm, byte[] value) { @Override public boolean equals(Object obj) { - if (obj instanceof Digest that) { - if (getAlgorithm().equals(that.getAlgorithm())) { - return Arrays.equals(getValue(), that.getValue()); - } + if (obj instanceof Digest that && getAlgorithm().equals(that.getAlgorithm())) { + return Arrays.equals(getValue(), that.getValue()); } + return false; } diff --git a/org.restlet/src/main/java/org/restlet/data/Disposition.java b/org.restlet/src/main/java/org/restlet/data/Disposition.java index 401442fa07..0fe519bf16 100644 --- a/org.restlet/src/main/java/org/restlet/data/Disposition.java +++ b/org.restlet/src/main/java/org/restlet/data/Disposition.java @@ -109,7 +109,7 @@ public String getFilename() { */ public Series getParameters() { if (this.parameters == null) { - this.parameters = new Series(Parameter.class); + this.parameters = new Series<>(Parameter.class); } return this.parameters; diff --git a/org.restlet/src/main/java/org/restlet/data/Encoding.java b/org.restlet/src/main/java/org/restlet/data/Encoding.java index c90d098bd9..934cae3fd5 100644 --- a/org.restlet/src/main/java/org/restlet/data/Encoding.java +++ b/org.restlet/src/main/java/org/restlet/data/Encoding.java @@ -108,8 +108,8 @@ public Encoding(final String name, final String description) { /** {@inheritDoc} */ @Override public boolean equals(final Object object) { - return (object instanceof Encoding) - && getName().equalsIgnoreCase(((Encoding) object).getName()); + return object instanceof Encoding encoding + && getName().equalsIgnoreCase(encoding.getName()); } @Override diff --git a/org.restlet/src/main/java/org/restlet/data/Expectation.java b/org.restlet/src/main/java/org/restlet/data/Expectation.java index a80195bf1b..783f32ed98 100644 --- a/org.restlet/src/main/java/org/restlet/data/Expectation.java +++ b/org.restlet/src/main/java/org/restlet/data/Expectation.java @@ -102,7 +102,7 @@ public List getParameters() { synchronized (this) { r = this.parameters; if (r == null) { - this.parameters = r = new CopyOnWriteArrayList(); + this.parameters = r = new CopyOnWriteArrayList<>(); } } } diff --git a/org.restlet/src/main/java/org/restlet/data/Language.java b/org.restlet/src/main/java/org/restlet/data/Language.java index 675c7556d9..d581a87dd3 100644 --- a/org.restlet/src/main/java/org/restlet/data/Language.java +++ b/org.restlet/src/main/java/org/restlet/data/Language.java @@ -8,6 +8,7 @@ */ package org.restlet.data; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -103,8 +104,8 @@ public Language(final String name, final String description) { /** {@inheritDoc} */ @Override public boolean equals(final Object object) { - return (object instanceof Language) - && getName().equalsIgnoreCase(((Language) object).getName()); + return (object instanceof Language language) + && getName().equalsIgnoreCase(language.getName()); } @Override @@ -147,13 +148,11 @@ public List getSubTags() { synchronized (this) { v = this.subTags; if (v == null) { - List tokens = new CopyOnWriteArrayList(); + List tokens = new CopyOnWriteArrayList<>(); if (getName() != null) { final String[] tags = getName().split("-"); if (tags.length > 0) { - for (int i = 1; i < tags.length; i++) { - tokens.add(tags[i]); - } + tokens.addAll(Arrays.asList(tags).subList(1, tags.length)); } } @@ -188,20 +187,16 @@ public int hashCode() { * @see #isCompatible(Metadata) */ public boolean includes(Metadata included) { - boolean result = equals(ALL) || (included == null) || equals(included); - - if (!result && (included instanceof Language includedLanguage)) { + if (equals(ALL) || (included == null) || equals(included)) { + return true; + } - if (getPrimaryTag().equals(includedLanguage.getPrimaryTag())) { - // Both languages are different - if (getSubTags().equals(includedLanguage.getSubTags())) { - result = true; - } else if (getSubTags().isEmpty()) { - result = true; - } - } + if ((included instanceof Language includedLanguage) + && getPrimaryTag().equals(includedLanguage.getPrimaryTag())) { + // Both languages are different + return getSubTags().equals(includedLanguage.getSubTags()) || getSubTags().isEmpty(); } - return result; + return false; } } diff --git a/org.restlet/src/main/java/org/restlet/data/LocalReference.java b/org.restlet/src/main/java/org/restlet/data/LocalReference.java index e63b5451c6..9f48c60936 100644 --- a/org.restlet/src/main/java/org/restlet/data/LocalReference.java +++ b/org.restlet/src/main/java/org/restlet/data/LocalReference.java @@ -85,7 +85,7 @@ public static LocalReference createClapReference(Package pkg) { * @param pkg The package to identify. */ public static LocalReference createClapReference(int authorityType, Package pkg) { - String pkgPath = pkg.getName().replaceAll("\\.", "/"); + String pkgPath = pkg.getName().replace(".", "/"); return new LocalReference("clap://" + getAuthorityName(authorityType) + "/" + pkgPath); } diff --git a/org.restlet/src/main/java/org/restlet/data/MediaType.java b/org.restlet/src/main/java/org/restlet/data/MediaType.java index f6532a2ea1..b527a5e04f 100644 --- a/org.restlet/src/main/java/org/restlet/data/MediaType.java +++ b/org.restlet/src/main/java/org/restlet/data/MediaType.java @@ -531,11 +531,11 @@ public static MediaType getMostSpecific(MediaType... mediaTypes) MediaType mediaType = mediaTypes[i]; if (mediaType != null) { - if (mediaType.getMainType().equals("*")) { + if ("*".equals(mediaType.getMainType())) { continue; } - if (mostSpecific.getMainType().equals("*")) { + if ("*".equals(mostSpecific.getMainType())) { mostSpecific = mediaType; continue; } diff --git a/org.restlet/src/main/java/org/restlet/data/Metadata.java b/org.restlet/src/main/java/org/restlet/data/Metadata.java index a9aa88402c..69c6add38a 100644 --- a/org.restlet/src/main/java/org/restlet/data/Metadata.java +++ b/org.restlet/src/main/java/org/restlet/data/Metadata.java @@ -50,7 +50,7 @@ public Metadata(String name, String description) { /** {@inheritDoc} */ @Override public boolean equals(Object object) { - return (object instanceof Metadata) && ((Metadata) object).getName().equals(getName()); + return (object instanceof Metadata metadata) && metadata.getName().equals(getName()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Method.java b/org.restlet/src/main/java/org/restlet/data/Method.java index 1ea39c5460..d39d33e789 100644 --- a/org.restlet/src/main/java/org/restlet/data/Method.java +++ b/org.restlet/src/main/java/org/restlet/data/Method.java @@ -22,7 +22,7 @@ public final class Method implements Comparable { /** Map of registered methods. */ - private static final Map _methods = new ConcurrentHashMap(); + private static final Map _methods = new ConcurrentHashMap<>(); /** Pseudo-method use to match all methods. */ public static final Method ALL = new Method("*", "Pseudo-method use to match all methods."); @@ -313,7 +313,7 @@ public int compareTo(Method o) { /** {@inheritDoc} */ @Override public boolean equals(final Object object) { - return (object instanceof Method) && ((Method) object).getName().equals(getName()); + return (object instanceof Method method) && method.getName().equals(getName()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Preference.java b/org.restlet/src/main/java/org/restlet/data/Preference.java index 6780331856..330d3a4c5c 100644 --- a/org.restlet/src/main/java/org/restlet/data/Preference.java +++ b/org.restlet/src/main/java/org/restlet/data/Preference.java @@ -83,7 +83,7 @@ public Series getParameters() { synchronized (this) { p = this.parameters; if (p == null) { - this.parameters = p = new Series(Parameter.class); + this.parameters = p = new Series<>(Parameter.class); } } } diff --git a/org.restlet/src/main/java/org/restlet/data/Protocol.java b/org.restlet/src/main/java/org/restlet/data/Protocol.java index dfc2f30dc0..b99361dece 100644 --- a/org.restlet/src/main/java/org/restlet/data/Protocol.java +++ b/org.restlet/src/main/java/org/restlet/data/Protocol.java @@ -298,8 +298,8 @@ public Protocol( /** {@inheritDoc} */ @Override public boolean equals(final Object object) { - return (object instanceof Protocol) - && getName().equalsIgnoreCase(((Protocol) object).getName()); + return (object instanceof Protocol protocol) + && getName().equalsIgnoreCase(protocol.getName()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Range.java b/org.restlet/src/main/java/org/restlet/data/Range.java index ee9fe01d92..d873b464c8 100644 --- a/org.restlet/src/main/java/org/restlet/data/Range.java +++ b/org.restlet/src/main/java/org/restlet/data/Range.java @@ -111,9 +111,9 @@ public Range(long index, long size, long instanceSize, String unitName) { @Override public boolean equals(Object object) { - return (object instanceof Range) - && ((Range) object).getIndex() == getIndex() - && ((Range) object).getSize() == getSize(); + return (object instanceof Range range) + && range.getIndex() == getIndex() + && range.getSize() == getSize(); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Reference.java b/org.restlet/src/main/java/org/restlet/data/Reference.java index 10a98441cd..c5a16d9dd9 100644 --- a/org.restlet/src/main/java/org/restlet/data/Reference.java +++ b/org.restlet/src/main/java/org/restlet/data/Reference.java @@ -1676,7 +1676,7 @@ public String getSchemeSpecificPart(boolean decode) { * @return The segments of a hierarchical path. */ public List getSegments() { - final List result = new ArrayList(); + final List result = new ArrayList<>(); final String path = getPath(); int start = -2; // The index of the slash starting the segment char current; diff --git a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java index 7e59690ab9..40f892198b 100644 --- a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java +++ b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java @@ -37,7 +37,7 @@ public ReferenceList() { * @param initialCapacity The initial list capacity. */ public ReferenceList(int initialCapacity) { - super(new ArrayList(initialCapacity)); + super(new ArrayList<>(initialCapacity)); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Status.java b/org.restlet/src/main/java/org/restlet/data/Status.java index 7d3b71a7ba..169084ed8b 100644 --- a/org.restlet/src/main/java/org/restlet/data/Status.java +++ b/org.restlet/src/main/java/org/restlet/data/Status.java @@ -474,11 +474,9 @@ public final class Status { * @return The name if it is correct. */ private static String checkReasonPhrase(String reasonPhrase) { - if (reasonPhrase != null) { - if (reasonPhrase.contains("\n") || reasonPhrase.contains("\r")) { - throw new IllegalArgumentException( - "Reason phrase of the status must not contain CR or LF characters."); - } + if (reasonPhrase != null && (reasonPhrase.contains("\n") || reasonPhrase.contains("\r"))) { + throw new IllegalArgumentException( + "Reason phrase of the status must not contain CR or LF characters."); } return reasonPhrase; @@ -822,7 +820,7 @@ public Status(Status status, Throwable throwable, String reasonPhrase, String de */ @Override public boolean equals(final Object object) { - return (object instanceof Status) && (this.code == ((Status) object).getCode()); + return (object instanceof Status status) && (this.code == status.getCode()); } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/Engine.java b/org.restlet/src/main/java/org/restlet/engine/Engine.java index 3f6d77d497..2bd9d6c9d9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Engine.java +++ b/org.restlet/src/main/java/org/restlet/engine/Engine.java @@ -480,22 +480,21 @@ public org.restlet.engine.connector.ConnectorHelper createHelper( (result == null) && iter.hasNext(); ) { connector = iter.next(); - if (new HashSet<>(connector.getProtocols()).containsAll(client.getProtocols())) { - if ((helperClass == null) - || connector.getClass().getCanonicalName().equals(helperClass)) { - try { - result = - connector - .getClass() - .getConstructor(Client.class) - .newInstance(client); - } catch (Exception e) { - Context.getCurrentLogger() - .log( - Level.SEVERE, - "Exception during the instantiation of the client connector.", - e); - } + if (new HashSet<>(connector.getProtocols()).containsAll(client.getProtocols()) + && ((helperClass == null) + || connector.getClass().getCanonicalName().equals(helperClass))) { + try { + result = + connector + .getClass() + .getConstructor(Client.class) + .newInstance(client); + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.SEVERE, + "Exception during the instantiation of the client connector.", + e); } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java index 6c6ac8de33..ad5c4fe5ca 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java @@ -120,9 +120,9 @@ public Call() { this.method = null; this.protocol = null; this.reasonPhrase = ""; - this.requestHeaders = new Series

(Header.class); + this.requestHeaders = new Series<>(Header.class); this.requestUri = null; - this.responseHeaders = new Series
(Header.class); + this.responseHeaders = new Series<>(Header.class); this.serverAddress = null; this.serverPort = -1; this.statusCode = 200; @@ -267,9 +267,8 @@ public int getServerPort() { * Returns the status code. * * @return The status code. - * @throws IOException */ - public int getStatusCode() throws IOException { + public int getStatusCode() { return this.statusCode; } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java index 8ccd8a3a29..641ad4ae83 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java @@ -58,28 +58,27 @@ public void commit(final ClientCall httpCall, Request request, Response response httpCall.sendRequest( request, response, - new Uniform() { - public void handle(Request request, Response response) { - try { - updateResponse( - response, - new Status( - httpCall.getStatusCode(), - httpCall.getReasonPhrase()), - httpCall); - - if (userCallback != null) { - userCallback.handle(request, response); + (Uniform) + (request1, response1) -> { + try { + updateResponse( + response1, + new Status( + httpCall.getStatusCode(), + httpCall.getReasonPhrase()), + httpCall); + + if (userCallback != null) { + userCallback.handle(request1, response1); + } + } catch (Throwable t) { + getLogger() + .log( + Level.WARNING, + "Unexpected error or exception inside the user call back", + t); } - } catch (Throwable t) { - getLogger() - .log( - Level.WARNING, - "Unexpected error or exception inside the user call back", - t); - } - } - }); + }); } else { updateResponse(response, httpCall.sendRequest(request), httpCall); } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java index 4b5c4883e3..2d648bccb9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java @@ -60,10 +60,8 @@ public class JettyClientCall extends ClientCall { * @param helper The parent HTTP client helper. * @param method The method name. * @param requestUri The request URI. - * @throws IOException */ - public JettyClientCall(HttpClientHelper helper, final String method, final String requestUri) - throws IOException { + public JettyClientCall(HttpClientHelper helper, final String method, final String requestUri) { super(helper, method, requestUri); this.clientHelper = helper; @@ -126,11 +124,8 @@ public OutputStream getRequestHeadStream() { @Override public InputStream getResponseEntityStream(long size) { - final InputStreamResponseListener inputStreamResponseListener = - getInputStreamResponseListener(); - return inputStreamResponseListener == null - ? null - : inputStreamResponseListener.getInputStream(); + final InputStreamResponseListener isrl = getInputStreamResponseListener(); + return isrl == null ? null : isrl.getInputStream(); } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java b/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java index 2d52b95c1c..549ecb1bd5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java @@ -109,8 +109,7 @@ public synchronized void start() throws Exception { "By default, an application should be attached to a parent component to let application's outbound root handle calls properly."); setOutboundNext( new Restlet() { - final Map clients = - new ConcurrentHashMap(); + final Map clients = new ConcurrentHashMap<>(); @Override public void handle(Request request, Response response) { @@ -124,17 +123,16 @@ public void handle(Request request, Response response) { : null; if (protocol != null) { - Client c = clients.get(protocol); - - if (c == null) { - c = new Client(protocol); - clients.put(protocol, c); - getLogger() - .fine( - "Added runtime client for protocol: " - + protocol.getName()); - } - + Client c = + clients.computeIfAbsent( + protocol, + p -> { + getLogger() + .fine( + "Added runtime client for protocol: " + + p.getName()); + return new Client(p); + }); c.handle(request, response); } else { response.setStatus( @@ -164,5 +162,7 @@ public synchronized void stop() throws Exception { } @Override - public void update() throws Exception {} + public void update() throws Exception { + // Nothing to do here + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java index 1ce956dda6..34a5ee4117 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java @@ -62,8 +62,7 @@ public static List getSupportedEncodings() { public DecodeRepresentation(Representation wrappedRepresentation) { super(wrappedRepresentation); this.decoding = getSupportedEncodings().containsAll(wrappedRepresentation.getEncodings()); - this.wrappedEncodings = - new CopyOnWriteArrayList(wrappedRepresentation.getEncodings()); + this.wrappedEncodings = new CopyOnWriteArrayList<>(wrappedRepresentation.getEncodings()); } @Override diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java b/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java index 0cf4671210..7f83cbb7f9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java @@ -132,13 +132,11 @@ public Encoding getBestEncoding(ClientInfo client) { for (Preference encodingPreference : client.getAcceptedEncodings()) { currentPref = encodingPreference; - if (currentPref.getMetadata().equals(Encoding.ALL) - || currentPref.getMetadata().equals(currentEncoding)) { - // A match was found, compute its score - if (currentPref.getQuality() > bestScore) { - bestScore = currentPref.getQuality(); - bestEncoding = currentEncoding; - } + if ((currentPref.getMetadata().equals(Encoding.ALL) + || currentPref.getMetadata().equals(currentEncoding)) + && currentPref.getQuality() > bestScore) { + bestScore = currentPref.getQuality(); + bestEncoding = currentEncoding; } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java index 26743b1ca0..4fb6b29e31 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java @@ -106,6 +106,7 @@ protected boolean canAdd(T metadata, List undesired) { * * @return The enriched list of character set preferences. */ + @Override protected List> getCharacterSetPrefs() { return characterSetPrefs; } @@ -115,6 +116,7 @@ protected List> getCharacterSetPrefs() { * * @return The enriched list of encoding preferences. */ + @Override protected List> getEncodingPrefs() { return encodingPrefs; } @@ -144,7 +146,7 @@ protected List> getEnrichedPreferences( for (Preference pref : userPreferences) { if (pref.getQuality() == 0) { if (undesired == null) { - undesired = new ArrayList(); + undesired = new ArrayList<>(); } undesired.add(pref.getMetadata()); } @@ -157,22 +159,20 @@ protected List> getEnrichedPreferences( parent = (T) userPref.getMetadata().getParent(); // Add the parent if it is not proscribed. - if (parent != null) { - if (canAdd(parent, undesired)) { - result.add( - new Preference(parent, 0.005f + (0.001f * userPref.getQuality()))); - } + if (parent != null && canAdd(parent, undesired)) { + final float quality = 0.005f + (0.001f * userPref.getQuality()); + result.add(new Preference<>(parent, quality)); } } // 3) Add the default preference if (defaultValue != null && canAdd(defaultValue, undesired)) { - Preference defaultPref = new Preference(defaultValue, 0.003f); + Preference defaultPref = new Preference<>(defaultValue, 0.003f); result.add(defaultPref); T defaultParent = (T) defaultValue.getParent(); if (defaultParent != null && canAdd(defaultParent, undesired)) { - result.add(new Preference(defaultParent, 0.002f)); + result.add(new Preference<>(defaultParent, 0.002f)); } } @@ -184,7 +184,7 @@ protected List> getEnrichedPreferences( } } - result.add(new Preference(allValue, 0.001f)); + result.add(new Preference<>(allValue, 0.001f)); // 6) Return the enriched preferences return result; @@ -195,6 +195,7 @@ protected List> getEnrichedPreferences( * * @return The enriched list of language preferences. */ + @Override protected List> getLanguagePrefs() { return languagePrefs; } @@ -204,6 +205,7 @@ protected List> getLanguagePrefs() { * * @return The enriched list of media type preferences. */ + @Override protected List> getMediaTypePrefs() { return mediaTypePrefs; } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java index a1d6e99947..3d18f60e76 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java @@ -137,14 +137,12 @@ private float doScoreAnnotation(MethodAnnotationInfo annotation) { for (int j = 0; !constraintMatched && (j < actualParams.size()); j++) { actualParam = actualParams.get(j); - if (constraintParam.getName().equals(actualParam.getName())) { - // Potential match found based on name - if ((constraintParam.getValue() == null) - || constraintParam.getValue().equals(actualParam.getValue())) { - // Actual match found! - constraintMatched = true; - matchedParams.add(actualParam); - } + if (constraintParam.getName().equals(actualParam.getName()) + && ((constraintParam.getValue() == null) + || constraintParam.getValue().equals(actualParam.getValue()))) { + // Actual match found! + constraintMatched = true; + matchedParams.add(actualParam); } } @@ -305,9 +303,9 @@ public float scoreVariant(Variant variant) { float encodingScore = scoreEncodings(variant.getEncodings()); if (encodingScore != -1.0F) { - if (variant instanceof VariantInfo) { + if (variant instanceof VariantInfo variantInfo) { float annotationScore = - scoreAnnotation(((VariantInfo) variant).getAnnotationInfo()); + scoreAnnotation(variantInfo.getAnnotationInfo()); // Return the weighted average score result = @@ -319,7 +317,7 @@ public float scoreVariant(Variant variant) { / 12.0F; // Take into account the affinity with the input // entity - result *= ((VariantInfo) variant).getInputScore(); + result *= variantInfo.getInputScore(); } else { // Return the weighted average score result = diff --git a/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java index ce562c5b1f..64a4f2c730 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java @@ -63,7 +63,7 @@ public class TunnelFilter extends Filter { private static class HeaderReplacer { static class Builder { - Map agentAttributes = new HashMap(); + Map agentAttributes = new HashMap<>(); String newValue; @@ -221,7 +221,7 @@ private List getAcceptReplacers() { */ private List getHeaderReplacers( final URL userAgentPropertiesUrl, String oldHeaderName, String newHeaderName) { - List headerReplacers = new ArrayList(); + List headerReplacers = new ArrayList<>(); if (userAgentPropertiesUrl != null) { BufferedReader reader; @@ -538,42 +538,38 @@ private boolean processQuery(Request request) { */ private void processUserAgent(Request request) { final Map agentAttributes = request.getClientInfo().getAgentAttributes(); - if (agentAttributes != null) { - if (!this.acceptReplacers.isEmpty() || !this.acceptEncodingReplacers.isEmpty()) { - // Get the old Accept header value - @SuppressWarnings("unchecked") - Series
headers = - (Series
) - request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); - - String acceptOld = - (headers != null) - ? headers.getFirstValue(HeaderConstants.HEADER_ACCEPT, true) - : null; - // Check each replacer - for (HeaderReplacer headerReplacer : this.acceptReplacers) { - if (headerReplacer.matchesConditions(agentAttributes, acceptOld)) { - ClientInfo clientInfo = new ClientInfo(); - PreferenceReader.addMediaTypes(headerReplacer.getHeaderNew(), clientInfo); - request.getClientInfo() - .setAcceptedMediaTypes(clientInfo.getAcceptedMediaTypes()); - break; - } + if (agentAttributes != null + && (!this.acceptReplacers.isEmpty() || !this.acceptEncodingReplacers.isEmpty())) { + // Get the old Accept header value + @SuppressWarnings("unchecked") + Series
headers = + (Series
) request.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS); + + String acceptOld = + (headers != null) + ? headers.getFirstValue(HeaderConstants.HEADER_ACCEPT, true) + : null; + // Check each replacer + for (HeaderReplacer headerReplacer : this.acceptReplacers) { + if (headerReplacer.matchesConditions(agentAttributes, acceptOld)) { + ClientInfo clientInfo = new ClientInfo(); + PreferenceReader.addMediaTypes(headerReplacer.getHeaderNew(), clientInfo); + request.getClientInfo() + .setAcceptedMediaTypes(clientInfo.getAcceptedMediaTypes()); + break; } - String acceptEncodingOld = - (headers != null) - ? headers.getFirstValue( - HeaderConstants.HEADER_ACCEPT_ENCODING, true) - : null; - // Check each replacer - for (HeaderReplacer headerReplacer : this.acceptEncodingReplacers) { - if (headerReplacer.matchesConditions(agentAttributes, acceptEncodingOld)) { - ClientInfo clientInfo = new ClientInfo(); - PreferenceReader.addEncodings(headerReplacer.getHeaderNew(), clientInfo); - request.getClientInfo() - .setAcceptedEncodings(clientInfo.getAcceptedEncodings()); - break; - } + } + String acceptEncodingOld = + (headers != null) + ? headers.getFirstValue(HeaderConstants.HEADER_ACCEPT_ENCODING, true) + : null; + // Check each replacer + for (HeaderReplacer headerReplacer : this.acceptEncodingReplacers) { + if (headerReplacer.matchesConditions(agentAttributes, acceptEncodingOld)) { + ClientInfo clientInfo = new ClientInfo(); + PreferenceReader.addEncodings(headerReplacer.getHeaderNew(), clientInfo); + request.getClientInfo().setAcceptedEncodings(clientInfo.getAcceptedEncodings()); + break; } } } @@ -587,18 +583,18 @@ private void processUserAgent(Request request) { * @param metadata The metadata to use. */ private void updateMetadata(ClientInfo clientInfo, Metadata metadata) { - if (metadata instanceof CharacterSet) { + if (metadata instanceof CharacterSet characterSet) { clientInfo.getAcceptedCharacterSets().clear(); - clientInfo.getAcceptedCharacterSets().add(new Preference<>((CharacterSet) metadata)); - } else if (metadata instanceof Encoding) { + clientInfo.getAcceptedCharacterSets().add(new Preference<>(characterSet)); + } else if (metadata instanceof Encoding encoding) { clientInfo.getAcceptedEncodings().clear(); - clientInfo.getAcceptedEncodings().add(new Preference<>((Encoding) metadata)); - } else if (metadata instanceof Language) { + clientInfo.getAcceptedEncodings().add(new Preference<>(encoding)); + } else if (metadata instanceof Language language) { clientInfo.getAcceptedLanguages().clear(); - clientInfo.getAcceptedLanguages().add(new Preference<>((Language) metadata)); - } else if (metadata instanceof MediaType) { + clientInfo.getAcceptedLanguages().add(new Preference<>(language)); + } else if (metadata instanceof MediaType mediaType) { clientInfo.getAcceptedMediaTypes().clear(); - clientInfo.getAcceptedMediaTypes().add(new Preference<>((MediaType) metadata)); + clientInfo.getAcceptedMediaTypes().add(new Preference<>(mediaType)); } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java b/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java index c03f88a76a..78aeb4973f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java @@ -76,13 +76,4 @@ public float score(Request request, Response response) { return result; } - - /** - * Sets the next client. - * - * @param next The next client. - */ - public void setNext(Client next) { - super.setNext(next); - } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java index 53ceb99634..a2e176c176 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java @@ -43,8 +43,8 @@ public ClientRouter(Component component) { @Override protected void logRoute(org.restlet.routing.Route route) { if (getLogger().isLoggable(Level.FINE)) { - if (route instanceof ClientRoute) { - Client client = ((ClientRoute) route).getClient(); + if (route instanceof ClientRoute clientRoute) { + Client client = clientRoute.getClient(); getLogger().fine("This client was selected: \"" + client.getProtocols() + "\""); } else { diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java index 4989aa0787..43e840e676 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java @@ -75,10 +75,10 @@ protected int doHandle(Request request, Response response) { } } - if ((host == null) && (component.getDefaultHost() != null)) { - if (component.getDefaultHost().hashCode() == hostHashCode) { - host = component.getDefaultHost(); - } + if ((host == null) + && (component.getDefaultHost() != null) + && component.getDefaultHost().hashCode() == hostHashCode) { + host = component.getDefaultHost(); } if (host != null) { diff --git a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java index 732d6fc671..a1c9708665 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java @@ -156,7 +156,7 @@ public float score(Request request, Response response) { serverPortValue = request.getProtocol().getDefaultPort(); } - String serverPort = Integer.toString(response.getServerInfo().getPort()); + String serverPort = Integer.toString(serverPortValue); // Check if all the criteria match if (matches(getVirtualHost().getHostDomain(), hostDomain) @@ -183,13 +183,4 @@ && matches(getVirtualHost().getServerPort(), serverPort)) { return result; } - - /** - * Sets the next virtual host. - * - * @param next The next virtual host. - */ - public void setNext(VirtualHost next) { - super.setNext(next); - } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java index b5ba9b7faa..fc04f2c7bc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java @@ -55,8 +55,8 @@ private Component getComponent() { @Override protected void logRoute(org.restlet.routing.Route route) { if (getLogger().isLoggable(Level.FINE)) { - if (route instanceof HostRoute) { - VirtualHost vhost = ((HostRoute) route).getVirtualHost(); + if (route instanceof HostRoute hostRoute) { + VirtualHost vhost = hostRoute.getVirtualHost(); if (getComponent().getDefaultHost() == vhost) { getLogger().fine("Default virtual host selected"); diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java index e8912a701f..4fb40acfe3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.restlet.Connector; -import org.restlet.Context; import org.restlet.data.Protocol; import org.restlet.engine.RestletHelper; @@ -46,17 +45,7 @@ public static org.restlet.service.ConnectorService getConnectorService() { /** Constructor. */ public ConnectorHelper(T connector) { super(connector); - this.protocols = new CopyOnWriteArrayList(); - } - - /** - * Returns the helped Restlet context. - * - * @return The helped Restlet context. - */ - @Override - public Context getContext() { - return super.getContext(); + this.protocols = new CopyOnWriteArrayList<>(); } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java index 69d8496ebc..a70252485e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java @@ -8,7 +8,6 @@ */ package org.restlet.engine.connector; -import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.file.Path; @@ -251,15 +250,11 @@ public HttpClientHelper(Client client) { public ClientCall create(Request request) { ClientCall result = null; - try { - result = - new JettyClientCall( - this, - request.getMethod().toString(), - ReferenceUtils.update(request.getResourceRef(), request).toString()); - } catch (IOException e) { - getLogger().log(Level.WARNING, "Unable to create the Jetty HTTP/HTTPS client call", e); - } + result = + new JettyClientCall( + this, + request.getMethod().toString(), + ReferenceUtils.update(request.getResourceRef(), request).toString()); return result; } @@ -289,17 +284,17 @@ protected HttpClient createHttpClient() { case DYNAMIC -> getHttpClientTransportForDynamicMode(sslContextFactory); }; - final HttpClient httpClient = new HttpClient(httpTransport); - httpClient.setAddressResolutionTimeout(getAddressResolutionTimeout()); + final HttpClient result = new HttpClient(httpTransport); + result.setAddressResolutionTimeout(getAddressResolutionTimeout()); if (getAuthenticationStore() != null) { - httpClient.setAuthenticationStore(getAuthenticationStore()); + result.setAuthenticationStore(getAuthenticationStore()); } - httpClient.setBindAddress(getBindAddress()); - httpClient.setConnectBlocking(isConnectBlocking()); - httpClient.setConnectTimeout(getConnectTimeout()); - httpClient.setDestinationIdleTimeout(getDestinationIdleTimeout()); - httpClient.setExecutor(getExecutor()); - httpClient.setFollowRedirects(isFollowRedirects()); + result.setBindAddress(getBindAddress()); + result.setConnectBlocking(isConnectBlocking()); + result.setConnectTimeout(getConnectTimeout()); + result.setDestinationIdleTimeout(getDestinationIdleTimeout()); + result.setExecutor(getExecutor()); + result.setFollowRedirects(isFollowRedirects()); final String httpComplianceMode = getHttpComplianceMode(); final HttpCompliance httpCompliance = @@ -317,33 +312,33 @@ protected HttpClient createHttpClient() { yield HttpCompliance.RFC7230; } }; - httpClient.setHttpCompliance(httpCompliance); + result.setHttpCompliance(httpCompliance); - httpClient.setHttpCookieStore(getCookieStore()); - httpClient.setIdleTimeout(getIdleTimeout()); - httpClient.setMaxConnectionsPerDestination(getMaxConnectionsPerDestination()); - httpClient.setMaxRedirects(getMaxRedirects()); - httpClient.setMaxRequestsQueuedPerDestination(getMaxRequestsQueuedPerDestination()); - httpClient.setMaxResponseHeadersSize(getMaxResponseHeadersSize()); + result.setHttpCookieStore(getCookieStore()); + result.setIdleTimeout(getIdleTimeout()); + result.setMaxConnectionsPerDestination(getMaxConnectionsPerDestination()); + result.setMaxRedirects(getMaxRedirects()); + result.setMaxRequestsQueuedPerDestination(getMaxRequestsQueuedPerDestination()); + result.setMaxResponseHeadersSize(getMaxResponseHeadersSize()); final String httpProxyHost = getProxyHost(); if (httpProxyHost != null) { HttpProxy proxy = new HttpProxy(httpProxyHost, getProxyPort()); - httpClient.getProxyConfiguration().addProxy(proxy); + result.getProxyConfiguration().addProxy(proxy); } - httpClient.setRequestBufferSize(getRequestBufferSize()); - httpClient.setResponseBufferSize(getResponseBufferSize()); - httpClient.setScheduler(getScheduler()); - httpClient.setSslContextFactory(sslContextFactory); - httpClient.setStrictEventOrdering(isStrictEventOrdering()); + result.setRequestBufferSize(getRequestBufferSize()); + result.setResponseBufferSize(getResponseBufferSize()); + result.setScheduler(getScheduler()); + result.setSslContextFactory(sslContextFactory); + result.setStrictEventOrdering(isStrictEventOrdering()); final String userAgentField = getUserAgentField(); if (userAgentField != null) { - httpClient.setUserAgentField(new HttpField(HttpHeader.USER_AGENT, userAgentField)); + result.setUserAgentField(new HttpField(HttpHeader.USER_AGENT, userAgentField)); } - return httpClient; + return result; } private static HttpClientTransportOverHTTP getHttpTransportForHttp1_1() { @@ -713,7 +708,6 @@ public void start() throws Exception { this.httpClient = createHttpClient(); } - final HttpClient httpClient = getHttpClient(); if (httpClient != null) { getLogger().info("Starting a Jetty HTTP/HTTPS client"); httpClient.start(); @@ -722,7 +716,6 @@ public void start() throws Exception { @Override public void stop() throws Exception { - final HttpClient httpClient = getHttpClient(); if (httpClient != null) { getLogger().info("Stopping a Jetty HTTP/HTTPS client"); httpClient.stop(); diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java index caa574e330..2b63b7f0de 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java @@ -379,7 +379,6 @@ public boolean handle(Request request, Response response, Callback callback) { jettyServerHelper.handle(httpCall); return true; // Indicates that the request is accepted } - ; }; if (getShutdownGracefully()) { @@ -732,7 +731,7 @@ public void start() throws Exception { } @Override - public void stop() throws Exception { + public synchronized void stop() throws Exception { getLogger() .info( "Stopping the Jetty " diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java index ee9cbcd21e..f5e28595ed 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java @@ -19,6 +19,8 @@ */ public class ServerHelper extends ConnectorHelper { + private static final String ATTRIBUTE_EPHEMERAL_PORT = "ephemeralPort"; + /** * Constructor. * @@ -26,9 +28,7 @@ public class ServerHelper extends ConnectorHelper { */ public ServerHelper(Server server) { super(server); - - // Clear the ephemeral port - getAttributes().put("ephemeralPort", -1); + clearEphemeralPort(); } /** @@ -50,10 +50,9 @@ public void handle(Request request, Response response) { * @param localPort The ephemeral local port. */ public void setEphemeralPort(int localPort) { - // If an ephemeral port is used, make sure we update the attribute for - // the API + // If an ephemeral port is used, make sure we update the attribute for the API if (getHelped().getPort() == 0) { - getAttributes().put("ephemeralPort", localPort); + getAttributes().put(ATTRIBUTE_EPHEMERAL_PORT, localPort); } } @@ -69,8 +68,11 @@ public void setEphemeralPort(java.net.ServerSocket socket) { @Override public synchronized void stop() throws Exception { super.stop(); + clearEphemeralPort(); + } + private void clearEphemeralPort() { // Clear the ephemeral port - getAttributes().put("ephemeralPort", -1); + getAttributes().put(ATTRIBUTE_EPHEMERAL_PORT, -1); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java index 2bdc6ffbcb..e3fba6eb5f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java @@ -36,7 +36,7 @@ public abstract class ConverterHelper extends Helper { */ protected List> addObjectClass(List> objectClasses, Class objectClass) { if (objectClasses == null) { - objectClasses = new ArrayList>(); + objectClasses = new ArrayList<>(); } objectClasses.add(objectClass); diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java index 24beb240b1..724fd9ab39 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java @@ -8,6 +8,9 @@ */ package org.restlet.engine.converter; +import static org.restlet.data.MediaType.APPLICATION_OCTET_STREAM; +import static org.restlet.data.MediaType.TEXT_PLAIN; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -211,7 +214,7 @@ public float score(Representation source, Class target, Resource resource @Override public T toObject(Representation source, Class target, Resource resource) throws IOException { - Object result = null; + final Object result; if (target != null) { if (target.isAssignableFrom(source.getClass())) { @@ -223,8 +226,8 @@ public T toObject(Representation source, Class target, Resource resource) } else if (EmptyRepresentation.class.isAssignableFrom(target)) { result = null; } else if (File.class.isAssignableFrom(target)) { - if (source instanceof FileRepresentation) { - result = ((FileRepresentation) source).getFile(); + if (source instanceof FileRepresentation fileRepresentation) { + result = fileRepresentation.getFile(); } else { result = null; } @@ -239,8 +242,8 @@ public T toObject(Representation source, Class target, Resource resource) } else if (ReaderRepresentation.class.isAssignableFrom(target)) { result = new ReaderRepresentation(source.getReader()); } else if (Serializable.class.isAssignableFrom(target) || target.isPrimitive()) { - if (source instanceof ObjectRepresentation) { - result = ((ObjectRepresentation) source).getObject(); + if (source instanceof ObjectRepresentation objectRepresentation) { + result = objectRepresentation.getObject(); } else { try { result = new ObjectRepresentation<>(source).getObject(); @@ -251,9 +254,13 @@ public T toObject(Representation source, Class target, Resource resource) throw ioe; } } + } else { + result = null; } - } else if (source instanceof ObjectRepresentation) { - result = ((ObjectRepresentation) source).getObject(); + } else if (source instanceof ObjectRepresentation objectRepresentation) { + result = objectRepresentation.getObject(); + } else { + result = null; } return (T) result; @@ -262,40 +269,37 @@ public T toObject(Representation source, Class target, Resource resource) @Override public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { - Representation result = null; + final Representation result; - if (source instanceof String) { - result = - new StringRepresentation( - (String) source, - MediaType.getMostSpecific(target.getMediaType(), MediaType.TEXT_PLAIN)); - } else if (source instanceof File) { - result = - new FileRepresentation( - (File) source, - MediaType.getMostSpecific( - target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); - } else if (source instanceof Form) { - result = ((Form) source).getWebRepresentation(); - } else if (source instanceof InputStream) { - result = - new InputRepresentation( - (InputStream) source, - MediaType.getMostSpecific( - target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); - } else if (source instanceof Reader) { - result = - new ReaderRepresentation( - (Reader) source, - MediaType.getMostSpecific(target.getMediaType(), MediaType.TEXT_PLAIN)); - } else if (source instanceof Representation) { - result = (Representation) source; - } else if (source instanceof Serializable) { - result = - new ObjectRepresentation<>( - (Serializable) source, - MediaType.getMostSpecific( - target.getMediaType(), MediaType.APPLICATION_OCTET_STREAM)); + switch (source) { + case String string -> { + final MediaType mediaType = + MediaType.getMostSpecific(target.getMediaType(), TEXT_PLAIN); + result = new StringRepresentation(string, mediaType); + } + case File file -> { + final MediaType mediaType = + MediaType.getMostSpecific(target.getMediaType(), APPLICATION_OCTET_STREAM); + result = new FileRepresentation(file, mediaType); + } + case Form form -> result = form.getWebRepresentation(); + case InputStream inputStream -> { + final MediaType mediaType = + MediaType.getMostSpecific(target.getMediaType(), APPLICATION_OCTET_STREAM); + result = new InputRepresentation(inputStream, mediaType); + } + case Reader reader -> { + final MediaType mediaType = + MediaType.getMostSpecific(target.getMediaType(), TEXT_PLAIN); + result = new ReaderRepresentation(reader, mediaType); + } + case Representation representation -> result = representation; + case Serializable serializable -> { + final MediaType mediaType = + MediaType.getMostSpecific(target.getMediaType(), APPLICATION_OCTET_STREAM); + result = new ObjectRepresentation<>(serializable, mediaType); + } + case null, default -> result = null; } return result; @@ -313,10 +317,10 @@ public void updatePreferences(List> preferences, Class updatePreferences(preferences, MediaType.APPLICATION_JAVA_OBJECT_XML, 1.0F); } } else if (String.class.isAssignableFrom(entity) || Reader.class.isAssignableFrom(entity)) { - updatePreferences(preferences, MediaType.TEXT_PLAIN, 1.0F); + updatePreferences(preferences, TEXT_PLAIN, 1.0F); updatePreferences(preferences, MediaType.TEXT_ALL, 0.5F); } else if (InputStream.class.isAssignableFrom(entity)) { - updatePreferences(preferences, MediaType.APPLICATION_OCTET_STREAM, 1.0F); + updatePreferences(preferences, APPLICATION_OCTET_STREAM, 1.0F); updatePreferences(preferences, MediaType.APPLICATION_ALL, 0.5F); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java index 08ecd3bb39..063d475da9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java @@ -34,6 +34,7 @@ public static String write(Collection dimensions) { * @param dimensions The dimensions to format. * @return This writer. */ + @Override public DimensionWriter append(Collection dimensions) { if ((dimensions != null) && !dimensions.isEmpty()) { if (dimensions.contains(Dimension.CLIENT_ADDRESS) diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java index 7ba777ecfd..98995f87c6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java @@ -14,6 +14,9 @@ * @author Jerome Louvel */ public final class HeaderConstants { + private HeaderConstants() { + /* This utility class should not be instantiated */ + } // -------------------- // --- Expectations --- diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java index 219bb23d07..3c5ed3ba6e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java @@ -617,7 +617,7 @@ public V readValue() throws IOException { * @return A new list with all values added. */ public List readValues() { - List result = new CopyOnWriteArrayList(); + List result = new CopyOnWriteArrayList<>(); addValues(result); return result; } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java index 1a2b4f800d..79b7f25864 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java @@ -184,7 +184,7 @@ protected Preference createPreference(CharSequence metadata, Series result; if (parameters == null) { - result = new Preference(); + result = new Preference<>(); switch (this.type) { case TYPE_CHARACTER_SET: @@ -207,7 +207,7 @@ protected Preference createPreference(CharSequence metadata, Series mediaParams = extractMediaParams(parameters); final float quality = extractQuality(parameters); - result = new Preference(null, quality, parameters); + result = new Preference<>(null, quality, parameters); switch (this.type) { case TYPE_CHARACTER_SET: @@ -298,6 +298,7 @@ protected float extractQuality(Series parameters) { * * @return The next preference. */ + @Override public Preference readValue() throws IOException { Preference result = null; @@ -334,7 +335,7 @@ public Preference readValue() throws IOException { readingMetadata = false; readingParamName = true; paramNameBuffer = new StringBuilder(); - parameters = new Series(Parameter.class); + parameters = new Series<>(Parameter.class); } } else if (isSpace(next)) { // Ignore spaces diff --git a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java index 3e3b5eb2d3..1b08036560 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java @@ -8,7 +8,6 @@ */ package org.restlet.engine.header; -import java.io.IOException; import java.util.List; import org.restlet.data.Parameter; import org.restlet.data.Preference; @@ -34,7 +33,6 @@ public static boolean isValidQuality(float quality) { * * @param prefs The list of preferences. * @return The formatted list of preferences. - * @throws IOException */ @SuppressWarnings({"unchecked", "rawtypes"}) public static String write(List prefs) { diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java index 805f96107a..cca7fdfea0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java @@ -28,7 +28,7 @@ public class ProductReader { * instances. */ public static List read(String userAgent) throws IllegalArgumentException { - final List result = new ArrayList(); + final List result = new ArrayList<>(); if (userAgent != null) { String token = null; diff --git a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java index f53176c18a..d7e3660b2a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java +++ b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java @@ -14,7 +14,6 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; -import org.osgi.framework.BundleListener; import org.restlet.Client; import org.restlet.Server; import org.restlet.engine.Engine; @@ -123,16 +122,15 @@ public void start(BundleContext context) throws Exception { // Listen to installed bundles context.addBundleListener( - new BundleListener() { - public void bundleChanged(BundleEvent event) { - switch (event.getType()) { - case BundleEvent.INSTALLED: - registerHelpers(event.getBundle()); - break; - - case BundleEvent.UNINSTALLED: - break; - } + event -> { + switch (event.getType()) { + case BundleEvent.INSTALLED: + registerHelpers(event.getBundle()); + break; + case BundleEvent.UNINSTALLED: + break; + default: + break; } }); diff --git a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java index 9e35ec839a..42296291fe 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java @@ -340,16 +340,7 @@ public static java.io.OutputStream getStream(java.io.Writer writer, CharacterSet * @return An input stream based on a given character reader. */ public static InputStream getStream(Reader reader, CharacterSet characterSet) { - InputStream result = null; - - try { - result = new ReaderInputStream(reader, characterSet); - } catch (IOException e) { - Context.getCurrentLogger() - .log(Level.WARNING, "Unable to create the reader input stream", e); - } - - return result; + return new ReaderInputStream(reader, characterSet); } /** @@ -574,8 +565,8 @@ public static String toString(Reader reader) { try { StringBuilder sb = new StringBuilder(); BufferedReader br = - (reader instanceof BufferedReader) - ? (BufferedReader) reader + (reader instanceof BufferedReader bufferedReader) + ? bufferedReader : new BufferedReader(reader, BUFFER_SIZE); char[] buffer = new char[2048]; int charsRead = br.read(buffer); diff --git a/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java index b093cc86e4..1ad0b08dda 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java @@ -124,7 +124,7 @@ public int read() throws IOException { @Override public int read(byte[] b, int off, int len) throws IOException { // Reach the start index. - while (!(position >= startIndex)) { + while (position < startIndex) { long skipped = skip(startIndex - position); if (skipped <= 0) { diff --git a/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java index 6eb3f13a78..9b06b17b85 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java @@ -44,9 +44,8 @@ public class ReaderInputStream extends InputStream { * Constructor. Uses the {@link CharacterSet#ISO_8859_1} character set by default. * * @param reader The reader to wrap as an input stream. - * @throws IOException */ - public ReaderInputStream(Reader reader) throws IOException { + public ReaderInputStream(Reader reader) { this(reader, CharacterSet.ISO_8859_1); } @@ -55,9 +54,8 @@ public ReaderInputStream(Reader reader) throws IOException { * * @param reader The reader to wrap as an input stream. * @param characterSet The character set to use for encoding. - * @throws IOException */ - public ReaderInputStream(Reader reader, CharacterSet characterSet) throws IOException { + public ReaderInputStream(Reader reader, CharacterSet characterSet) { this.byteBuffer = ByteBuffer.allocate(1024); this.byteBuffer.flip(); this.charBuffer = CharBuffer.allocate(1024); @@ -68,8 +66,8 @@ public ReaderInputStream(Reader reader, CharacterSet characterSet) throws IOExce : characterSet.toCharset().newEncoder(); this.endReached = false; this.reader = - (reader instanceof BufferedReader) - ? (BufferedReader) reader + (reader instanceof BufferedReader bufferedReader) + ? bufferedReader : new BufferedReader(reader, IoUtils.BUFFER_SIZE); } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java index 586db5ee7b..8cd4430e27 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java @@ -29,5 +29,7 @@ public UnclosableInputStream(InputStream source) { } @Override - public void close() throws IOException {} + public void close() throws IOException { + // Do nothing + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java index f716085423..7729715e59 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java @@ -29,5 +29,7 @@ public UnclosableOutputStream(OutputStream source) { } @Override - public void close() throws IOException {} + public void close() throws IOException { + // Do nothing + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java index bfeca911e2..439bc6d460 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java @@ -795,22 +795,22 @@ public void preventUpperDirectoryAccess() { @Override public Representation put(Representation entity) throws ResourceException { - if (!this.directory.isModifiable()) { + if (this.directory + .isModifiable()) { // Transfer of PUT calls is only allowed if the readOnly flag is + // not set. + Request contextRequest = new Request(Method.PUT, this.targetUri); + + // Add support for partial PUT calls. + contextRequest.getRanges().addAll(getRanges()); + contextRequest.setEntity(entity); + Response contextResponse = new Response(contextRequest); + contextRequest.setResourceRef(this.targetUri); + dispatchRequest(contextRequest, contextResponse); + setStatus(contextResponse.getStatus()); + } else { setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED, "The directory is not modifiable."); - return null; } - // Transfer of PUT calls is only allowed if the readOnly flag is not set. - Request contextRequest = new Request(Method.PUT, this.targetUri); - - // Add support for partial PUT calls. - contextRequest.getRanges().addAll(getRanges()); - contextRequest.setEntity(entity); - Response contextResponse = new Response(contextRequest); - contextRequest.setResourceRef(this.targetUri); - dispatchRequest(contextRequest, contextResponse); - setStatus(contextResponse.getStatus()); - return null; } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java index 6e265778cc..97bf1d9e41 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java @@ -134,27 +134,27 @@ public static void updateMetadata( if (current != null) { // Metadata extension detected - if (current instanceof MediaType) { - variant.setMediaType((MediaType) current); - } else if (current instanceof CharacterSet) { - variant.setCharacterSet((CharacterSet) current); - } else if (current instanceof Encoding) { + if (current instanceof MediaType mediaType) { + variant.setMediaType(mediaType); + } else if (current instanceof CharacterSet characterSet) { + variant.setCharacterSet(characterSet); + } else if (current instanceof Encoding encoding) { // Do we need to add this metadata? boolean found = false; for (int i = 0; !found && i < variant.getEncodings().size(); i++) { found = current.includes(variant.getEncodings().get(i)); } if (!found) { - variant.getEncodings().add((Encoding) current); + variant.getEncodings().add(encoding); } - } else if (current instanceof Language) { + } else if (current instanceof Language language) { // Do we need to add this metadata? boolean found = false; for (int i = 0; !found && i < variant.getLanguages().size(); i++) { found = current.includes(variant.getLanguages().get(i)); } if (!found) { - variant.getLanguages().add((Language) current); + variant.getLanguages().add(language); } } } @@ -165,8 +165,8 @@ public static void updateMetadata( // Try to find a language matching the primary part of the extension. final String primaryPart = tokens[j].substring(0, dashIndex); current = metadataService.getMetadata(primaryPart); - if (current instanceof Language) { - variant.getLanguages().add((Language) current); + if (current instanceof Language language) { + variant.getLanguages().add(language); } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java index 355e64aea6..81a0b34bcc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java @@ -320,7 +320,7 @@ private Status doHandleFilePut(Request request, String path, File file) { File[] files = file.getParentFile().listFiles(filter); File uniqueVariant = null; - List variantsList = new ArrayList(); + List variantsList = new ArrayList<>(); if (files != null && files.length > 0) { // Set the list of extensions, due to the file name and the @@ -407,13 +407,10 @@ private Status doHandleFilePut(Request request, String path, File file) { // The file does not exist yet. File parent = file.getParentFile(); - if ((parent != null) && !parent.exists()) { - // Create the parent directories then the new file - if (!parent.mkdirs()) { - String message = "Unable to create the parent directory"; - getLogger().warning(message); - return new Status(SERVER_ERROR_INTERNAL, message); - } + if ((parent != null) && !parent.exists() && !parent.mkdirs()) { + String message = "Unable to create the parent directory"; + getLogger().warning(message); + return new Status(SERVER_ERROR_INTERNAL, message); } // Create the new file @@ -570,7 +567,7 @@ private Status createFile(Request request, File file) { } private void cleanTemporaryFileIfUploadNotResumed(File tmp) { - if (tmp.exists() && !isResumeUpload()) { + if (tmp != null && tmp.exists() && !isResumeUpload()) { IoUtils.delete(tmp); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java index 1cd232effe..a93424810a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java @@ -26,28 +26,29 @@ public class SimplerFormatter extends Formatter { /** * Format the given LogRecord. * - * @param record the log record to be formatted. + * @param logRecord the log record to be formatted. * @return a formatted log record */ - public synchronized String format(LogRecord record) { + public synchronized String format(LogRecord logRecord) { StringBuilder sb = new StringBuilder(); - sb.append(record.getLevel().getLocalizedName()); + sb.append(logRecord.getLevel().getLocalizedName()); sb.append(" ["); - sb.append(record.getLoggerName()); + sb.append(logRecord.getLoggerName()); sb.append("] - "); - sb.append(formatMessage(record)); + sb.append(formatMessage(logRecord)); sb.append('\n'); - if (record.getThrown() != null) { + if (logRecord.getThrown() != null) { try { sb.append(System.lineSeparator()); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); - record.getThrown().printStackTrace(pw); + logRecord.getThrown().printStackTrace(pw); pw.close(); sb.append(sw); } catch (Exception ignored) { + // Ignored } } diff --git a/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java index 7d29c7e35f..6edb9d2a5d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java @@ -26,24 +26,25 @@ public class SimplestFormatter extends Formatter { /** * Format the given LogRecord. * - * @param record the log record to be formatted. + * @param logRecord the log record to be formatted. * @return a formatted log record */ - public synchronized String format(LogRecord record) { + public synchronized String format(LogRecord logRecord) { StringBuilder sb = new StringBuilder(); - sb.append(formatMessage(record)); + sb.append(formatMessage(logRecord)); sb.append('\n'); - if (record.getThrown() != null) { + if (logRecord.getThrown() != null) { try { sb.append(System.lineSeparator()); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); - record.getThrown().printStackTrace(pw); + logRecord.getThrown().printStackTrace(pw); pw.close(); sb.append(sw); } catch (Exception ignored) { + // Ignored } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java index e63ebf6cda..4d1c046c9b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java @@ -91,10 +91,10 @@ protected static Class getTypeClass(Type type) { if (type instanceof Class) { result = (Class) type; - } else if (type instanceof ParameterizedType) { - result = getTypeClass(((ParameterizedType) type).getRawType()); - } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType) type).getGenericComponentType(); + } else if (type instanceof ParameterizedType parameterizedType) { + result = getTypeClass(parameterizedType.getRawType()); + } else if (type instanceof GenericArrayType genericArrayType) { + Type componentType = genericArrayType.getGenericComponentType(); Class componentClass = getTypeClass(componentType); if (componentClass != null) { diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java index dbd830ff9e..e034f26c3b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java @@ -31,7 +31,7 @@ public class AnnotationUtils { /** Annotation info cache. */ private static final ConcurrentMap, List> cache = - new ConcurrentHashMap, List>(); + new ConcurrentHashMap<>(); /** Current instance. */ private static final AnnotationUtils instance = new AnnotationUtils(); @@ -67,11 +67,11 @@ private List addAnnotations( if (clazz.isInterface()) { for (java.lang.reflect.Method javaMethod : clazz.getMethods()) { - addMethodAnnotationDescriptors(result, clazz, initialClass, javaMethod); + addMethodAnnotationDescriptors(result, initialClass, javaMethod); } } else { for (java.lang.reflect.Method javaMethod : clazz.getDeclaredMethods()) { - addMethodAnnotationDescriptors(result, clazz, initialClass, javaMethod); + addMethodAnnotationDescriptors(result, initialClass, javaMethod); } } @@ -93,14 +93,12 @@ private List addAnnotations( * Computes the annotation descriptors for the given Java method. * * @param descriptors The annotation descriptors to update or null to create a new one. - * @param clazz The class or interface that hosts the javaMethod. * @param initialClass The class or interface that runs the javaMethod. * @param javaMethod The Java method to inspect. * @return The annotation descriptors. */ private List addMethodAnnotationDescriptors( List descriptors, - Class clazz, Class initialClass, java.lang.reflect.Method javaMethod) { List result = descriptors; @@ -217,7 +215,7 @@ public synchronized List getAnnotations(Class clazz) { */ public List getAnnotations( Class clazz, java.lang.reflect.Method javaMethod) { - return addMethodAnnotationDescriptors(null, clazz, clazz, javaMethod); + return addMethodAnnotationDescriptors(null, clazz, javaMethod); } /** @@ -231,9 +229,9 @@ public MethodAnnotationInfo getMethodAnnotation( List annotations, java.lang.reflect.Method javaMethod) { if (annotations != null) { for (AnnotationInfo annotationInfo : annotations) { - if (annotationInfo instanceof MethodAnnotationInfo + if (annotationInfo instanceof MethodAnnotationInfo methodAnnotationInfo && annotationInfo.getJavaMethod().equals(javaMethod)) { - return (MethodAnnotationInfo) annotationInfo; + return methodAnnotationInfo; } } } @@ -263,16 +261,10 @@ public MethodAnnotationInfo getMethodAnnotation( throws IOException { if (annotations != null) { for (AnnotationInfo annotationInfo : annotations) { - if (annotationInfo instanceof MethodAnnotationInfo) { - if (((MethodAnnotationInfo) annotationInfo) - .isCompatible( - restletMethod, - query, - entity, - metadataService, - converterService)) { - return (MethodAnnotationInfo) annotationInfo; - } + if (annotationInfo instanceof MethodAnnotationInfo methodAnnotationInfo + && methodAnnotationInfo.isCompatible( + restletMethod, query, entity, metadataService, converterService)) { + return methodAnnotationInfo; } } } @@ -303,8 +295,8 @@ public ThrowableAnnotationInfo getThrowableAnnotationInfo(Class clazz) { List annotationInfos = getAnnotations(clazz); for (AnnotationInfo annotationInfo : annotationInfos) { - if (annotationInfo instanceof ThrowableAnnotationInfo) { - return (ThrowableAnnotationInfo) annotationInfo; + if (annotationInfo instanceof ThrowableAnnotationInfo throwableAnnotationInfo) { + return throwableAnnotationInfo; } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java b/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java index f6ccbb2cfd..10c5708341 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java @@ -141,19 +141,21 @@ public Object invoke(Object proxy, java.lang.reflect.Method javaMethod, Object[] final Result rCallback = (Result) o; Type[] genericParameterTypes = javaMethod.getGenericParameterTypes(); Type genericParameterType = genericParameterTypes[i]; - ParameterizedType parameterizedType = - (genericParameterType - instanceof java.lang.reflect.ParameterizedType) - ? (java.lang.reflect.ParameterizedType) - genericParameterType - : null; - final Class actualType = - (parameterizedType != null - && parameterizedType.getActualTypeArguments()[0] - instanceof Class) - ? (Class) - parameterizedType.getActualTypeArguments()[0] - : null; + + final Class actualType; + if (genericParameterType + instanceof ParameterizedType parameterizedType) { + if (parameterizedType.getActualTypeArguments()[0] + instanceof Class) { + actualType = + (Class) + parameterizedType.getActualTypeArguments()[0]; + } else { + actualType = null; + } + } else { + actualType = null; + } // Define the callback Uniform callback = diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java index 0eff3f1589..247e0cccbf 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java @@ -272,21 +272,21 @@ private List getVariants(MetadataService metadataService, String annota if (metadataList != null) { for (Metadata metadata : metadataList) { - if (metadata instanceof MediaType) { + if (metadata instanceof MediaType mediaType) { if (mediaTypes == null) { mediaTypes = new ArrayList<>(); } - mediaTypes.add((MediaType) metadata); - } else if (metadata instanceof Language) { + mediaTypes.add(mediaType); + } else if (metadata instanceof Language language) { if (languages == null) { languages = new ArrayList<>(); } - languages.add((Language) metadata); - } else if (metadata instanceof CharacterSet) { + languages.add(language); + } else if (metadata instanceof CharacterSet characterSet1) { if (characterSet == null) { - characterSet = (CharacterSet) metadata; + characterSet = characterSet1; } else { Context.getCurrentLogger() .warning( diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java index e973897061..8994223b17 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java @@ -298,7 +298,7 @@ public static AuthenticationInfo parseAuthenticationInfo(String header) { */ public static List parseRequest( Response response, String header, Series
httpHeaders) { - List result = new ArrayList(); + List result = new ArrayList<>(); if (header != null) { result = new ChallengeRequestReader(header).readValues(); diff --git a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java index c908bf6263..7cb59caae7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java @@ -109,7 +109,7 @@ public void formatResponse( public void parseRequest( ChallengeRequest challenge, Response response, Series
httpHeaders) { if (challenge.getRawValue() != null) { - HeaderReader hr = new HeaderReader(challenge.getRawValue()); + HeaderReader hr = new HeaderReader<>(challenge.getRawValue()); try { Parameter param = hr.readParameter(); diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java index 527518c482..110602a523 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java @@ -456,7 +456,7 @@ public String getSecureRandomAlgorithm() { * @return The selected cipher suites. */ public String[] getSelectedCipherSuites(String[] supportedCipherSuites) { - Set resultSet = new HashSet(); + Set resultSet = new HashSet<>(); if (supportedCipherSuites != null) { for (String supportedCipherSuite : supportedCipherSuites) { @@ -483,7 +483,7 @@ public String[] getSelectedCipherSuites(String[] supportedCipherSuites) { * @return The selected SSL protocols. */ public String[] getSelectedSslProtocols(String[] supportedProtocols) { - Set resultSet = new HashSet(); + Set resultSet = new HashSet<>(); if (supportedProtocols != null) { for (String supportedProtocol : supportedProtocols) { diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java index 2d941fff0b..c7369d68b2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java @@ -24,8 +24,7 @@ public class SslUtils { /** Cache of SSL key sizes for various cipher suites. */ - private static final ConcurrentMap keySizesCache = - new ConcurrentHashMap(); + private static final ConcurrentMap keySizesCache = new ConcurrentHashMap<>(); /** * Extract the SSL key size of a given cipher suite. diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java index 0ecaa0d00b..e25dbe6a94 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.net.InetAddress; import java.net.Socket; -import java.net.UnknownHostException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -71,14 +70,14 @@ public Socket createSocket(Socket s, String host, int port, boolean autoClose) } @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + public Socket createSocket(String host, int port) throws IOException { SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port); return initSslSocket(result); } @Override public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) - throws IOException, UnknownHostException { + throws IOException { SSLSocket result = (SSLSocket) getWrappedSocketFactory().createSocket(host, port, localAddress, localPort); diff --git a/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java b/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java index bd0fb6a39a..c5dbc99db8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java @@ -36,13 +36,15 @@ public int compare(final String uri0, final String uri1) { int ptr = 0; int msd = 0; int diff = 0; - char a, b; + char a; + char b; - final int llength = uri0.length(); - final int rlength = uri1.length(); - final int min = Math.min(rlength, llength); + final int lLength = uri0.length(); + final int rLength = uri1.length(); + final int min = Math.min(rLength, lLength); - boolean rAtEnd, rHasNoMoreDigits; + boolean rAtEnd; + boolean rHasNoMoreDigits; while (ptr < min) { a = uri0.charAt(ptr); @@ -60,9 +62,9 @@ public int compare(final String uri0, final String uri1) { msd = diff; } - rAtEnd = rlength - ptr < 2; + rAtEnd = rLength - ptr < 2; - if (llength - ptr < 2) { + if (lLength - ptr < 2) { if (rAtEnd) { return msd; } @@ -96,7 +98,7 @@ public int compare(final String uri0, final String uri1) { } ptr++; } - return llength - rlength; + return lLength - rLength; } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java index ffb92a6787..ec40d30d72 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java @@ -22,8 +22,7 @@ public class BeanInfoUtils { /** BeanInfo cache. */ - private static final ConcurrentMap, BeanInfo> cache = - new ConcurrentHashMap, BeanInfo>(); + private static final ConcurrentMap, BeanInfo> cache = new ConcurrentHashMap<>(); /** * Get a BeanInfo from the cache or create it. Stop introspection to {@link Object} or {@link diff --git a/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java b/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java index 443588bdb2..6bc6bd4d9b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java @@ -21,7 +21,7 @@ public class CaseInsensitiveHashSet extends HashSet { * * @param source The source collection to use for initialization. */ - public CaseInsensitiveHashSet(Collection source) { + public CaseInsensitiveHashSet(Collection source) { super(source); } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java index c414763d8b..2d5fd0766b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java @@ -73,15 +73,13 @@ public int doHandle(Request request, Response response) { } } else { if ((getChildContext() != null) - && (getChildContext().getChild() instanceof Application application)) { - - if (!application.getConnectorService().getClientProtocols().contains(protocol)) { - getLogger() - .fine( - "The protocol used by this request is not declared in the application's connector service (" - + protocol - + "). Please update the list of client connectors used by your application and restart it."); - } + && (getChildContext().getChild() instanceof Application application) + && !application.getConnectorService().getClientProtocols().contains(protocol)) { + getLogger() + .fine( + "The protocol used by this request is not declared in the application's connector service (" + + protocol + + "). Please update the list of client connectors used by your application and restart it."); } parentHandle(request, response); diff --git a/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java b/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java index 395544b14c..49455bf161 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java @@ -136,7 +136,7 @@ protected Engine getEngine() { @Override public Enumeration getResources(String name) throws IOException { Enumeration allUrls = super.getResources(name); - Vector result = new Vector(); + Vector result = new Vector<>(); if (allUrls != null) { try { diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java index a263077c2b..97c860427e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java @@ -35,7 +35,7 @@ public static List copySubList(List list, int fromIndex, int toIndex) if (fromIndex > toIndex) throw new IllegalArgumentException( "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); - ArrayList subList = new ArrayList(); + ArrayList subList = new ArrayList<>(); for (int i = fromIndex; i <= toIndex; i++) { subList.add(list.get(i)); } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java index 25d76a7b4f..1d58b057f5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java @@ -90,7 +90,7 @@ protected void clear(T object) {} * @return The store of reusable objects. */ protected Queue createStore() { - return new ConcurrentLinkedQueue(); + return new ConcurrentLinkedQueue<>(); } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java index 5774626f59..9534815ee6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java @@ -27,7 +27,7 @@ public class SetUtils { */ @SafeVarargs public static Set newHashSet(E... elements) { - HashSet set = new HashSet<>(elements.length); + HashSet set = HashSet.newHashSet(elements.length); Collections.addAll(set, elements); return set; } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java index cd3e0d4b5a..3b3978dd1b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java @@ -44,6 +44,7 @@ protected void afterHandle(Request request, Response response) { * @param request The request to handle. * @param response The response to update. */ + @Override public int beforeHandle(Request request, Response response) { // Associate the response to the current thread Protocol protocol = request.getProtocol(); diff --git a/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java index d031cae78f..7a5ae73584 100644 --- a/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java @@ -170,6 +170,7 @@ public void release() { try { IoUtils.delete(getFile(), true); } catch (Exception ignored) { + // Ignored } } From 59e7f918d2d0d4e14d76b19c7f2fb6c8c371a73b Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Fri, 3 Apr 2026 23:41:01 +0200 Subject: [PATCH 07/30] Take into account Sonar comments --- .../org/restlet/ext/crypto/DigestUtils.java | 1 - .../ext/freemarker/ContextTemplateLoader.java | 6 +- .../ext/freemarker/FreemarkerConverter.java | 4 +- .../internal/ResolverHashModel.java | 4 +- .../org/restlet/ext/gson/GsonConverter.java | 4 +- .../ext/jaas/ChallengeCallbackHandler.java | 13 +-- .../java/org/restlet/ext/jaas/JaasUtils.java | 3 +- .../org/restlet/ext/json/JsonConverter.java | 26 ++--- .../org/restlet/ext/slf4j/Slf4jLogger.java | 10 +- .../restlet/ext/spring/SpringComponent.java | 26 ++--- .../org/restlet/ext/spring/SpringHost.java | 4 +- .../restlet/ext/spring/SpringResource.java | 4 +- .../org/restlet/ext/spring/SpringRouter.java | 8 +- .../ext/thymeleaf/ThymeleafConverter.java | 12 +- .../RepresentationResourceLoader.java | 3 +- .../ext/velocity/VelocityConverter.java | 5 +- .../ext/xml/TransformRepresentation.java | 104 +++++++++--------- .../org/restlet/ext/xml/XmlConverter.java | 8 +- .../restlet/ext/xml/XmlRepresentation.java | 4 +- .../java/org/restlet/ext/xml/XmlWriter.java | 10 +- 20 files changed, 120 insertions(+), 139 deletions(-) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java index bb5a41eb17..1a0925a69a 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java @@ -62,7 +62,6 @@ public static String digest(String target, String algorithm) { throw new IllegalArgumentException("Unsupported algorithm."); } - ; /** * Converts a source string to its HMAC/SHA-1 value. diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java index af5f869a65..c0e5bee590 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java @@ -60,9 +60,9 @@ public ContextTemplateLoader(Context context, String baseUri) { * * @param templateSource The template source {@link Representation}. */ - public void closeTemplateSource(Object templateSource) throws IOException { - if (templateSource instanceof Representation) { - ((Representation) templateSource).release(); + public void closeTemplateSource(Object templateSource) { + if (templateSource instanceof Representation representation) { + representation.release(); } } diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java index 610a9dd96a..c5957e5a4b 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java @@ -58,9 +58,9 @@ public T toObject(Representation source, Class target, Resource resource) @Override public Representation toRepresentation(Object source, Variant target, Resource resource) { - if (source instanceof Template) { + if (source instanceof Template template) { return new TemplateRepresentation( - (Template) source, + template, new ResolverHashModel( Resolver.createResolver(resource.getRequest(), resource.getResponse())), target.getMediaType()); diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java index dc71e7229c..7ce1e3f7f3 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java @@ -37,8 +37,8 @@ public TemplateModel get(String key) throws TemplateModelException { Object value = this.resolver.resolve(key); if (value == null) { return null; - } else if (value instanceof TemplateModel) { - return (TemplateModel) value; + } else if (value instanceof TemplateModel templateModel) { + return templateModel; } return new ScalarModel(value); diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java index db97e03e20..ecffae14ce 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java @@ -37,7 +37,7 @@ public class GsonConverter extends ConverterHelper { * @return The unmarshaling {@link GsonRepresentation}. */ protected GsonRepresentation create(Representation source, Class objectClass) { - return new GsonRepresentation(source, objectClass); + return new GsonRepresentation<>(source, objectClass); } /** @@ -48,7 +48,7 @@ protected GsonRepresentation create(Representation source, Class objec * @return The marshaling {@link GsonRepresentation}. */ protected GsonRepresentation create(T source) { - return new GsonRepresentation(source); + return new GsonRepresentation<>(source); } @Override diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java index c5f2eac3a4..72de5cd9ea 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java @@ -68,18 +68,13 @@ public Response getResponse() { * @throws UnsupportedCallbackException */ protected void handle(Callback callback) throws UnsupportedCallbackException { - if (callback instanceof javax.security.auth.callback.NameCallback) { - javax.security.auth.callback.NameCallback nc = - (javax.security.auth.callback.NameCallback) callback; - + if (callback instanceof javax.security.auth.callback.NameCallback nameCallback) { if (getRequest().getChallengeResponse() != null) { - nc.setName(getRequest().getChallengeResponse().getIdentifier()); + nameCallback.setName(getRequest().getChallengeResponse().getIdentifier()); } - } else if (callback instanceof PasswordCallback) { - PasswordCallback pc = (PasswordCallback) callback; - + } else if (callback instanceof PasswordCallback passwordCallback) { if (getRequest().getChallengeResponse() != null) { - pc.setPassword(getRequest().getChallengeResponse().getSecret()); + passwordCallback.setPassword(getRequest().getChallengeResponse().getSecret()); } } else { throw new UnsupportedCallbackException(callback, "Unrecognized Callback"); diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java index 033bef4d8a..c73c22d0f7 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java @@ -77,7 +77,6 @@ public static T doAsPriviledged(ClientInfo clientInfo, PrivilegedAction a public static T doAsPriviledged( ClientInfo clientInfo, PrivilegedAction action, AccessControlContext acc) { Subject subject = JaasUtils.createSubject(clientInfo); - T result = Subject.doAsPrivileged(subject, action, acc); - return result; + return Subject.doAsPrivileged(subject, action, acc); } } diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java index c3784a40e4..533bd44d64 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java @@ -49,11 +49,9 @@ public List> getObjectClasses(Variant source) { public List getVariants(Class source) { List result = null; - if (JSONArray.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_JSON); - } else if (JSONObject.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_JSON); - } else if (JSONTokener.class.isAssignableFrom(source)) { + if (JSONArray.class.isAssignableFrom(source) + || JSONObject.class.isAssignableFrom(source) + || JSONTokener.class.isAssignableFrom(source)) { result = addVariant(result, VARIANT_JSON); } @@ -113,9 +111,9 @@ public float score(Representation source, Class target, Resource resource @Override public T toObject(Representation source, Class target, Resource resource) throws IOException { - JsonRepresentation jsonSource = null; - if (source instanceof JsonRepresentation) { - jsonSource = (JsonRepresentation) source; + final JsonRepresentation jsonSource; + if (source instanceof JsonRepresentation jsonRepresentation) { + jsonSource = jsonRepresentation; } else { jsonSource = new JsonRepresentation(source); } @@ -158,12 +156,12 @@ public T toObject(Representation source, Class target, Resource resource) public Representation toRepresentation(Object source, Variant target, Resource resource) { Representation result = null; - if (source instanceof JSONArray) { - result = new JsonRepresentation((JSONArray) source); - } else if (source instanceof JSONObject) { - result = new JsonRepresentation((JSONObject) source); - } else if (source instanceof JSONTokener) { - result = new JsonRepresentation((JSONTokener) source); + if (source instanceof JSONArray jsonArray) { + result = new JsonRepresentation(jsonArray); + } else if (source instanceof JSONObject jsonObject) { + result = new JsonRepresentation(jsonObject); + } else if (source instanceof JSONTokener jsonTokener) { + result = new JsonRepresentation(jsonTokener); } return result; diff --git a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java index 508bcca95b..81df37f1b1 100644 --- a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java +++ b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java @@ -204,11 +204,11 @@ public void log(Level level, String msg, Throwable thrown) { } @Override - public void log(LogRecord record) { - Level level = record.getLevel(); - String msg = record.getMessage(); - Object[] params = record.getParameters(); - Throwable thrown = record.getThrown(); + public void log(LogRecord logRecord) { + Level level = logRecord.getLevel(); + String msg = logRecord.getMessage(); + Object[] params = logRecord.getParameters(); + Throwable thrown = logRecord.getThrown(); if (thrown != null) { log(level, msg, thrown); diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java index ce3a116d0c..a89552c1b2 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java @@ -66,7 +66,7 @@ public class SpringComponent extends org.restlet.Component { * @param clientInfo The client info. */ public void setClient(Object clientInfo) { - final List clients = new ArrayList(); + final List clients = new ArrayList<>(); clients.add(clientInfo); setClientsList(clients); } @@ -78,12 +78,12 @@ public void setClient(Object clientInfo) { */ public synchronized void setClientsList(List clients) { for (final Object client : clients) { - if (client instanceof String) { - getClients().add(Protocol.valueOf((String) client)); - } else if (client instanceof Protocol) { - getClients().add((Protocol) client); - } else if (client instanceof Client) { - getClients().add((Client) client); + if (client instanceof String string) { + getClients().add(Protocol.valueOf(string)); + } else if (client instanceof Protocol protocol) { + getClients().add(protocol); + } else if (client instanceof Client client1) { + getClients().add(client1); } else { getLogger() .warning( @@ -120,12 +120,12 @@ public void setServer(Object serverInfo) { */ public void setServersList(List serversInfo) { for (final Object serverInfo : serversInfo) { - if (serverInfo instanceof String) { - getServers().add(Protocol.valueOf((String) serverInfo)); - } else if (serverInfo instanceof Protocol) { - getServers().add((Protocol) serverInfo); - } else if (serverInfo instanceof Server) { - getServers().add((Server) serverInfo); + if (serverInfo instanceof String string) { + getServers().add(Protocol.valueOf(string)); + } else if (serverInfo instanceof Protocol protocol) { + getServers().add(protocol); + } else if (serverInfo instanceof Server server) { + getServers().add(server); } else { getLogger() .warning( diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java index 3fbf1e5acf..0ff27d3dfb 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java @@ -68,8 +68,8 @@ public SpringHost(Context context) { * @param route The route object to attach. */ public void setAttachment(String path, Object route) { - if (route instanceof Restlet) { - checkContext((Restlet) route); + if (route instanceof Restlet restlet) { + checkContext(restlet); } SpringRouter.setAttachment(this, path, route); diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java index 3cef0ecdd2..ec85e6e017 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java @@ -58,8 +58,8 @@ public SpringResource(Representation representation, String description) { @Override public boolean equals(Object obj) { return ((obj == this) - || ((obj instanceof SpringResource) - && ((SpringResource) obj).representation.equals(this.representation))); + || ((obj instanceof SpringResource springResource) + && springResource.representation.equals(this.representation))); } /** This implementation always returns true. */ diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java index ce9a84891d..a919923a04 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java @@ -52,13 +52,13 @@ public class SpringRouter extends Router { public static void setAttachment(Router router, String path, Object route) { Class resourceClass; - if (route instanceof Restlet) { - router.attach(path, (Restlet) route); + if (route instanceof Restlet restlet) { + router.attach(path, restlet); } else if (route instanceof Class) { router.attach(path, (Class) route); - } else if (route instanceof String) { + } else if (route instanceof String string) { try { - resourceClass = Engine.loadClass((String) route); + resourceClass = Engine.loadClass(string); if (org.restlet.resource.ServerResource.class.isAssignableFrom(resourceClass)) { router.attach(path, (Class) resourceClass); diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java index 7fd66003c3..b368ca0eeb 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java @@ -30,10 +30,6 @@ public class ThymeleafConverter extends ConverterHelper { private static final VariantInfo VARIANT_ALL = new VariantInfo(MediaType.ALL); - private Locale getLocale(Resource resource) { - return Locale.getDefault(); - } - @Override public List> getObjectClasses(Variant source) { return null; @@ -74,14 +70,12 @@ public T toObject(Representation source, Class target, Resource resource) public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { - if (source instanceof ITemplateResource) { - Locale locale = getLocale(resource); + if (source instanceof ITemplateResource iTemplateResource) { + Locale locale = Locale.getDefault(); TemplateRepresentation tr = new TemplateRepresentation( - ((ITemplateResource) source).getBaseName(), - locale, - target.getMediaType()); + iTemplateResource.getBaseName(), locale, target.getMediaType()); tr.setDataModel(resource.getRequest(), resource.getResponse()); return tr; } diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java index 6ec34f9644..cd7a57f55c 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java @@ -26,8 +26,7 @@ public class RepresentationResourceLoader extends ResourceLoader { /** The cache of template representations. */ - private static final Map store = - new ConcurrentHashMap(); + private static final Map store = new ConcurrentHashMap<>(); /** * Returns the cache of template representations. diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java index 938831a52c..64e29bee1e 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java @@ -69,9 +69,8 @@ public T toObject(Representation source, Class target, Resource resource) public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { - if (source instanceof Template) { - TemplateRepresentation tr = - new TemplateRepresentation((Template) source, target.getMediaType()); + if (source instanceof Template template) { + TemplateRepresentation tr = new TemplateRepresentation(template, target.getMediaType()); tr.setDataModel(resource.getRequest(), resource.getResponse()); return tr; } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java index 0959bfef03..3e6e39ab72 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java @@ -63,38 +63,39 @@ public class TransformRepresentation extends WriterRepresentation { public static SAXSource toSaxSource(Representation representation) throws IOException { SAXSource result = null; - if (representation instanceof XmlRepresentation) { - result = ((XmlRepresentation) representation).getSaxSource(); - } else if (representation instanceof final TransformRepresentation source) { - XMLReader reader = - new AbstractXmlReader() { - - /** - * Parses the input source by sending the result event to the XML reader's - * content handler. - * - * @param input The input source. - */ - public void parse(InputSource input) throws IOException, SAXException { - try { - source.getTransformer() - .transform( - source.getSaxSource(), - new SAXResult(getContentHandler())); - } catch (TransformerException te) { - throw new IOException("Transformer exception. " + te.getMessage()); + switch (representation) { + case XmlRepresentation xmlRepresentation -> result = xmlRepresentation.getSaxSource(); + case TransformRepresentation source -> { + XMLReader reader = + new AbstractXmlReader() { + + /** + * Parses the input source by sending the result event to the XML + * reader's content handler. + * + * @param input The input source. + */ + public void parse(InputSource input) throws IOException, SAXException { + try { + source.getTransformer() + .transform( + source.getSaxSource(), + new SAXResult(getContentHandler())); + } catch (TransformerException te) { + throw new IOException( + "Transformer exception. " + te.getMessage()); + } } - } - public void parse(String systemId) { - throw new IllegalStateException("Not implemented"); - } - }; + public void parse(String systemId) { + throw new IllegalStateException("Not implemented"); + } + }; - result = new SAXSource(reader, new InputSource(representation.getReader())); - } else { + result = new SAXSource(reader, new InputSource(representation.getReader())); + } // Prepare the source and result documents - result = new SAXSource(new InputSource(representation.getReader())); + default -> result = new SAXSource(new InputSource(representation.getReader())); } // Copy the representation's URI as an XML system ID. @@ -258,32 +259,29 @@ public Representation getSourceRepresentation() { * @return The templates to be used and reused. */ public Templates getTemplates() throws IOException { - if (this.templates == null) { - if (getTransformSheet() != null) { - try { - // Prepare the XSLT transformer documents - final StreamSource transformSource = - new StreamSource(getTransformSheet().getStream()); - - if (getTransformSheet().getLocationRef() != null) { - transformSource.setSystemId( - getTransformSheet().getLocationRef().getTargetRef().toString()); - } - - // Create the transformer factory - final TransformerFactory transformerFactory = TransformerFactory.newInstance(); - - // Set the URI resolver - if (getUriResolver() != null) { - transformerFactory.setURIResolver(getUriResolver()); - } - - // Create a new transformer - this.templates = transformerFactory.newTemplates(transformSource); - } catch (TransformerConfigurationException tce) { - throw new IOException( - "Transformer configuration exception. " + tce.getMessage()); + if (this.templates == null && getTransformSheet() != null) { + try { + // Prepare the XSLT transformer documents + final StreamSource transformSource = + new StreamSource(getTransformSheet().getStream()); + + if (getTransformSheet().getLocationRef() != null) { + transformSource.setSystemId( + getTransformSheet().getLocationRef().getTargetRef().toString()); + } + + // Create the transformer factory + final TransformerFactory transformerFactory = TransformerFactory.newInstance(); + + // Set the URI resolver + if (getUriResolver() != null) { + transformerFactory.setURIResolver(getUriResolver()); } + + // Create a new transformer + this.templates = transformerFactory.newTemplates(transformSource); + } catch (TransformerConfigurationException tce) { + throw new IOException("Transformer configuration exception. " + tce.getMessage()); } } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java index 98b39c6149..5994a36d06 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java @@ -143,10 +143,10 @@ public Representation toRepresentation(Object source, Variant target, Resource r throws IOException { Representation result = null; - if (source instanceof Document) { - result = new DomRepresentation(target.getMediaType(), (Document) source); - } else if (source instanceof Representation) { - result = (Representation) source; + if (source instanceof Document document) { + result = new DomRepresentation(target.getMediaType(), document); + } else if (source instanceof Representation representation) { + result = representation; } return result; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java index d040f42b0f..8ac8e0fdef 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java @@ -398,7 +398,7 @@ public ErrorHandler getErrorHandler() { */ public Map getNamespaces() { if (this.namespaces == null) { - this.namespaces = new HashMap(); + this.namespaces = new HashMap<>(); } return this.namespaces; @@ -459,7 +459,7 @@ public String getPrefix(String namespaceURI) { /** {@inheritDoc javax.xml.namespace.NamespaceContext#getPrefixes(java.lang.String} */ public Iterator getPrefixes(String namespaceURI) { - final List result = new ArrayList(); + final List result = new ArrayList<>(); for (Iterator iterator = getNamespaces().keySet().iterator(); iterator.hasNext(); ) { diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java index b83d888802..1eefa6e1a2 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java @@ -308,7 +308,7 @@ public final class XmlWriter extends XMLFilterImpl { private volatile Object state = SEEN_NOTHING; - private volatile Stack stateStack = new Stack(); + private volatile Stack stateStack = new Stack<>(); /** * Create a new XML writer. @@ -401,7 +401,7 @@ public XmlWriter(XMLReader xmlreader, Writer writer) { * restlet further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#characters */ - private void characters(boolean dataFormat, char ch[], int start, int len) throws SAXException { + private void characters(boolean dataFormat, char[] ch, int start, int len) throws SAXException { if (dataFormat) { this.state = SEEN_DATA; } @@ -441,7 +441,7 @@ private void characters(boolean dataFormat, String data) throws SAXException { * @see org.xml.sax.ContentHandler#characters */ @Override - public void characters(char ch[], int start, int len) throws SAXException { + public void characters(char[] ch, int start, int len) throws SAXException { characters(isDataFormat(), ch, start, len); } @@ -884,7 +884,7 @@ public Writer getWriter() { * @see org.xml.sax.ContentHandler#ignorableWhitespace */ @Override - public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { writeEsc(ch, start, length, false); super.ignorableWhitespace(ch, start, length); } @@ -1233,7 +1233,7 @@ private void writeNSDecls() throws SAXException { if (uri == null) { uri = ""; } - final char ch[] = uri.toCharArray(); + final char[] ch = uri.toCharArray(); write(' '); if ("".equals(prefix)) { write("xmlns=\""); From 5418c682303914b8646fda9cd542c55ce56b6989 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 10:55:31 +0200 Subject: [PATCH 08/30] Take into account Sonar comments --- .../restlet/ext/spring/SpringResource.java | 13 +++- .../src/main/java/org/restlet/Request.java | 6 +- .../java/org/restlet/data/CacheDirective.java | 5 +- .../org/restlet/data/ChallengeMessage.java | 17 ++-- .../org/restlet/data/ChallengeRequest.java | 2 +- .../org/restlet/data/ChallengeResponse.java | 15 +--- .../org/restlet/data/ChallengeScheme.java | 53 +++++++------ .../java/org/restlet/data/CharacterSet.java | 11 ++- .../java/org/restlet/data/ClientInfo.java | 29 +++---- .../main/java/org/restlet/data/Cookie.java | 2 - .../java/org/restlet/data/CookieSetting.java | 5 +- .../main/java/org/restlet/data/Digest.java | 13 +++- .../main/java/org/restlet/data/Encoding.java | 11 ++- .../java/org/restlet/data/Expectation.java | 1 - .../main/java/org/restlet/data/Language.java | 11 ++- .../main/java/org/restlet/data/MediaType.java | 77 ++++++------------- .../main/java/org/restlet/data/Metadata.java | 14 +++- .../main/java/org/restlet/data/Method.java | 11 ++- .../main/java/org/restlet/data/Parameter.java | 8 +- .../main/java/org/restlet/data/Protocol.java | 13 +++- .../src/main/java/org/restlet/data/Range.java | 13 +++- .../main/java/org/restlet/data/Reference.java | 23 +++--- .../java/org/restlet/data/ReferenceList.java | 14 ++++ .../main/java/org/restlet/data/Status.java | 17 ++-- .../src/main/java/org/restlet/data/Tag.java | 32 ++++---- .../java/org/restlet/engine/adapter/Call.java | 2 +- .../restlet/engine/adapter/ClientAdapter.java | 12 +-- .../restlet/engine/adapter/ClientCall.java | 5 +- .../engine/adapter/HttpClientHelper.java | 2 +- .../restlet/engine/adapter/HttpRequest.java | 7 +- .../engine/adapter/JettyClientCall.java | 3 +- .../restlet/engine/adapter/ServerAdapter.java | 13 ++-- .../restlet/engine/adapter/ServerCall.java | 4 +- .../restlet/engine/application/Conneg.java | 2 +- .../engine/application/CorsFilter.java | 2 +- .../application/CorsResponseHelper.java | 12 +-- .../application/RangeRepresentation.java | 12 +++ .../engine/application/StatusFilter.java | 6 +- .../engine/resource/AnnotationInfo.java | 13 +--- .../engine/resource/MethodAnnotationInfo.java | 13 +--- .../resource/ThrowableAnnotationInfo.java | 13 +--- .../restlet/engine/resource/VariantInfo.java | 13 +--- .../org/restlet/representation/Variant.java | 13 +--- .../main/java/org/restlet/security/Role.java | 16 ++-- .../java/org/restlet/util/WrapperList.java | 20 ++--- .../java/org/restlet/util/WrapperMap.java | 7 +- .../java/org/restlet/resource/MyBean.java | 20 ++--- 47 files changed, 305 insertions(+), 321 deletions(-) diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java index ec85e6e017..08ae2315bc 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Objects; import org.restlet.engine.util.SystemUtils; import org.restlet.representation.Representation; import org.springframework.core.io.AbstractResource; @@ -54,12 +55,16 @@ public SpringResource(Representation representation, String description) { this.description = (description != null) ? description : ""; } - /** This implementation compares the underlying InputStream. */ + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { - return ((obj == this) - || ((obj instanceof SpringResource springResource) - && springResource.representation.equals(this.representation))); + if (obj == this) { + return true; + } + if (!(obj instanceof SpringResource that)) { + return false; + } + return Objects.equals(representation, that.representation); } /** This implementation always returns true. */ diff --git a/org.restlet/src/main/java/org/restlet/Request.java b/org.restlet/src/main/java/org/restlet/Request.java index 3ef45c3d71..e0f558276b 100644 --- a/org.restlet/src/main/java/org/restlet/Request.java +++ b/org.restlet/src/main/java/org/restlet/Request.java @@ -35,7 +35,7 @@ /** * Generic request sent by client connectors. It is then received by server connectors and processed * by {@link Restlet}s. This request can also be processed by a chain of Restlets, on both client - * and server sides. Requests are uniform across all types of connectors, protocols and components. + * and server sides. Requests are uniform across all types of connectors, protocols, and components. * * @see org.restlet.Response * @see org.restlet.Uniform @@ -330,7 +330,7 @@ public Set getAccessControlRequestHeaders() { synchronized (this) { a = this.accessControlRequestHeaders; if (a == null) { - this.accessControlRequestHeaders = a = new CopyOnWriteArraySet(); + this.accessControlRequestHeaders = a = new CopyOnWriteArraySet<>(); } } } @@ -520,7 +520,7 @@ public List getRanges() { synchronized (this) { r = this.ranges; if (r == null) { - this.ranges = r = new CopyOnWriteArrayList(); + this.ranges = r = new CopyOnWriteArrayList<>(); } } } diff --git a/org.restlet/src/main/java/org/restlet/data/CacheDirective.java b/org.restlet/src/main/java/org/restlet/data/CacheDirective.java index b1e27ca1e0..c6a932c167 100644 --- a/org.restlet/src/main/java/org/restlet/data/CacheDirective.java +++ b/org.restlet/src/main/java/org/restlet/data/CacheDirective.java @@ -353,13 +353,10 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - - if (!(obj instanceof CacheDirective)) { + if (!(obj instanceof CacheDirective that)) { return false; } - CacheDirective that = (CacheDirective) obj; - return Objects.equals(getName(), that.getName()) && Objects.equals(getValue(), that.getValue()) && (this.digit == that.digit); diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java index 1791b0a2b8..2c29ef3713 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java @@ -51,7 +51,7 @@ public abstract class ChallengeMessage { * * @param scheme The challenge scheme. */ - public ChallengeMessage(ChallengeScheme scheme) { + protected ChallengeMessage(ChallengeScheme scheme) { this(scheme, null, null); } @@ -61,7 +61,7 @@ public ChallengeMessage(ChallengeScheme scheme) { * @param scheme The challenge scheme. * @param parameters The additional scheme parameters. */ - public ChallengeMessage(ChallengeScheme scheme, Series parameters) { + protected ChallengeMessage(ChallengeScheme scheme, Series parameters) { this(scheme, null, null); } @@ -71,7 +71,7 @@ public ChallengeMessage(ChallengeScheme scheme, Series parameters) { * @param scheme The challenge scheme. * @param realm The authentication realm. */ - public ChallengeMessage(ChallengeScheme scheme, String realm) { + protected ChallengeMessage(ChallengeScheme scheme, String realm) { this(scheme, realm, null); } @@ -82,7 +82,7 @@ public ChallengeMessage(ChallengeScheme scheme, String realm) { * @param realm The authentication realm. * @param parameters The additional scheme parameters. */ - public ChallengeMessage(ChallengeScheme scheme, String realm, Series parameters) { + protected ChallengeMessage(ChallengeScheme scheme, String realm, Series parameters) { this(scheme, realm, parameters, Digest.ALGORITHM_MD5, null, null); } @@ -96,7 +96,7 @@ public ChallengeMessage(ChallengeScheme scheme, String realm, Series * @param opaque An opaque string of data which should be returned by the client unchanged. * @param serverNonce The server nonce. */ - public ChallengeMessage( + protected ChallengeMessage( ChallengeScheme scheme, String realm, Series parameters, @@ -112,18 +112,17 @@ public ChallengeMessage( this.digestAlgorithm = digestAlgorithm; } + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } - if (!(obj instanceof ChallengeMessage)) { + if (!(obj instanceof final ChallengeMessage that)) { return false; } - final ChallengeMessage that = (ChallengeMessage) obj; - - return getParameters().equals(that.getParameters()) + return Objects.equals(getParameters(), that.getParameters()) && Objects.equals(getRealm(), that.getRealm()) && Objects.equals(getScheme(), that.getScheme()) && Objects.equals(getServerNonce(), that.getServerNonce()) diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java index 461c2029ce..5a3a30b400 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java @@ -65,7 +65,7 @@ public boolean equals(final Object obj) { return false; } - return getParameters().equals(that.getParameters()) + return Objects.equals(getParameters(), that.getParameters()) && Objects.equals(getRealm(), that.getRealm()) && Objects.equals(getScheme(), that.getScheme()); } diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java index 363485e2e8..8b05ae6ee4 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java @@ -242,23 +242,16 @@ public ChallengeResponse(ChallengeScheme scheme, String identifier, String secre /** {@inheritDoc} */ @Override public boolean equals(Object obj) { - // if obj == this no need to go further if (obj == this) { return true; } - - // if obj isn't a challenge request or is null don't evaluate further if (!(obj instanceof ChallengeResponse that)) { return false; } - - if (!Objects.equals(getRawValue(), that.getRawValue()) - || !Objects.equals(getIdentifier(), that.getIdentifier()) - || !Objects.equals(getScheme(), that.getScheme())) { - return false; - } - - return Arrays.equals(getSecret(), that.getSecret()); + return Objects.equals(getRawValue(), that.getRawValue()) + && Objects.equals(getIdentifier(), that.getIdentifier()) + && Objects.equals(getScheme(), that.getScheme()) + && Arrays.equals(getSecret(), that.getSecret()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java b/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java index a3acd32dde..323705eeef 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java @@ -8,8 +8,6 @@ */ package org.restlet.data; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; /** @@ -94,28 +92,24 @@ public final class ChallengeScheme { "HTTP_MAC", "Mac", "OAuth 2.0 message authentication code authentication"); /** Private list of schemes for optimization purpose. */ - private static Map SCHEMES; - - static { - Map schemes = new HashMap(); - - schemes.put(CUSTOM.getName().toLowerCase(), CUSTOM); - schemes.put(FTP_PLAIN.getName().toLowerCase(), FTP_PLAIN); - schemes.put(HTTP_AWS_IAM.getName().toLowerCase(), HTTP_AWS_S3); - schemes.put(HTTP_AWS_QUERY.getName().toLowerCase(), HTTP_AWS_S3); - schemes.put(HTTP_AWS_S3.getName().toLowerCase(), HTTP_AWS_S3); - schemes.put(HTTP_AZURE_SHAREDKEY.getName().toLowerCase(), HTTP_AZURE_SHAREDKEY); - schemes.put(HTTP_AZURE_SHAREDKEY_LITE.getName().toLowerCase(), HTTP_AZURE_SHAREDKEY_LITE); - schemes.put(HTTP_BASIC.getName().toLowerCase(), HTTP_BASIC); - schemes.put(HTTP_COOKIE.getName().toLowerCase(), HTTP_COOKIE); - schemes.put(HTTP_DIGEST.getName().toLowerCase(), HTTP_DIGEST); - schemes.put(HTTP_NTLM.getName().toLowerCase(), HTTP_NTLM); - schemes.put(HTTP_OAUTH.getName().toLowerCase(), HTTP_OAUTH); - schemes.put(HTTP_OAUTH_BEARER.getName().toLowerCase(), HTTP_OAUTH); - schemes.put(HTTP_OAUTH_MAC.getName().toLowerCase(), HTTP_OAUTH); - - ChallengeScheme.SCHEMES = Collections.unmodifiableMap(schemes); - } + private static final Map SCHEMES = + Map.ofEntries( + Map.entry(CUSTOM.getName().toLowerCase(), CUSTOM), + Map.entry(FTP_PLAIN.getName().toLowerCase(), FTP_PLAIN), + Map.entry(HTTP_AWS_IAM.getName().toLowerCase(), HTTP_AWS_S3), + Map.entry(HTTP_AWS_QUERY.getName().toLowerCase(), HTTP_AWS_S3), + Map.entry(HTTP_AWS_S3.getName().toLowerCase(), HTTP_AWS_S3), + Map.entry(HTTP_AZURE_SHAREDKEY.getName().toLowerCase(), HTTP_AZURE_SHAREDKEY), + Map.entry( + HTTP_AZURE_SHAREDKEY_LITE.getName().toLowerCase(), + HTTP_AZURE_SHAREDKEY_LITE), + Map.entry(HTTP_BASIC.getName().toLowerCase(), HTTP_BASIC), + Map.entry(HTTP_COOKIE.getName().toLowerCase(), HTTP_COOKIE), + Map.entry(HTTP_DIGEST.getName().toLowerCase(), HTTP_DIGEST), + Map.entry(HTTP_NTLM.getName().toLowerCase(), HTTP_NTLM), + Map.entry(HTTP_OAUTH.getName().toLowerCase(), HTTP_OAUTH), + Map.entry(HTTP_OAUTH_BEARER.getName().toLowerCase(), HTTP_OAUTH), + Map.entry(HTTP_OAUTH_MAC.getName().toLowerCase(), HTTP_OAUTH)); /** * Returns the challenge scheme associated with a scheme name. If an existing constant exists, @@ -174,9 +168,14 @@ public ChallengeScheme( /** {@inheritDoc} */ @Override - public boolean equals(final Object object) { - return (object instanceof ChallengeScheme) - && ((ChallengeScheme) object).getName().equalsIgnoreCase(getName()); + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ChallengeScheme that)) { + return false; + } + return getName().equalsIgnoreCase(that.getName()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java index f9377b1b44..5c9a042f1b 100644 --- a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java +++ b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java @@ -248,11 +248,14 @@ public CharacterSet(String name, String description) { /** {@inheritDoc} */ @Override - public boolean equals(Object object) { - if (object instanceof CharacterSet characterSet) { - return getName().equalsIgnoreCase(characterSet.getName()); + public boolean equals(Object obj) { + if (obj == this) { + return true; } - return false; + if (!(obj instanceof CharacterSet that)) { + return false; + } + return getName().equalsIgnoreCase(that.getName()); } @Override diff --git a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java index f3b30c1026..5455f574e4 100644 --- a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java @@ -19,6 +19,8 @@ import org.restlet.Context; import org.restlet.engine.Engine; import org.restlet.engine.io.IoUtils; +import org.restlet.routing.Template; +import org.restlet.routing.Variable; /** * Client-specific data related to a call. When extracted from a request, most of these data are @@ -493,26 +495,17 @@ public Map getAgentAttributes() { // version and optional comment. Respectively, these // variables are called "agentName", "agentVersion" and // "agentComment". - org.restlet.routing.Template template = null; + Template template; // Predefined variables. - org.restlet.routing.Variable agentName = - new org.restlet.routing.Variable(org.restlet.routing.Variable.TYPE_TOKEN); - org.restlet.routing.Variable agentVersion = - new org.restlet.routing.Variable(org.restlet.routing.Variable.TYPE_TOKEN); - org.restlet.routing.Variable agentComment = - new org.restlet.routing.Variable(org.restlet.routing.Variable.TYPE_COMMENT); - org.restlet.routing.Variable agentCommentAttribute = - new org.restlet.routing.Variable( - org.restlet.routing.Variable.TYPE_COMMENT_ATTRIBUTE); - org.restlet.routing.Variable facultativeData = - new org.restlet.routing.Variable( - org.restlet.routing.Variable.TYPE_ALL, null, false, false); + Variable agentName = new Variable(Variable.TYPE_TOKEN); + Variable agentVersion = new Variable(Variable.TYPE_TOKEN); + Variable agentComment = new Variable(Variable.TYPE_COMMENT); + Variable agentCommentAttribute = new Variable(Variable.TYPE_COMMENT_ATTRIBUTE); + Variable facultativeData = new Variable(Variable.TYPE_ALL, null, false, false); if (ClientInfo.getUserAgentTemplates() != null) { for (String string : ClientInfo.getUserAgentTemplates()) { - template = - new org.restlet.routing.Template( - string, org.restlet.routing.Template.MODE_EQUALS); + template = new Template(string, Template.MODE_EQUALS); // Update the predefined variables. template.getVariables().put("agentName", agentName); @@ -524,8 +517,8 @@ public Map getAgentAttributes() { // Parse the template if (template.parse(getAgent(), map) > -1) { - for (String key : map.keySet()) { - this.agentAttributes.put(key, (String) map.get(key)); + for (Map.Entry entry : map.entrySet()) { + this.agentAttributes.put(entry.getKey(), (String) entry.getValue()); } break; } diff --git a/org.restlet/src/main/java/org/restlet/data/Cookie.java b/org.restlet/src/main/java/org/restlet/data/Cookie.java index 4037ddd8c0..b42e290e76 100644 --- a/org.restlet/src/main/java/org/restlet/data/Cookie.java +++ b/org.restlet/src/main/java/org/restlet/data/Cookie.java @@ -91,9 +91,7 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - if (!(obj instanceof Cookie that)) { - // if obj isn't a cookie or is null don't evaluate further return false; } diff --git a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java index 005c751604..622aad2219 100644 --- a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java +++ b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java @@ -23,6 +23,9 @@ * @author Jerome Louvel */ public final class CookieSetting extends Cookie { + + private static final String DESCRIPTION = "Cookie setting"; + /** * Indicates whether to restrict cookie access to untrusted parties. Currently, this toggles the * non-standard but widely supported HttpOnly cookie parameter. @@ -167,7 +170,7 @@ public String getComment() { * @return The description of this REST element. */ public String getDescription() { - return "Cookie setting"; + return DESCRIPTION; } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Digest.java b/org.restlet/src/main/java/org/restlet/data/Digest.java index 70ee94ac84..2b611aef08 100644 --- a/org.restlet/src/main/java/org/restlet/data/Digest.java +++ b/org.restlet/src/main/java/org/restlet/data/Digest.java @@ -9,6 +9,7 @@ package org.restlet.data; import java.util.Arrays; +import java.util.Objects; import org.restlet.engine.util.SystemUtils; import org.restlet.representation.Representation; @@ -79,13 +80,17 @@ public Digest(String algorithm, byte[] value) { System.arraycopy(value, 0, this.value, 0, value.length); } + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { - if (obj instanceof Digest that && getAlgorithm().equals(that.getAlgorithm())) { - return Arrays.equals(getValue(), that.getValue()); + if (obj == this) { + return true; } - - return false; + if (!(obj instanceof Digest that)) { + return false; + } + return Objects.equals(getAlgorithm(), that.getAlgorithm()) + && Arrays.equals(getValue(), that.getValue()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Encoding.java b/org.restlet/src/main/java/org/restlet/data/Encoding.java index 934cae3fd5..336e9a7946 100644 --- a/org.restlet/src/main/java/org/restlet/data/Encoding.java +++ b/org.restlet/src/main/java/org/restlet/data/Encoding.java @@ -107,9 +107,14 @@ public Encoding(final String name, final String description) { /** {@inheritDoc} */ @Override - public boolean equals(final Object object) { - return object instanceof Encoding encoding - && getName().equalsIgnoreCase(encoding.getName()); + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Encoding that)) { + return false; + } + return getName().equalsIgnoreCase(that.getName()); } @Override diff --git a/org.restlet/src/main/java/org/restlet/data/Expectation.java b/org.restlet/src/main/java/org/restlet/data/Expectation.java index 783f32ed98..b7b8c1c94b 100644 --- a/org.restlet/src/main/java/org/restlet/data/Expectation.java +++ b/org.restlet/src/main/java/org/restlet/data/Expectation.java @@ -71,7 +71,6 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - if (!(obj instanceof Expectation that)) { return false; } diff --git a/org.restlet/src/main/java/org/restlet/data/Language.java b/org.restlet/src/main/java/org/restlet/data/Language.java index d581a87dd3..b96d6bb6ec 100644 --- a/org.restlet/src/main/java/org/restlet/data/Language.java +++ b/org.restlet/src/main/java/org/restlet/data/Language.java @@ -103,9 +103,14 @@ public Language(final String name, final String description) { /** {@inheritDoc} */ @Override - public boolean equals(final Object object) { - return (object instanceof Language language) - && getName().equalsIgnoreCase(language.getName()); + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Language that)) { + return false; + } + return getName().equalsIgnoreCase(that.getName()); } @Override diff --git a/org.restlet/src/main/java/org/restlet/data/MediaType.java b/org.restlet/src/main/java/org/restlet/data/MediaType.java index b527a5e04f..efe7bd7e82 100644 --- a/org.restlet/src/main/java/org/restlet/data/MediaType.java +++ b/org.restlet/src/main/java/org/restlet/data/MediaType.java @@ -9,8 +9,9 @@ package org.restlet.data; import java.io.IOException; -import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import org.restlet.Context; import org.restlet.engine.header.HeaderWriter; @@ -22,8 +23,8 @@ * Metadata used to specify the format of representations. The {@link #getName()} method returns a * full String representation of the media type including the parameters. * - * @see MIME types on Wikipedia * @author Jerome Louvel + * @see MIME types on Wikipedia */ public final class MediaType extends Metadata { @@ -33,14 +34,13 @@ public final class MediaType extends Metadata { * * @see RFC 1521 */ - private static final String _TSPECIALS = "()<>@,;:/[]?=\\\""; + private static final String TSPECIALS = "()<>@,;:/[]?=\\\""; /** * The known media types registered with {@link #register(String, String)}, retrievable using - * {@link #valueOf(String)}.
- * Keep the underscore for the ordering. + * {@link #valueOf(String)}. */ - private static volatile Map _types = null; + private static final Map TYPES = new ConcurrentHashMap<>(); public static final MediaType ALL = register("*/*", "All media"); @@ -530,38 +530,21 @@ public static MediaType getMostSpecific(MediaType... mediaTypes) for (int i = 1; i < mediaTypes.length; i++) { MediaType mediaType = mediaTypes[i]; - if (mediaType != null) { - if ("*".equals(mediaType.getMainType())) { - continue; - } - - if ("*".equals(mostSpecific.getMainType())) { - mostSpecific = mediaType; - continue; - } + if (mediaType == null || "*".equals(mediaType.getMainType())) { + continue; + } - if (mostSpecific.getSubType().contains("*")) { - mostSpecific = mediaType; - continue; - } + if ("*".equals(mostSpecific.getMainType())) { + mostSpecific = mediaType; + } else if (mostSpecific.getSubType() != null + && mostSpecific.getSubType().contains("*")) { + mostSpecific = mediaType; } } return mostSpecific; } - /** - * Returns the known media types map. - * - * @return the known media types map. - */ - private static Map getTypes() { - if (_types == null) { - _types = new HashMap<>(); - } - return _types; - } - /** * Normalizes the specified token. * @@ -581,7 +564,7 @@ private static String normalizeToken(String token) { length = token.length(); for (int i = 0; i < length; i++) { c = token.charAt(i); - if (c <= 32 || c >= 127 || _TSPECIALS.indexOf(c) != -1) + if (c <= 32 || c >= 127 || TSPECIALS.indexOf(c) != -1) throw new IllegalArgumentException("Illegal token: " + token); } @@ -662,13 +645,7 @@ public HeaderWriter append(Parameter value) { * @return The registered media type */ public static synchronized MediaType register(String name, String description) { - - if (!getTypes().containsKey(name)) { - final MediaType type = new MediaType(name, description); - getTypes().put(name, type); - } - - return getTypes().get(name); + return TYPES.computeIfAbsent(name, n -> new MediaType(n, description)); } /** @@ -682,7 +659,7 @@ public static MediaType valueOf(String name) { MediaType result = null; if (!StringUtils.isNullOrEmpty(name)) { - result = getTypes().get(name); + result = TYPES.get(name); if (result == null) { result = new MediaType(name); } @@ -788,20 +765,16 @@ public boolean equals(Object obj) { * @return True if both media types are equal. */ public boolean equals(Object obj, boolean ignoreParameters) { - boolean result = (obj == this); - - // if obj == this no need to go further - if (!result) { - // if obj isn't a media type or is null, don't evaluate further - if (obj instanceof final MediaType that) { - if (getMainType().equals(that.getMainType()) - && getSubType().equals(that.getSubType())) { - result = ignoreParameters || getParameters().equals(that.getParameters()); - } - } + if (obj == this) { + return true; + } + if (!(obj instanceof MediaType that)) { + return false; } - return result; + return Objects.equals(getMainType(), that.getMainType()) + && Objects.equals(getSubType(), that.getSubType()) + && (ignoreParameters || getParameters().equals(that.getParameters())); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Metadata.java b/org.restlet/src/main/java/org/restlet/data/Metadata.java index 69c6add38a..ffa4a2d9db 100644 --- a/org.restlet/src/main/java/org/restlet/data/Metadata.java +++ b/org.restlet/src/main/java/org/restlet/data/Metadata.java @@ -8,6 +8,8 @@ */ package org.restlet.data; +import java.util.Objects; + /** * Representations metadata for content negotiation. "Metadata is in the form of name-value pairs, * where the name corresponds to a standard that defines the value's structure and semantics. @@ -32,7 +34,7 @@ public abstract class Metadata { * * @param name The unique name. */ - public Metadata(String name) { + protected Metadata(String name) { this(name, null); } @@ -42,7 +44,7 @@ public Metadata(String name) { * @param name The unique name. * @param description The description. */ - public Metadata(String name, String description) { + protected Metadata(String name, String description) { this.name = name; this.description = description; } @@ -50,7 +52,13 @@ public Metadata(String name, String description) { /** {@inheritDoc} */ @Override public boolean equals(Object object) { - return (object instanceof Metadata metadata) && metadata.getName().equals(getName()); + if (object == this) { + return true; + } + if (!(object instanceof Metadata that)) { + return false; + } + return Objects.equals(getName(), that.getName()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Method.java b/org.restlet/src/main/java/org/restlet/data/Method.java index d39d33e789..097066956e 100644 --- a/org.restlet/src/main/java/org/restlet/data/Method.java +++ b/org.restlet/src/main/java/org/restlet/data/Method.java @@ -11,6 +11,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import org.restlet.engine.Engine; @@ -312,8 +313,14 @@ public int compareTo(Method o) { /** {@inheritDoc} */ @Override - public boolean equals(final Object object) { - return (object instanceof Method method) && method.getName().equals(getName()); + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Method that)) { + return false; + } + return Objects.equals(getName(), that.getName()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Parameter.java b/org.restlet/src/main/java/org/restlet/data/Parameter.java index ad6bae9654..eb9ac9d84d 100644 --- a/org.restlet/src/main/java/org/restlet/data/Parameter.java +++ b/org.restlet/src/main/java/org/restlet/data/Parameter.java @@ -117,11 +117,11 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - if (obj instanceof Parameter that) { - return Objects.equals(getName(), that.getName()) - && Objects.equals(getValue(), that.getValue()); + if (!(obj instanceof Parameter that)) { + return false; } - return false; + return Objects.equals(getName(), that.getName()) + && Objects.equals(getValue(), that.getValue()); } /* diff --git a/org.restlet/src/main/java/org/restlet/data/Protocol.java b/org.restlet/src/main/java/org/restlet/data/Protocol.java index b99361dece..b9ee9627ba 100644 --- a/org.restlet/src/main/java/org/restlet/data/Protocol.java +++ b/org.restlet/src/main/java/org/restlet/data/Protocol.java @@ -154,7 +154,7 @@ public static Protocol valueOf(String name) { public static Protocol valueOf(String name, String version) { Protocol result = valueOf(name); - if (!version.equals(result.getVersion())) { + if (result != null && !version.equals(result.getVersion())) { result = new Protocol( result.getSchemeName(), @@ -297,9 +297,14 @@ public Protocol( /** {@inheritDoc} */ @Override - public boolean equals(final Object object) { - return (object instanceof Protocol protocol) - && getName().equalsIgnoreCase(protocol.getName()); + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Protocol that)) { + return false; + } + return getName().equalsIgnoreCase(that.getName()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Range.java b/org.restlet/src/main/java/org/restlet/data/Range.java index d873b464c8..70160b5446 100644 --- a/org.restlet/src/main/java/org/restlet/data/Range.java +++ b/org.restlet/src/main/java/org/restlet/data/Range.java @@ -109,11 +109,16 @@ public Range(long index, long size, long instanceSize, String unitName) { this.unitName = unitName; } + /** {@inheritDoc} */ @Override - public boolean equals(Object object) { - return (object instanceof Range range) - && range.getIndex() == getIndex() - && range.getSize() == getSize(); + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Range that)) { + return false; + } + return getIndex() == that.getIndex() && getSize() == that.getSize(); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Reference.java b/org.restlet/src/main/java/org/restlet/data/Reference.java index c5a16d9dd9..bd32a08d59 100644 --- a/org.restlet/src/main/java/org/restlet/data/Reference.java +++ b/org.restlet/src/main/java/org/restlet/data/Reference.java @@ -11,6 +11,7 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.logging.Level; import org.restlet.Context; @@ -727,22 +728,16 @@ private String encodeInvalidCharacters(String uriRef) throws IllegalArgumentExce return result; } - /** - * Indicates whether some other object is "equal to" this one. - * - * @param object The object to compare to. - * @return True if this object is the same as the obj argument. - */ + /** {@inheritDoc} */ @Override - public boolean equals(Object object) { - if (object instanceof final Reference ref) { - if (this.internalRef == null) { - return ref.internalRef == null; - } - return this.internalRef.equals(ref.internalRef); + public boolean equals(Object obj) { + if (obj == this) { + return true; } - - return false; + if (!(obj instanceof Reference that)) { + return false; + } + return Objects.equals(this.internalRef, that.internalRef); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java index 40f892198b..53d5e0bda1 100644 --- a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java +++ b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import org.restlet.engine.io.IoUtils; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; @@ -86,6 +87,13 @@ public boolean add(String uri) { return add(new Reference(uri)); } + /** {@inheritDoc} */ + @Override + public boolean equals(final Object obj) { + return super.equals(obj) + && Objects.equals(getIdentifier(), ((ReferenceList) obj).getIdentifier()); + } + /** * Returns the list identifier. * @@ -147,6 +155,12 @@ public Representation getWebRepresentation() { return new StringRepresentation(sb.toString(), MediaType.TEXT_HTML); } + /** {@inheritDoc} */ + @Override + public int hashCode() { + return super.hashCode(); + } + /** * Sets the list reference. * diff --git a/org.restlet/src/main/java/org/restlet/data/Status.java b/org.restlet/src/main/java/org/restlet/data/Status.java index 169084ed8b..3e43337320 100644 --- a/org.restlet/src/main/java/org/restlet/data/Status.java +++ b/org.restlet/src/main/java/org/restlet/data/Status.java @@ -812,15 +812,16 @@ public Status(Status status, Throwable throwable, String reasonPhrase, String de status.getUri()); } - /** - * Indicates if the status is equal to a given one. - * - * @param object The object to compare to. - * @return True if the status is equal to a given one. - */ + /** {@inheritDoc} */ @Override - public boolean equals(final Object object) { - return (object instanceof Status status) && (this.code == status.getCode()); + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Status that)) { + return false; + } + return this.code == that.getCode(); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Tag.java b/org.restlet/src/main/java/org/restlet/data/Tag.java index 4e98bef0bd..daa216d59f 100644 --- a/org.restlet/src/main/java/org/restlet/data/Tag.java +++ b/org.restlet/src/main/java/org/restlet/data/Tag.java @@ -57,14 +57,14 @@ public static Tag parse(String httpTag) { result = new Tag("*", weak); } else { Context.getCurrentLogger() - .log(Level.WARNING, "Invalid tag format detected: " + httpTagCopy); + .log(Level.WARNING, "Invalid tag format detected: {0}", httpTagCopy); } return result; } /** The name. */ - private volatile String name; + private final String name; /** The tag weakness. */ private final boolean weak; @@ -97,34 +97,28 @@ public Tag(final String opaqueTag, boolean weak) { this.weak = weak; } - /** - * Indicates if both tags are equal. - * - * @param object The object to compare to. - * @return True if both tags are equal. - */ + /** {@inheritDoc} */ @Override - public boolean equals(final Object object) { - return equals(object, true); + public boolean equals(final Object obj) { + return equals(obj, true); } /** * Indicates if both tags are equal. * - * @param object The object to compare to. + * @param obj The object to compare to. * @param checkWeakness The equality test takes care or not of the weakness. * @return True if both tags are equal. */ - public boolean equals(final Object object, boolean checkWeakness) { - if (object instanceof Tag that) { - if (checkWeakness && that.isWeak() != isWeak()) { - return false; - } - - return Objects.equals(getName(), that.getName()); - } else { + public boolean equals(final Object obj, boolean checkWeakness) { + if (obj == this) { + return true; + } + if (!(obj instanceof Tag that)) { return false; } + return Objects.equals(getName(), that.getName()) + && (checkWeakness || isWeak() == that.isWeak()); } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java index ad5c4fe5ca..fd3e09f029 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java @@ -111,7 +111,7 @@ public static boolean isBroken(Throwable exception) { private volatile String version; /** Constructor. */ - public Call() { + protected Call() { this.hostDomain = null; this.hostPort = -1; this.clientAddress = null; diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java index 641ad4ae83..844294ee71 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java @@ -8,7 +8,6 @@ */ package org.restlet.engine.adapter; -import java.io.IOException; import java.util.logging.Level; import org.restlet.Context; import org.restlet.Request; @@ -71,12 +70,12 @@ public void commit(final ClientCall httpCall, Request request, Response response if (userCallback != null) { userCallback.handle(request1, response1); } - } catch (Throwable t) { + } catch (Exception exception) { getLogger() .log( Level.WARNING, "Unexpected error or exception inside the user call back", - t); + exception); } }); } else { @@ -142,7 +141,6 @@ public ClientCall toSpecific(HttpClientHelper client, Request request) { * @param response The response to update. * @param status The response status to apply. * @param httpCall The source HTTP client call. - * @throws IOException */ public void updateResponse(Response response, Status status, ClientCall httpCall) { // Send the request to the client @@ -166,14 +164,12 @@ public void updateResponse(Response response, Status status, ClientCall httpCall response.getEntity().release(); } else if (response.getStatus().equals(Status.SUCCESS_NO_CONTENT)) { response.getEntity().release(); - } else if (response.getStatus().equals(Status.SUCCESS_RESET_CONTENT)) { + } else if (response.getStatus().equals(Status.SUCCESS_RESET_CONTENT) + || response.getStatus().isInformational()) { response.getEntity().release(); response.setEntity(null); } else if (response.getStatus().equals(Status.REDIRECTION_NOT_MODIFIED)) { response.getEntity().release(); - } else if (response.getStatus().isInformational()) { - response.getEntity().release(); - response.setEntity(null); } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java index 0bb02e5fa0..3821b336a9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java @@ -61,7 +61,7 @@ public static String getLocalAddress() { * @param method The method name. * @param requestUri The request URI. */ - public ClientCall(HttpClientHelper helper, String method, String requestUri) { + protected ClientCall(HttpClientHelper helper, String method, String requestUri) { this.helper = helper; setMethod(method); setRequestUri(requestUri); @@ -269,8 +269,7 @@ public Status sendRequest(Request request) { * @param response The high-level response. * @param callback The callback invoked upon request completion. */ - public void sendRequest(Request request, Response response, org.restlet.Uniform callback) - throws Exception { + public void sendRequest(Request request, Response response, org.restlet.Uniform callback) { Context.getCurrentLogger() .warning("Currently callbacks are not available for this connector."); } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java index bd51e99ea0..6d2f538a6d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java @@ -49,7 +49,7 @@ public abstract class HttpClientHelper extends ClientHelper { * * @param client The client to help. */ - public HttpClientHelper(Client client) { + protected HttpClientHelper(Client client) { super(client); this.adapter = null; } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java index e2031eb342..7f7fff90e5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java @@ -417,7 +417,8 @@ public Conditions getConditions() { .getLogger() .log( Level.INFO, - "Unable to process the if-match header: " + ifMatchHeader); + "Unable to process the if-match header: {0}", + ifMatchHeader); } } @@ -430,8 +431,8 @@ public Conditions getConditions() { .getLogger() .log( Level.INFO, - "Unable to process the if-none-match header: " - + ifNoneMatchHeader); + "Unable to process the if-none-match header: {0}", + ifNoneMatchHeader); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java index 2d648bccb9..50398660b5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java @@ -275,8 +275,7 @@ public Status sendRequest(org.restlet.Request request) { @Override public void sendRequest( - org.restlet.Request request, org.restlet.Response response, Uniform callback) - throws Exception { + org.restlet.Request request, org.restlet.Response response, Uniform callback) { sendRequest(request); final Uniform getOnSent = request.getOnSent(); diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java index 4f88618dbd..beb45358f4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java @@ -157,17 +157,20 @@ public void commit(HttpResponse response) { // Send the response to the client response.getHttpCall().sendResponse(response); - } catch (Throwable t) { - if (response.getHttpCall().isConnectionBroken(t)) { + } catch (Exception exception) { + if (response.getHttpCall().isConnectionBroken(exception)) { // output a single log line for this common case to avoid filling server logs getLogger() .log( Level.INFO, - "The connection was broken. It was probably closed by the client. Reason: " - + t.getMessage()); + "The connection was broken. It was probably closed by the client. Reason: {0}", + exception.getMessage()); } else { getLogger() - .log(Level.SEVERE, "An exception occurred writing the response entity", t); + .log( + Level.SEVERE, + "An exception occurred writing the response entity", + exception); response.getHttpCall().setStatusCode(Status.SERVER_ERROR_INTERNAL.getCode()); response.getHttpCall() .setReasonPhrase("An exception occurred writing the response entity"); diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java index 062d628041..4cf634ffda 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java @@ -52,7 +52,7 @@ public abstract class ServerCall extends Call { * * @param server The parent server connector. */ - public ServerCall(Server server) { + protected ServerCall(Server server) { this( (server == null) ? null : server.getAddress(), (server == null) ? 0 : server.getPort()); @@ -64,7 +64,7 @@ public ServerCall(Server server) { * @param serverAddress The server IP address. * @param serverPort The server port. */ - public ServerCall(String serverAddress, int serverPort) { + protected ServerCall(String serverAddress, int serverPort) { setServerAddress(serverAddress); setServerPort(serverPort); this.hostParsed = false; diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java b/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java index 8f28cbfe6f..025de6fbb4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java @@ -29,7 +29,7 @@ public abstract class Conneg { * @param request The request including client preferences. * @param metadataService The metadata service used to get default metadata values. */ - public Conneg(Request request, MetadataService metadataService) { + protected Conneg(Request request, MetadataService metadataService) { this.request = request; } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java index 57002fb3c1..1c58948f2f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java @@ -42,7 +42,7 @@ public class CorsFilter extends Filter { * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. * Default is true. */ - public boolean allowAllRequestedHeaders = true; + private boolean allowAllRequestedHeaders = true; /** If true, add an 'Access-Control-Allow-Credentials' header. Default is false. */ private boolean allowedCredentials = false; diff --git a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java index 40c6dc375f..3c47499313 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java @@ -36,27 +36,27 @@ public class CorsResponseHelper { * 'Access-Control-Allow-Headers' response header. If false, use {@link #allowedHeaders}. * Default is true. */ - public boolean allowAllRequestedHeaders = true; + private boolean allowAllRequestedHeaders = true; /** If true, add an 'Access-Control-Allow-Credentials' header. Default is false. */ - public boolean allowedCredentials = false; + private boolean allowedCredentials = false; /** * The value of 'Access-Control-Allow-Headers' response header. Used only if {@link * #allowAllRequestedHeaders} is false. */ - public Set allowedHeaders = null; + private Set allowedHeaders = null; /** The value of the 'Access-Control-Allow-Origin' header. Default is '*'. */ - public Set allowedOrigins = SetUtils.newHashSet("*"); + private Set allowedOrigins = SetUtils.newHashSet("*"); /** The value of 'Access-Control-Expose-Headers' response header. */ - public Set exposedHeaders = null; + private Set exposedHeaders = null; /** * The value of the 'Access-Control-Max-Age' response header. By default, the header is not set. */ - public int maxAge = -1; + private int maxAge = -1; /** * Adds CORS headers to the given response. diff --git a/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java index 07baf5f071..21d93f7087 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java @@ -12,6 +12,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; +import java.util.Objects; import org.restlet.data.Range; import org.restlet.engine.io.IoUtils; import org.restlet.engine.io.RangeInputStream; @@ -52,6 +53,12 @@ public RangeRepresentation(Representation wrappedRepresentation, Range range) { setRange(range); } + @Override + public boolean equals(final Object obj) { + return super.equals(obj) + && Objects.equals(getRange(), ((RangeRepresentation) obj).getRange()); + } + @Override public long getAvailableSize() { return IoUtils.getAvailableSize(this); @@ -83,6 +90,11 @@ public String getText() throws IOException { return IoUtils.getText(this); } + @Override + public int hashCode() { + return super.hashCode(); + } + /** * Sets the range specific to this wrapper. This will not affect the wrapped representation. * diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java index 28b26b09b8..4503a5fa3c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java @@ -103,8 +103,8 @@ protected int doHandle(Request request, Response response) { // Normally handle the call try { super.doHandle(request, response); - } catch (Throwable throwable) { - Status status = getStatusService().toStatus(throwable, request, response); + } catch (Exception exception) { + Status status = getStatusService().toStatus(exception, request, response); final Level level; if (status.isServerError()) { @@ -116,7 +116,7 @@ protected int doHandle(Request request, Response response) { } else { level = Level.FINE; } - getLogger().log(level, "Exception or error caught by status service", throwable); + getLogger().log(level, "Exception or error caught by status service", exception); if (response != null) { response.setStatus(status); diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java index 4d1c046c9b..33b8095838 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java @@ -155,18 +155,13 @@ public AnnotationInfo(Class javaClass, String annotationValue) { this(javaClass, null, annotationValue); } - /** - * Indicates if the current variant is equal to the given object. - * - * @param other The other object. - * @return True if the current object is equal to the given object. - */ + /** {@inheritDoc} */ @Override - public boolean equals(Object other) { - if (other == this) { + public boolean equals(Object obj) { + if (obj == this) { return true; } - if (!(other instanceof final AnnotationInfo that)) { + if (!(obj instanceof final AnnotationInfo that)) { return false; } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java index 247e0cccbf..3a300d3ae3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java @@ -91,18 +91,13 @@ public MethodAnnotationInfo( } } - /** - * Indicates if the current object is equal to the given object. - * - * @param other The other object. - * @return True if the current object includes the other. - */ + /** {@inheritDoc} */ @Override - public boolean equals(Object other) { - if (other == this) { + public boolean equals(Object obj) { + if (obj == this) { return true; } - if (!(other instanceof final MethodAnnotationInfo that)) { + if (!(obj instanceof final MethodAnnotationInfo that)) { return false; } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java index 562f1e4aad..28ab382acc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java @@ -41,18 +41,13 @@ public ThrowableAnnotationInfo( this.serializable = serializable; } - /** - * Indicates if the current object is equal to the given object. - * - * @param other The other object. - * @return True if the current object includes the other. - */ + /** {@inheritDoc} */ @Override - public boolean equals(Object other) { - if (other == this) { + public boolean equals(Object obj) { + if (obj == this) { return true; } - if (!(other instanceof final ThrowableAnnotationInfo that)) { + if (!(obj instanceof final ThrowableAnnotationInfo that)) { return false; } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java index 9e035828c0..fdbb3cf7e2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java @@ -60,18 +60,13 @@ public VariantInfo(Variant variant, MethodAnnotationInfo annotationInfo) { setLanguages(variant.getLanguages()); } - /** - * Indicates if the current variant is equal to the given variant. - * - * @param other The other variant. - * @return True if the current variant includes the other. - */ + /** {@inheritDoc} */ @Override - public boolean equals(Object other) { - if (other == this) { + public boolean equals(Object obj) { + if (obj == this) { return true; } - if (!(other instanceof final VariantInfo that)) { + if (!(obj instanceof final VariantInfo that)) { return false; } diff --git a/org.restlet/src/main/java/org/restlet/representation/Variant.java b/org.restlet/src/main/java/org/restlet/representation/Variant.java index fba3ab1fbd..c950cde2a1 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Variant.java +++ b/org.restlet/src/main/java/org/restlet/representation/Variant.java @@ -113,18 +113,13 @@ public ClientInfo createClientInfo() { return result; } - /** - * Indicates if the current variant is equal to the given variant. - * - * @param other The other variant. - * @return True if the current variant includes the other. - */ + /** {@inheritDoc} */ @Override - public boolean equals(Object other) { - if (other == this) { + public boolean equals(Object obj) { + if (obj == this) { return true; } - if (!(other instanceof final Variant that)) { + if (!(obj instanceof final Variant that)) { return false; } diff --git a/org.restlet/src/main/java/org/restlet/security/Role.java b/org.restlet/src/main/java/org/restlet/security/Role.java index e490ebf50f..0f1d7279ad 100644 --- a/org.restlet/src/main/java/org/restlet/security/Role.java +++ b/org.restlet/src/main/java/org/restlet/security/Role.java @@ -101,13 +101,19 @@ public Role(Application application, String name, String description) { this.childRoles = new CopyOnWriteArrayList<>(); } + /** {@inheritDoc} */ @Override - public boolean equals(Object o) { - if (!(o instanceof final Role that)) return false; + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Role that)) { + return false; + } - return Objects.equals(that.getApplication(), getApplication()) - && Objects.equals(that.getName(), getName()) - && Objects.equals(that.getChildRoles(), getChildRoles()); + return Objects.equals(getApplication(), that.getApplication()) + && Objects.equals(getName(), that.getName()) + && Objects.equals(getChildRoles(), that.getChildRoles()); } /** diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperList.java b/org.restlet/src/main/java/org/restlet/util/WrapperList.java index eb5898ad01..9fbd09f109 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperList.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperList.java @@ -115,24 +115,16 @@ public boolean containsAll(Collection elements) { return getDelegate().containsAll(elements); } - /** - * Compares the specified object with this list for equality. - * - * @param o The object to be compared for equality with this list. - * @return True if the specified object is equal to this list. - */ + /** {@inheritDoc} */ @Override - public boolean equals(Object o) { - if (this == o) { + public boolean equals(Object obj) { + if (obj == this) { return true; } - - if (o instanceof List) { - List that = (List) o; - return Arrays.equals(this.toArray(), that.toArray()); + if (!(obj instanceof List that)) { + return false; } - - return false; + return Arrays.equals(this.toArray(), that.toArray()); } /** diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java index 3fcedc41ca..d54b36cd73 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java @@ -74,12 +74,7 @@ public Set> entrySet() { return getDelegate().entrySet(); } - /** - * Compares the specified object with this map for equality. - * - * @param o Object to be compared for equality with this map. - * @return True if the specified object is equal to this map. - */ + /** {@inheritDoc} */ @Override public boolean equals(Object o) { return getDelegate().equals(o); diff --git a/org.restlet/src/test/java/org/restlet/resource/MyBean.java b/org.restlet/src/test/java/org/restlet/resource/MyBean.java index 87d1692d8a..d8a24487c9 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyBean.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyBean.java @@ -9,6 +9,7 @@ package org.restlet.resource; import java.io.Serializable; +import java.util.Objects; import org.restlet.engine.util.SystemUtils; /** @@ -32,18 +33,17 @@ public MyBean(String name, String description) { this.description = description; } + /** {@inheritDoc} */ @Override public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - MyBean other = (MyBean) obj; - if (description == null) { - if (other.description != null) return false; - } else if (!description.equals(other.description)) return false; - if (name == null) { - return other.name == null; - } else return name.equals(other.name); + if (obj == this) { + return true; + } + if (!(obj instanceof MyBean that)) { + return false; + } + return Objects.equals(getName(), that.getName()) + && Objects.equals(getDescription(), that.getDescription()); } public String getDescription() { From 81e97c54f305a0a493c9b34f0ba30b07adc9b8ea Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 14:53:11 +0200 Subject: [PATCH 09/30] Take into account Sonar comments --- .../restlet/ext/crypto/CookieAuthenticator.java | 8 ++++---- .../restlet/ext/thymeleaf/TemplateFilter.java | 10 +++++----- .../org/restlet/ext/xml/XmlRepresentation.java | 4 ++-- .../ext/xml/internal/AbstractXmlReader.java | 2 +- .../src/main/java/org/restlet/Connector.java | 4 ++-- .../src/main/java/org/restlet/Message.java | 4 ++-- .../src/main/java/org/restlet/Restlet.java | 4 ++-- .../java/org/restlet/engine/CompositeHelper.java | 2 +- .../java/org/restlet/engine/RestletHelper.java | 2 +- .../engine/connector/ConnectorHelper.java | 2 +- .../engine/connector/JettyServerHelper.java | 2 +- .../restlet/engine/connector/ProtocolHelper.java | 2 +- .../java/org/restlet/engine/local/Entity.java | 2 +- .../restlet/engine/local/EntityClientHelper.java | 2 +- .../restlet/engine/local/LocalClientHelper.java | 2 +- .../restlet/engine/resource/AnnotationInfo.java | 4 ++-- .../engine/security/AuthenticatorHelper.java | 2 +- .../restlet/engine/util/ContextualRunnable.java | 2 +- .../main/java/org/restlet/engine/util/Pool.java | 4 ++-- .../representation/CharacterRepresentation.java | 2 +- .../representation/OutputRepresentation.java | 4 ++-- .../restlet/representation/Representation.java | 16 ++++++++-------- .../representation/StreamRepresentation.java | 2 +- .../representation/WriterRepresentation.java | 4 ++-- .../org/restlet/resource/ServerResource.java | 2 +- .../main/java/org/restlet/routing/Filter.java | 6 +++--- .../src/main/java/org/restlet/routing/Route.java | 4 ++-- .../java/org/restlet/security/Authenticator.java | 8 ++++---- .../java/org/restlet/security/Authorizer.java | 4 ++-- .../main/java/org/restlet/security/Realm.java | 4 ++-- .../main/java/org/restlet/service/Service.java | 4 ++-- .../jetty/resource/JettyConnectorTestCase.java | 2 +- 32 files changed, 63 insertions(+), 63 deletions(-) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java index ad18df95a2..e49e4afa94 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java @@ -477,14 +477,14 @@ protected int logout(Request request, Response response) { * @return The credentials as a proper challenge response. */ protected ChallengeResponse parseCredentials(String cookieValue) { + if (cookieValue == null) { + return null; + } + try { // 1) Decode Base64 string byte[] encrypted = Base64.getDecoder().decode(cookieValue); - if (encrypted == null) { - getLogger().warning("Cannot decode cookie credentials : " + cookieValue); - } - // 2) Decrypt the credentials String decrypted = CryptoUtils.decrypt(getEncryptAlgorithm(), getEncryptSecretKey(), encrypted); diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java index e7f1ed1f9e..75e49efed3 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java @@ -42,7 +42,7 @@ public abstract class TemplateFilter extends Filter { private volatile Resolver resolverDataModel; /** Constructor. */ - public TemplateFilter() { + protected TemplateFilter() { super(); } @@ -51,7 +51,7 @@ public TemplateFilter() { * * @param context The context. */ - public TemplateFilter(Context context) { + protected TemplateFilter(Context context) { super(context); } @@ -61,7 +61,7 @@ public TemplateFilter(Context context) { * @param context The context. * @param next The next Restlet. */ - public TemplateFilter(Context context, Restlet next) { + protected TemplateFilter(Context context, Restlet next) { super(context, next); this.mapDataModel = null; this.resolverDataModel = null; @@ -74,7 +74,7 @@ public TemplateFilter(Context context, Restlet next) { * @param next The next Restlet. * @param dataModel The filter's data model. */ - public TemplateFilter(Context context, Restlet next, Map dataModel) { + protected TemplateFilter(Context context, Restlet next, Map dataModel) { super(context, next); this.mapDataModel = dataModel; this.resolverDataModel = null; @@ -87,7 +87,7 @@ public TemplateFilter(Context context, Restlet next, Map dataMod * @param next The next Restlet. * @param dataModel The filter's data model. */ - public TemplateFilter(Context context, Restlet next, Resolver dataModel) { + protected TemplateFilter(Context context, Restlet next, Resolver dataModel) { super(context, next); this.mapDataModel = null; this.resolverDataModel = dataModel; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java index 8ac8e0fdef..d5c12b0c19 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java @@ -250,7 +250,7 @@ public static String getTextContent(Node node) { * * @param mediaType The representation's mediaType. */ - public XmlRepresentation(MediaType mediaType) { + protected XmlRepresentation(MediaType mediaType) { this(mediaType, UNKNOWN_SIZE); } @@ -260,7 +260,7 @@ public XmlRepresentation(MediaType mediaType) { * @param mediaType The representation's mediaType. * @param expectedSize The expected input stream size. */ - public XmlRepresentation(MediaType mediaType, long expectedSize) { + protected XmlRepresentation(MediaType mediaType, long expectedSize) { super(mediaType, expectedSize); this.coalescing = false; this.entityResolver = null; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java index fc7c348184..7d3ca76aa8 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java @@ -41,7 +41,7 @@ public abstract class AbstractXmlReader implements XMLReader { private final HashMap properties; /** Default constructor. */ - public AbstractXmlReader() { + protected AbstractXmlReader() { this.features = new HashMap<>(); this.properties = new HashMap<>(); this.contentHandler = null; diff --git a/org.restlet/src/main/java/org/restlet/Connector.java b/org.restlet/src/main/java/org/restlet/Connector.java index a6ee939947..9810ac6917 100644 --- a/org.restlet/src/main/java/org/restlet/Connector.java +++ b/org.restlet/src/main/java/org/restlet/Connector.java @@ -42,7 +42,7 @@ public abstract class Connector extends Restlet { * * @param context The context. */ - public Connector(Context context) { + protected Connector(Context context) { this(context, null); } @@ -52,7 +52,7 @@ public Connector(Context context) { * @param context The context. * @param protocols The supported protocols. */ - public Connector(Context context, List protocols) { + protected Connector(Context context, List protocols) { super(context); if (protocols == null) { diff --git a/org.restlet/src/main/java/org/restlet/Message.java b/org.restlet/src/main/java/org/restlet/Message.java index 2269c9cb39..b84dcb09c3 100644 --- a/org.restlet/src/main/java/org/restlet/Message.java +++ b/org.restlet/src/main/java/org/restlet/Message.java @@ -62,7 +62,7 @@ public abstract class Message { private volatile List warnings; /** Constructor. */ - public Message() { + protected Message() { this(null); } @@ -71,7 +71,7 @@ public Message() { * * @param entity The payload of the message. */ - public Message(Representation entity) { + protected Message(Representation entity) { this.attributes = null; this.cacheDirectives = null; this.date = null; diff --git a/org.restlet/src/main/java/org/restlet/Restlet.java b/org.restlet/src/main/java/org/restlet/Restlet.java index 161daa97d0..139538a5e6 100644 --- a/org.restlet/src/main/java/org/restlet/Restlet.java +++ b/org.restlet/src/main/java/org/restlet/Restlet.java @@ -74,7 +74,7 @@ private static void fireContextChanged(Restlet restlet, Context context) { private volatile boolean started; /** Constructor with null context. */ - public Restlet() { + protected Restlet() { this(null); } @@ -85,7 +85,7 @@ public Restlet() { * @see Context#createChildContext() * @param context The context of the Restlet. */ - public Restlet(Context context) { + protected Restlet(Context context) { this.context = context; this.started = false; this.name = toString(); diff --git a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java index 14a60d53a4..348e1fe452 100644 --- a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java @@ -45,7 +45,7 @@ public abstract class CompositeHelper extends RestletHelper extends Helper { * * @param helped The helped Restlet. */ - public RestletHelper(T helped) { + protected RestletHelper(T helped) { this.attributes = new ConcurrentHashMap<>(); this.helped = helped; } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java index 4fb40acfe3..6b7daee07d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java @@ -43,7 +43,7 @@ public static org.restlet.service.ConnectorService getConnectorService() { private final List protocols; /** Constructor. */ - public ConnectorHelper(T connector) { + protected ConnectorHelper(T connector) { super(connector); this.protocols = new CopyOnWriteArrayList<>(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java index 2b63b7f0de..396374ed4e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java @@ -217,7 +217,7 @@ public abstract class JettyServerHelper extends org.restlet.engine.adapter.HttpS * * @param server The server to help. */ - public JettyServerHelper(Server server) { + protected JettyServerHelper(Server server) { super(server); } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java index c2ddcc2e07..85b281cc16 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java @@ -18,7 +18,7 @@ public abstract class ProtocolHelper extends Helper { /** Constructor. */ - public ProtocolHelper() { + protected ProtocolHelper() { super(); registerMethods(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java index 97bf1d9e41..d49ca0f333 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java @@ -223,7 +223,7 @@ public static void updateMetadata( * * @param metadataService The metadata service to use. */ - public Entity(MetadataService metadataService) { + protected Entity(MetadataService metadataService) { this.metadataService = metadataService; } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java index 522c92619e..2b46331f44 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java @@ -35,7 +35,7 @@ public abstract class EntityClientHelper extends LocalClientHelper { * * @param client The client to help. */ - public EntityClientHelper(Client client) { + protected EntityClientHelper(Client client) { super(client); } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java index 5be8249255..7bf7d7d6c5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java @@ -55,7 +55,7 @@ public abstract class LocalClientHelper extends ClientHelper { * * @param client The client to help. */ - public LocalClientHelper(Client client) { + protected LocalClientHelper(Client client) { super(client); } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java index 33b8095838..f871aa7df6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java @@ -124,7 +124,7 @@ protected static Class getTypeClass(Type type) { * @param javaMethod The annotated Java method. * @param annotationValue The annotation value. */ - public AnnotationInfo( + protected AnnotationInfo( Class javaClass, java.lang.reflect.Method javaMethod, String annotationValue) { super(); this.javaClass = javaClass; @@ -151,7 +151,7 @@ public AnnotationInfo( * @param javaClass The annotated Java class or parent Java class. * @param annotationValue The annotation value. */ - public AnnotationInfo(Class javaClass, String annotationValue) { + protected AnnotationInfo(Class javaClass, String annotationValue) { this(javaClass, null, annotationValue); } diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java index eacdbbdd51..abf6e2d21f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java @@ -46,7 +46,7 @@ public abstract class AuthenticatorHelper extends Helper { * @param clientSide Indicates if client side authentication is supported. * @param serverSide Indicates if server side authentication is supported. */ - public AuthenticatorHelper( + protected AuthenticatorHelper( ChallengeScheme challengeScheme, boolean clientSide, boolean serverSide) { this.challengeScheme = challengeScheme; this.clientSide = clientSide; diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java b/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java index 84d3aff446..5d86273c53 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java @@ -18,7 +18,7 @@ public abstract class ContextualRunnable implements Runnable { private ClassLoader contextClassLoader; /** Constructor. */ - public ContextualRunnable() { + protected ContextualRunnable() { this.contextClassLoader = Thread.currentThread().getContextClassLoader(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java index 1d58b057f5..1a06dba496 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java @@ -23,7 +23,7 @@ public abstract class Pool { private final Queue store; /** Default constructor. */ - public Pool() { + protected Pool() { this.store = createStore(); } @@ -33,7 +33,7 @@ public Pool() { * * @param initialSize The initial number of objects in the pool. */ - public Pool(int initialSize) { + protected Pool(int initialSize) { this(); preCreate(initialSize); } diff --git a/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java index 3aa81e2457..1fe3ddd5ea 100644 --- a/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java @@ -27,7 +27,7 @@ public abstract class CharacterRepresentation extends Representation { * * @param mediaType The media type. */ - public CharacterRepresentation(MediaType mediaType) { + protected CharacterRepresentation(MediaType mediaType) { super(mediaType); setCharacterSet(CharacterSet.UTF_8); } diff --git a/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java index af500de364..852bb752bf 100644 --- a/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java @@ -29,7 +29,7 @@ public abstract class OutputRepresentation extends StreamRepresentation { * * @param mediaType The representation's mediaType. */ - public OutputRepresentation(MediaType mediaType) { + protected OutputRepresentation(MediaType mediaType) { super(mediaType); } @@ -39,7 +39,7 @@ public OutputRepresentation(MediaType mediaType) { * @param mediaType The representation's mediaType. * @param expectedSize The expected input stream size. */ - public OutputRepresentation(MediaType mediaType, long expectedSize) { + protected OutputRepresentation(MediaType mediaType, long expectedSize) { super(mediaType); setSize(expectedSize); } diff --git a/org.restlet/src/main/java/org/restlet/representation/Representation.java b/org.restlet/src/main/java/org/restlet/representation/Representation.java index a6db42b130..2523871000 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Representation.java +++ b/org.restlet/src/main/java/org/restlet/representation/Representation.java @@ -73,7 +73,7 @@ public abstract class Representation extends RepresentationInfo { private volatile long size; /** Default constructor. */ - public Representation() { + protected Representation() { this(null); } @@ -82,7 +82,7 @@ public Representation() { * * @param mediaType The media type. */ - public Representation(MediaType mediaType) { + protected Representation(MediaType mediaType) { super(mediaType); this.available = true; this.disposition = null; @@ -99,7 +99,7 @@ public Representation(MediaType mediaType) { * @param mediaType The media type. * @param modificationDate The modification date. */ - public Representation(MediaType mediaType, Date modificationDate) { + protected Representation(MediaType mediaType, Date modificationDate) { this(mediaType, modificationDate, null); } @@ -110,7 +110,7 @@ public Representation(MediaType mediaType, Date modificationDate) { * @param modificationDate The modification date. * @param tag The tag. */ - public Representation(MediaType mediaType, Date modificationDate, Tag tag) { + protected Representation(MediaType mediaType, Date modificationDate, Tag tag) { super(mediaType, modificationDate, tag); } @@ -120,7 +120,7 @@ public Representation(MediaType mediaType, Date modificationDate, Tag tag) { * @param mediaType The media type. * @param tag The tag. */ - public Representation(MediaType mediaType, Tag tag) { + protected Representation(MediaType mediaType, Tag tag) { this(mediaType, null, tag); } @@ -130,7 +130,7 @@ public Representation(MediaType mediaType, Tag tag) { * @param variant The variant to copy. * @param modificationDate The modification date. */ - public Representation(Variant variant, Date modificationDate) { + protected Representation(Variant variant, Date modificationDate) { this(variant, modificationDate, null); } @@ -141,7 +141,7 @@ public Representation(Variant variant, Date modificationDate) { * @param modificationDate The modification date. * @param tag The tag. */ - public Representation(Variant variant, Date modificationDate, Tag tag) { + protected Representation(Variant variant, Date modificationDate, Tag tag) { setCharacterSet(variant.getCharacterSet()); setEncodings(variant.getEncodings()); setLocationRef(variant.getLocationRef()); @@ -157,7 +157,7 @@ public Representation(Variant variant, Date modificationDate, Tag tag) { * @param variant The variant to copy. * @param tag The tag. */ - public Representation(Variant variant, Tag tag) { + protected Representation(Variant variant, Tag tag) { this(variant, null, tag); } diff --git a/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java index 883f20fd16..4247be84ff 100644 --- a/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java @@ -26,7 +26,7 @@ public abstract class StreamRepresentation extends Representation { * * @param mediaType The media type. */ - public StreamRepresentation(MediaType mediaType) { + protected StreamRepresentation(MediaType mediaType) { super(mediaType); } diff --git a/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java index 488e272bb5..4fe4fa29de 100644 --- a/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java @@ -30,7 +30,7 @@ public abstract class WriterRepresentation extends CharacterRepresentation { * * @param mediaType The representation's mediaType. */ - public WriterRepresentation(MediaType mediaType) { + protected WriterRepresentation(MediaType mediaType) { super(mediaType); } @@ -40,7 +40,7 @@ public WriterRepresentation(MediaType mediaType) { * @param mediaType The representation's mediaType. * @param expectedSize The expected writer size in bytes. */ - public WriterRepresentation(MediaType mediaType, long expectedSize) { + protected WriterRepresentation(MediaType mediaType, long expectedSize) { super(mediaType); setSize(expectedSize); } diff --git a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java index 3d1b48e363..77ddcb9b7f 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java @@ -110,7 +110,7 @@ public abstract class ServerResource extends Resource { * Default constructor. Note that the {@link #init(Context, Request, Response)}() method will be * invoked right after the creation of the resource. */ - public ServerResource() {} + protected ServerResource() {} /** * Ask the connector to abort the related network connection, for example immediately closing diff --git a/org.restlet/src/main/java/org/restlet/routing/Filter.java b/org.restlet/src/main/java/org/restlet/routing/Filter.java index 1eecdf4242..21210621dd 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Filter.java +++ b/org.restlet/src/main/java/org/restlet/routing/Filter.java @@ -56,7 +56,7 @@ public abstract class Filter extends Restlet { private volatile Restlet next; /** Constructor. */ - public Filter() { + protected Filter() { this(null); } @@ -65,7 +65,7 @@ public Filter() { * * @param context The context. */ - public Filter(Context context) { + protected Filter(Context context) { this(context, null); } @@ -75,7 +75,7 @@ public Filter(Context context) { * @param context The context. * @param next The next Restlet. */ - public Filter(Context context, Restlet next) { + protected Filter(Context context, Restlet next) { super(context); this.next = next; } diff --git a/org.restlet/src/main/java/org/restlet/routing/Route.java b/org.restlet/src/main/java/org/restlet/routing/Route.java index 1df38cc2a5..e9c0b6832b 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Route.java +++ b/org.restlet/src/main/java/org/restlet/routing/Route.java @@ -33,7 +33,7 @@ public abstract class Route extends Filter { * * @param next The next Restlet. */ - public Route(Restlet next) { + protected Route(Restlet next) { this(null, next); } @@ -43,7 +43,7 @@ public Route(Restlet next) { * @param router The parent router. * @param next The next Restlet. */ - public Route(Router router, Restlet next) { + protected Route(Router router, Restlet next) { super( (router != null) ? router.getContext() : (next != null) ? next.getContext() : null, next); diff --git a/org.restlet/src/main/java/org/restlet/security/Authenticator.java b/org.restlet/src/main/java/org/restlet/security/Authenticator.java index 8e2fd52358..57367189c2 100644 --- a/org.restlet/src/main/java/org/restlet/security/Authenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/Authenticator.java @@ -48,7 +48,7 @@ public abstract class Authenticator extends Filter { * @param context The context. * @see #Authenticator(Context, boolean) */ - public Authenticator(Context context) { + protected Authenticator(Context context) { this(context, false); } @@ -59,7 +59,7 @@ public Authenticator(Context context) { * @param optional Indicates if the authenticator is not required to succeed. * @see #Authenticator(Context, boolean, Enroler) */ - public Authenticator(Context context, boolean optional) { + protected Authenticator(Context context, boolean optional) { this(context, optional, (context != null) ? context.getDefaultEnroler() : null); } @@ -72,7 +72,7 @@ public Authenticator(Context context, boolean optional) { * @param optional Indicates if the authenticator is not required to succeed. * @param enroler The enroler to invoke upon successful authentication. */ - public Authenticator( + protected Authenticator( Context context, boolean multiAuthenticating, boolean optional, Enroler enroler) { super(context); this.multiAuthenticating = multiAuthenticating; @@ -87,7 +87,7 @@ public Authenticator( * @param optional Indicates if the authenticator is not required to succeed. * @param enroler The enroler to invoke upon successful authentication. */ - public Authenticator(Context context, boolean optional, Enroler enroler) { + protected Authenticator(Context context, boolean optional, Enroler enroler) { this(context, true, optional, enroler); } diff --git a/org.restlet/src/main/java/org/restlet/security/Authorizer.java b/org.restlet/src/main/java/org/restlet/security/Authorizer.java index 55535c2f9d..d8eba44e1e 100644 --- a/org.restlet/src/main/java/org/restlet/security/Authorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/Authorizer.java @@ -67,14 +67,14 @@ public boolean authorize(Request request, Response response) { private volatile String identifier; /** Default constructor. */ - public Authorizer() {} + protected Authorizer() {} /** * Constructor. * * @param identifier The identifier unique within an application. */ - public Authorizer(String identifier) { + protected Authorizer(String identifier) { this.identifier = identifier; } diff --git a/org.restlet/src/main/java/org/restlet/security/Realm.java b/org.restlet/src/main/java/org/restlet/security/Realm.java index 12631a4755..3f1de7db9c 100644 --- a/org.restlet/src/main/java/org/restlet/security/Realm.java +++ b/org.restlet/src/main/java/org/restlet/security/Realm.java @@ -37,7 +37,7 @@ public abstract class Realm { private volatile boolean started; /** Constructor. */ - public Realm() { + protected Realm() { this(null, null); } @@ -48,7 +48,7 @@ public Realm() { * request. * @param enroler The enroler that can add the user roles based on user principals. */ - public Realm(Verifier verifier, Enroler enroler) { + protected Realm(Verifier verifier, Enroler enroler) { this.enroler = enroler; this.verifier = verifier; this.parameters = diff --git a/org.restlet/src/main/java/org/restlet/service/Service.java b/org.restlet/src/main/java/org/restlet/service/Service.java index 41088204d6..78e2469d63 100644 --- a/org.restlet/src/main/java/org/restlet/service/Service.java +++ b/org.restlet/src/main/java/org/restlet/service/Service.java @@ -31,7 +31,7 @@ public abstract class Service { private volatile boolean started; /** Constructor. Enables the service by default. */ - public Service() { + protected Service() { this(true); } @@ -40,7 +40,7 @@ public Service() { * * @param enabled True if the service has been enabled. */ - public Service(boolean enabled) { + protected Service(boolean enabled) { this.context = null; this.enabled = enabled; } diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java index 6676cd7560..76664bb584 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java @@ -37,7 +37,7 @@ public abstract class JettyConnectorTestCase { private String uri; - public JettyConnectorTestCase() { + protected JettyConnectorTestCase() { super(); } From a5194486920422bd1fd8e029ebb493325b451f52 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 15:26:10 +0200 Subject: [PATCH 10/30] Take into account Sonar comments --- .../freemarker/TemplateRepresentation.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java index 74d5f781e5..de0a4cccf9 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java @@ -8,6 +8,8 @@ */ package org.restlet.ext.freemarker; +import static freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS; + import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; @@ -129,7 +131,10 @@ public TemplateRepresentation( * @param mediaType The representation's media-type. */ public TemplateRepresentation(Representation templateRepresentation, MediaType mediaType) { - this(templateRepresentation, new Configuration(), mediaType); + this( + templateRepresentation, + new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS), + mediaType); } /** @@ -141,7 +146,11 @@ public TemplateRepresentation(Representation templateRepresentation, MediaType m */ public TemplateRepresentation( Representation templateRepresentation, Object dataModel, MediaType mediaType) { - this(templateRepresentation, new Configuration(), dataModel, mediaType); + this( + templateRepresentation, + new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS), + dataModel, + mediaType); } /** @@ -194,6 +203,11 @@ public TemplateRepresentation(Template template, Object dataModel, MediaType med this.dataModel = dataModel; } + @Override + public boolean equals(final Object obj) { + return super.equals(obj); + } + /** * Returns the template's data model. * @@ -212,6 +226,11 @@ public Template getTemplate() { return template; } + @Override + public int hashCode() { + return super.hashCode(); + } + /** * Sets the template's data model. * From 5f367924d05b043b7b8fc9ff0ef634e685846b16 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 15:40:57 +0200 Subject: [PATCH 11/30] Take into account Sonar comments --- .../java/org/restlet/ext/crypto/package-info.java | 6 ++++++ .../main/java/org/restlet/ext/crypto/package.html | 7 ------- .../org/restlet/ext/freemarker/package-info.java | 11 +++++++++++ .../java/org/restlet/ext/freemarker/package.html | 9 --------- .../java/org/restlet/ext/gson/package-info.java | 9 +++++++++ .../main/java/org/restlet/ext/gson/package.html | 9 --------- .../java/org/restlet/ext/jaas/package-info.java | 8 ++++++++ .../main/java/org/restlet/ext/jaas/package.html | 8 -------- .../java/org/restlet/ext/jackson/package-info.java | 10 ++++++++++ .../main/java/org/restlet/ext/jackson/package.html | 9 --------- .../java/org/restlet/ext/json/package-info.java | 13 +++++++++++++ .../main/java/org/restlet/ext/json/package.html | 14 -------------- .../java/org/restlet/ext/slf4j/package-info.java | 10 ++++++++++ .../main/java/org/restlet/ext/slf4j/package.html | 11 ----------- .../java/org/restlet/ext/spring/package-info.java | 9 +++++++++ .../main/java/org/restlet/ext/spring/package.html | 9 --------- .../org/restlet/ext/thymeleaf/package-info.java | 10 ++++++++++ .../java/org/restlet/ext/thymeleaf/package.html | 9 --------- .../org/restlet/ext/velocity/package-info.java | 10 ++++++++++ .../java/org/restlet/ext/velocity/package.html | 9 --------- .../java/org/restlet/ext/xml/package-info.java | 8 ++++++++ .../src/main/java/org/restlet/ext/xml/package.html | 8 -------- .../main/java/org/restlet/data/package-info.java | 14 ++++++++++++++ .../src/main/java/org/restlet/data/package.html | 12 ------------ .../org/restlet/engine/adapter/package-info.java | 6 ++++++ .../java/org/restlet/engine/adapter/package.html | 7 ------- .../restlet/engine/application/package-info.java | 6 ++++++ .../org/restlet/engine/application/package.html | 7 ------- .../org/restlet/engine/component/package-info.java | 6 ++++++ .../java/org/restlet/engine/component/package.html | 7 ------- .../org/restlet/engine/connector/package-info.java | 9 +++++++++ .../java/org/restlet/engine/connector/package.html | 9 --------- .../org/restlet/engine/converter/package-info.java | 6 ++++++ .../java/org/restlet/engine/converter/package.html | 7 ------- .../org/restlet/engine/header/package-info.java | 6 ++++++ .../java/org/restlet/engine/header/package.html | 7 ------- .../java/org/restlet/engine/io/package-info.java | 6 ++++++ .../main/java/org/restlet/engine/io/package.html | 6 ------ .../org/restlet/engine/local/package-info.java | 6 ++++++ .../java/org/restlet/engine/local/package.html | 7 ------- .../java/org/restlet/engine/log/package-info.java | 6 ++++++ .../main/java/org/restlet/engine/log/package.html | 7 ------- .../main/java/org/restlet/engine/package-info.java | 8 ++++++++ .../src/main/java/org/restlet/engine/package.html | 8 -------- .../org/restlet/engine/resource/package-info.java | 6 ++++++ .../java/org/restlet/engine/resource/package.html | 7 ------- .../org/restlet/engine/security/package-info.java | 6 ++++++ .../java/org/restlet/engine/security/package.html | 7 ------- .../java/org/restlet/engine/ssl/package-info.java | 6 ++++++ .../main/java/org/restlet/engine/ssl/package.html | 7 ------- .../java/org/restlet/engine/util/package-info.java | 6 ++++++ .../main/java/org/restlet/engine/util/package.html | 7 ------- .../src/main/java/org/restlet/package-info.java | 12 ++++++++++++ org.restlet/src/main/java/org/restlet/package.html | 10 ---------- .../org/restlet/representation/package-info.java | 8 ++++++++ .../java/org/restlet/representation/package.html | 8 -------- .../java/org/restlet/resource/package-info.java | 9 +++++++++ .../main/java/org/restlet/resource/package.html | 8 -------- .../java/org/restlet/routing/package-info.java | 8 ++++++++ .../src/main/java/org/restlet/routing/package.html | 8 -------- .../java/org/restlet/security/package-info.java | 9 +++++++++ .../main/java/org/restlet/security/package.html | 8 -------- .../java/org/restlet/service/package-info.java | 9 +++++++++ .../src/main/java/org/restlet/service/package.html | 8 -------- .../main/java/org/restlet/util/package-info.java | 6 ++++++ .../src/main/java/org/restlet/util/package.html | 7 ------- .../java/org/restlet/security/package-info.java | 6 ++++++ .../test/java/org/restlet/security/package.html | 6 ------ 68 files changed, 274 insertions(+), 277 deletions(-) create mode 100644 org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package-info.java delete mode 100644 org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package.html create mode 100644 org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package-info.java delete mode 100644 org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package.html create mode 100644 org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package-info.java delete mode 100644 org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package.html create mode 100644 org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package-info.java delete mode 100644 org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package.html create mode 100644 org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package-info.java delete mode 100644 org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package.html create mode 100644 org.restlet.ext.json/src/main/java/org/restlet/ext/json/package-info.java delete mode 100644 org.restlet.ext.json/src/main/java/org/restlet/ext/json/package.html create mode 100644 org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package-info.java delete mode 100644 org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package.html create mode 100644 org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package-info.java delete mode 100644 org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package.html create mode 100644 org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package-info.java delete mode 100644 org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package.html create mode 100644 org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package-info.java delete mode 100644 org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package.html create mode 100644 org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package-info.java delete mode 100644 org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package.html create mode 100644 org.restlet/src/main/java/org/restlet/data/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/data/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/adapter/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/adapter/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/application/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/application/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/component/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/component/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/connector/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/connector/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/converter/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/converter/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/header/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/header/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/io/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/io/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/local/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/local/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/log/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/log/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/resource/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/resource/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/security/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/security/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/ssl/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/ssl/package.html create mode 100644 org.restlet/src/main/java/org/restlet/engine/util/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/engine/util/package.html create mode 100644 org.restlet/src/main/java/org/restlet/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/package.html create mode 100644 org.restlet/src/main/java/org/restlet/representation/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/representation/package.html create mode 100644 org.restlet/src/main/java/org/restlet/resource/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/resource/package.html create mode 100644 org.restlet/src/main/java/org/restlet/routing/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/routing/package.html create mode 100644 org.restlet/src/main/java/org/restlet/security/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/security/package.html create mode 100644 org.restlet/src/main/java/org/restlet/service/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/service/package.html create mode 100644 org.restlet/src/main/java/org/restlet/util/package-info.java delete mode 100644 org.restlet/src/main/java/org/restlet/util/package.html create mode 100644 org.restlet/src/test/java/org/restlet/security/package-info.java delete mode 100644 org.restlet/src/test/java/org/restlet/security/package.html diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package-info.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package-info.java new file mode 100644 index 0000000000..056c91ed15 --- /dev/null +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package-info.java @@ -0,0 +1,6 @@ +/** + * Support for cryptography including Amazon S3 and Windows Azure client authentication. + * + * @since Restlet 2.0 + */ +package org.restlet.ext.crypto; diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package.html b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package.html deleted file mode 100644 index c2afcf8cf3..0000000000 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Support for cryptography including Amazon S3 and Windows Azure client authentication. - -@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package-info.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package-info.java new file mode 100644 index 0000000000..9b95514138 --- /dev/null +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package-info.java @@ -0,0 +1,11 @@ +/** + * Integration with Apache FreeMarker 2.3 template library. FreeMarker is a template engine; a + * generic tool to generate text output (anything from HTML to autogenerated source code) based on + * templates. + * + * @since Restlet 1.0 + * @see FreeMarker template engine + * @see User + * Guide - FreeMarker extension + */ +package org.restlet.ext.freemarker; diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package.html b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package.html deleted file mode 100644 index df1a980930..0000000000 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Integration with Apache FreeMarker 2.3 template library. FreeMarker is a template engine; a generic tool to generate text output (anything from HTML to autogenerated source code) based on templates. - -@since Restlet 1.0 -@see FreeMarker template engine -@see User Guide - FreeMarker extension - - \ No newline at end of file diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package-info.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package-info.java new file mode 100644 index 0000000000..05203004b7 --- /dev/null +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package-info.java @@ -0,0 +1,9 @@ +/** + * Integration with Gson 2.8. + * + * @since Restlet 2.2 + * @see Gson Web site + * @see User Guide + * - Gson extension + */ +package org.restlet.ext.gson; diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package.html b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package.html deleted file mode 100644 index 242aeed0c9..0000000000 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Integration with Gson 2.8. - -@since Restlet 2.2 -@see Gson Web site -@see User Guide - Gson extension - - diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package-info.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package-info.java new file mode 100644 index 0000000000..bf13f47f47 --- /dev/null +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package-info.java @@ -0,0 +1,8 @@ +/** + * Support for JAAS authentication and authorization framework. + * + * @since Restlet 2.0 + * @see User Guide + * - JAAS extension + */ +package org.restlet.ext.jaas; diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package.html b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package.html deleted file mode 100644 index f7b4727bd0..0000000000 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - -Support for JAAS authentication and authorization framework. - -@since Restlet 2.0 -@see User Guide - JAAS extension - - \ No newline at end of file diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package-info.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package-info.java new file mode 100644 index 0000000000..25c3b0b400 --- /dev/null +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package-info.java @@ -0,0 +1,10 @@ +/** + * Integration with Jackson 2.10. Jackson is a high-performance JSON processor able to serialize + * objects to JSON and back again. + * + * @since Restlet 2.0 + * @see Jackson Web site + * @see User + * Guide - Jackson extension + */ +package org.restlet.ext.jackson; diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package.html b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package.html deleted file mode 100644 index b3ca770c1e..0000000000 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Integration with Jackson 2.10. Jackson is a high-performance JSON processor able to serialize objects to JSON and back again. - -@since Restlet 2.0 -@see Jackson Web site -@see User Guide - Jackson extension - - diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/package-info.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/package-info.java new file mode 100644 index 0000000000..39795d857e --- /dev/null +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/package-info.java @@ -0,0 +1,13 @@ +/** + * Support for JSON representations. JSON stands for JavaScript Object Notation and is a lightweight + * data-interchange format. This extension is based on the JSON library from Java provided by the + * official JSON.org website. This library is useful in simple cases but not tuned for performance + * or extensive serialization support of POJOs. If you need such features, you can also consider the + * XStream extension which supports both XML and JSON serialization. + * + * @since Restlet 1.0 + * @see JSON project + * @see User Guide + * - JSON extension + */ +package org.restlet.ext.json; diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/package.html b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/package.html deleted file mode 100644 index 969c61b085..0000000000 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - -Support for JSON representations. JSON stands for JavaScript Object Notation and is a lightweight -data-interchange format. This extension is based on the JSON library from Java providing -by the official JSON.org Web site. This library is useful in simple cases, but not tuned -for performance or extensive serialization support of POJOs. If you need such features, -you can also consider the XStream extension which supports both XML and JSON serialization. -

- -@since Restlet 1.0 -@see JSON project -@see User Guide - JSON extension - - \ No newline at end of file diff --git a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package-info.java b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package-info.java new file mode 100644 index 0000000000..573b79ac55 --- /dev/null +++ b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package-info.java @@ -0,0 +1,10 @@ +/** + * Integration with SLF4J 1.7. This extension provides a log facade for SLF4J for the Restlet + * engine, allowing bridges to alternate logging mechanisms such as Log4J or LogBack. + * + * @since Restlet 2.0 + * @see SLF4J home + * @see User + * Guide - SLF4J extension + */ +package org.restlet.ext.slf4j; diff --git a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package.html b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package.html deleted file mode 100644 index 2b05dfe633..0000000000 --- a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/package.html +++ /dev/null @@ -1,11 +0,0 @@ - - -Integration with SLF4J 1.7. This extension provides a log facade for -SLF4J for the Restlet engine, allowing bridges to alternate logging mechanisms -such as Log4J or LogBack. - -@since Restlet 2.0 -@see SLF4J home -@see User Guide - SLF4J extension - - \ No newline at end of file diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package-info.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package-info.java new file mode 100644 index 0000000000..d716dacca4 --- /dev/null +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package-info.java @@ -0,0 +1,9 @@ +/** + * Integration with Spring Framework 5.2. + * + * @since Restlet 1.0 + * @see Spring Framework + * @see User + * Guide - Spring extension + */ +package org.restlet.ext.spring; diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package.html b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package.html deleted file mode 100644 index 8d7a3ec8d7..0000000000 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Integration with Spring Framework 5.2. - -@since Restlet 1.0 -@see Spring Framework -@see User Guide - Spring extension - - \ No newline at end of file diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package-info.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package-info.java new file mode 100644 index 0000000000..87d07f9321 --- /dev/null +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package-info.java @@ -0,0 +1,10 @@ +/** + * Integration with Thymeleaf 3.0. Thymeleaf is a "template engine"; a generic tool to generate text + * output (anything from HTML to autogenerated source code) based on templates. + * + * @since Restlet 2.2 + * @see Thymelead library + * @see User + * Guide - Thymeleaf extension + */ +package org.restlet.ext.thymeleaf; diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package.html b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package.html deleted file mode 100644 index 2318a11ea5..0000000000 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Integration with Thymeleaf 3.0. Thymeleaf is a "template engine"; a generic tool to generate text output (anything from HTML to autogenerated source code) based on templates. - -@since Restlet 2.2 -@see Thymelead library -@see User Guide - Thymeleaf extension - - \ No newline at end of file diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package-info.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package-info.java new file mode 100644 index 0000000000..d759385fe1 --- /dev/null +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package-info.java @@ -0,0 +1,10 @@ +/** + * Integration with Apache Velocity 2.1. Velocity is a "template engine"; a generic tool to generate + * text output (anything from HTML to autogenerated source code) based on templates. + * + * @since Restlet 1.0 + * @see Velocity template engine + * @see User + * Guide - Velocity extension + */ +package org.restlet.ext.velocity; diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package.html b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package.html deleted file mode 100644 index 515a7065cc..0000000000 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Integration with Apache Velocity 2.1. Velocity is a "template engine"; a generic tool to generate text output (anything from HTML to autogenerated source code) based on templates. - -@since Restlet 1.0 -@see Velocity template engine -@see User Guide - Velocity extension - - \ No newline at end of file diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package-info.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package-info.java new file mode 100644 index 0000000000..327e97594c --- /dev/null +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package-info.java @@ -0,0 +1,8 @@ +/** + * Support for XML and XSLT representations. + * + * @since Restlet 2.0 + * @see User Guide + * - XML extension + */ +package org.restlet.ext.xml; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package.html b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package.html deleted file mode 100644 index b8b4669cdb..0000000000 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - -Support for XML and XSLT representations. - -@since Restlet 2.0 -@see User Guide - XML extension - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/data/package-info.java b/org.restlet/src/main/java/org/restlet/data/package-info.java new file mode 100644 index 0000000000..76c67eacf1 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/data/package-info.java @@ -0,0 +1,14 @@ +/** + * Information exchanged by components. "A datum is an element of information that is transferred + * from a component, or received by a component, via a connector." Roy T. Fielding. + * + * @since Restlet 1.0 + * @see Source + * dissertation + * @see User + * Guide - Mapping HTTP headers + * @see User Guide - + * Data package + */ +package org.restlet.data; diff --git a/org.restlet/src/main/java/org/restlet/data/package.html b/org.restlet/src/main/java/org/restlet/data/package.html deleted file mode 100644 index 1d104a79ca..0000000000 --- a/org.restlet/src/main/java/org/restlet/data/package.html +++ /dev/null @@ -1,12 +0,0 @@ - - - Information exchanged by components. "A datum is an element of information - that is transferred from a component, or received by a component, via a - connector." Roy T. Fielding. -

- @since Restlet 1.0 - @see Source dissertation - @see User Guide - Mapping HTTP headers - @see User Guide - Data package - - diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/package-info.java b/org.restlet/src/main/java/org/restlet/engine/adapter/package-info.java new file mode 100644 index 0000000000..58e1e7c2c1 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/package-info.java @@ -0,0 +1,6 @@ +/** + * Adapters between low-level HTTP calls and high-level Restlet Request and Response objects. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.adapter; diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/package.html b/org.restlet/src/main/java/org/restlet/engine/adapter/package.html deleted file mode 100644 index 60d5c6dfeb..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Adapters between low-level HTTP calls and high-level Restlet Request and -Response objects. -

@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/application/package-info.java b/org.restlet/src/main/java/org/restlet/engine/application/package-info.java new file mode 100644 index 0000000000..2ef77fdd7f --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/application/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports Restlet applications. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.application; diff --git a/org.restlet/src/main/java/org/restlet/engine/application/package.html b/org.restlet/src/main/java/org/restlet/engine/application/package.html deleted file mode 100644 index 089e6e8889..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/application/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports Restlet applications. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/component/package-info.java b/org.restlet/src/main/java/org/restlet/engine/component/package-info.java new file mode 100644 index 0000000000..55f89804e8 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/component/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports Restlet components. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.component; diff --git a/org.restlet/src/main/java/org/restlet/engine/component/package.html b/org.restlet/src/main/java/org/restlet/engine/component/package.html deleted file mode 100644 index e26fa85934..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/component/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports Restlet components. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/package-info.java b/org.restlet/src/main/java/org/restlet/engine/connector/package-info.java new file mode 100644 index 0000000000..4592f7a21d --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/connector/package-info.java @@ -0,0 +1,9 @@ +/** + * Integration with Java URLConnection class. Provides FTP, HTTP, and HTTPS client connectors. + * + * @since Restlet 1.0 + * @see URLConnection + * Javadocs + */ +package org.restlet.engine.connector; diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/package.html b/org.restlet/src/main/java/org/restlet/engine/connector/package.html deleted file mode 100644 index eac5301117..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/connector/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Integration with Java URLConnection class. Provides FTP, HTTP and HTTPS -client connectors. - -@since Restlet 1.0 -@see URLConnection Javadocs - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/package-info.java b/org.restlet/src/main/java/org/restlet/engine/converter/package-info.java new file mode 100644 index 0000000000..57ea819d75 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/converter/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports the converter service. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.converter; diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/package.html b/org.restlet/src/main/java/org/restlet/engine/converter/package.html deleted file mode 100644 index 9464ed1a1c..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/converter/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports the converter service. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/header/package-info.java b/org.restlet/src/main/java/org/restlet/engine/header/package-info.java new file mode 100644 index 0000000000..a540c4e548 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/header/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports HTTP header parsing and formatting. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.header; diff --git a/org.restlet/src/main/java/org/restlet/engine/header/package.html b/org.restlet/src/main/java/org/restlet/engine/header/package.html deleted file mode 100644 index 73ec80f11b..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/header/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports HTTP header parsing and formatting. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/io/package-info.java b/org.restlet/src/main/java/org/restlet/engine/io/package-info.java new file mode 100644 index 0000000000..51175c1c68 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/io/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports input and output work. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.io; diff --git a/org.restlet/src/main/java/org/restlet/engine/io/package.html b/org.restlet/src/main/java/org/restlet/engine/io/package.html deleted file mode 100644 index e5b3a0f7a9..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/io/package.html +++ /dev/null @@ -1,6 +0,0 @@ - - -Supports input and output work. -

@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/local/package-info.java b/org.restlet/src/main/java/org/restlet/engine/local/package-info.java new file mode 100644 index 0000000000..8e20284f25 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/local/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports local connectors and resources. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.local; diff --git a/org.restlet/src/main/java/org/restlet/engine/local/package.html b/org.restlet/src/main/java/org/restlet/engine/local/package.html deleted file mode 100644 index 00ba49b8e2..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/local/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports local connectors and resources. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/log/package-info.java b/org.restlet/src/main/java/org/restlet/engine/log/package-info.java new file mode 100644 index 0000000000..937aedae56 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/log/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports the log service. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.log; diff --git a/org.restlet/src/main/java/org/restlet/engine/log/package.html b/org.restlet/src/main/java/org/restlet/engine/log/package.html deleted file mode 100644 index ff984d0e51..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/log/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports the log service. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/package-info.java b/org.restlet/src/main/java/org/restlet/engine/package-info.java new file mode 100644 index 0000000000..94283c16c6 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/package-info.java @@ -0,0 +1,8 @@ +/** + * Implementation of Restlet API. + * + * @since Restlet 2.0 + * @see User Guide - + * Engine + */ +package org.restlet.engine; diff --git a/org.restlet/src/main/java/org/restlet/engine/package.html b/org.restlet/src/main/java/org/restlet/engine/package.html deleted file mode 100644 index 9159bece91..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Implementation of Restlet API. -

- @since Restlet 2.0 - @see User Guide - Engine - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/package-info.java b/org.restlet/src/main/java/org/restlet/engine/resource/package-info.java new file mode 100644 index 0000000000..2c2ba780ce --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/resource/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports resources. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.resource; diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/package.html b/org.restlet/src/main/java/org/restlet/engine/resource/package.html deleted file mode 100644 index e2886f941c..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/resource/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports resources. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/security/package-info.java b/org.restlet/src/main/java/org/restlet/engine/security/package-info.java new file mode 100644 index 0000000000..20ea8df573 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/security/package-info.java @@ -0,0 +1,6 @@ +/** + * Supports security. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.security; diff --git a/org.restlet/src/main/java/org/restlet/engine/security/package.html b/org.restlet/src/main/java/org/restlet/engine/security/package.html deleted file mode 100644 index edf452d0f3..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/security/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Supports security. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/package-info.java b/org.restlet/src/main/java/org/restlet/engine/ssl/package-info.java new file mode 100644 index 0000000000..dc684c1024 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/package-info.java @@ -0,0 +1,6 @@ +/** + * Support SSL and TLS. + * + * @since Restlet 2.2 + */ +package org.restlet.engine.ssl; diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/package.html b/org.restlet/src/main/java/org/restlet/engine/ssl/package.html deleted file mode 100644 index 44799a23d1..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -Support SSL and TLS. -

-@since Restlet 2.2 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/engine/util/package-info.java b/org.restlet/src/main/java/org/restlet/engine/util/package-info.java new file mode 100644 index 0000000000..21cef4a769 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/engine/util/package-info.java @@ -0,0 +1,6 @@ +/** + * General utilities. + * + * @since Restlet 2.0 + */ +package org.restlet.engine.util; diff --git a/org.restlet/src/main/java/org/restlet/engine/util/package.html b/org.restlet/src/main/java/org/restlet/engine/util/package.html deleted file mode 100644 index bd7c30a7ff..0000000000 --- a/org.restlet/src/main/java/org/restlet/engine/util/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -General utilities. -

-@since Restlet 2.0 - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/package-info.java b/org.restlet/src/main/java/org/restlet/package-info.java new file mode 100644 index 0000000000..8e57d8f2e1 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/package-info.java @@ -0,0 +1,12 @@ +/** + * Core classes of the Restlet API. + * + * @since Restlet 1.0 + * @see REST dissertation by Roy + * T. Fielding + * @see User Guide - + * Restlet API + * @see User Guide - + * Base package + */ +package org.restlet; diff --git a/org.restlet/src/main/java/org/restlet/package.html b/org.restlet/src/main/java/org/restlet/package.html deleted file mode 100644 index 0f77683520..0000000000 --- a/org.restlet/src/main/java/org/restlet/package.html +++ /dev/null @@ -1,10 +0,0 @@ - - - Core classes of the Restlet API. -

- @since Restlet 1.0 - @see REST dissertation by Roy T. Fielding - @see User Guide - Restlet API - @see User Guide - Base package - - diff --git a/org.restlet/src/main/java/org/restlet/representation/package-info.java b/org.restlet/src/main/java/org/restlet/representation/package-info.java new file mode 100644 index 0000000000..4252937bcf --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/representation/package-info.java @@ -0,0 +1,8 @@ +/** + * Common representation data elements. + * + * @since Restlet 2.0 + * @see User + * Guide - Representation package + */ +package org.restlet.representation; diff --git a/org.restlet/src/main/java/org/restlet/representation/package.html b/org.restlet/src/main/java/org/restlet/representation/package.html deleted file mode 100644 index 390bb26997..0000000000 --- a/org.restlet/src/main/java/org/restlet/representation/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Common representation data elements. -

- @since Restlet 2.0 - @see User Guide - Representation package - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/resource/package-info.java b/org.restlet/src/main/java/org/restlet/resource/package-info.java new file mode 100644 index 0000000000..99cdbeda80 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/resource/package-info.java @@ -0,0 +1,9 @@ +/** + * Client and server resource classes. + * + * @since Restlet 1.0 + * @see User + * Guide - Resource package + */ +package org.restlet.resource; diff --git a/org.restlet/src/main/java/org/restlet/resource/package.html b/org.restlet/src/main/java/org/restlet/resource/package.html deleted file mode 100644 index 226f879878..0000000000 --- a/org.restlet/src/main/java/org/restlet/resource/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Client and server resource classes. -

- @since Restlet 1.0 - @see User Guide - Resource package - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/routing/package-info.java b/org.restlet/src/main/java/org/restlet/routing/package-info.java new file mode 100644 index 0000000000..c891425a8f --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/routing/package-info.java @@ -0,0 +1,8 @@ +/** + * Classes related to call routing. + * + * @since Restlet 2.0 + * @see User + * Guide - Routing package + */ +package org.restlet.routing; diff --git a/org.restlet/src/main/java/org/restlet/routing/package.html b/org.restlet/src/main/java/org/restlet/routing/package.html deleted file mode 100644 index 825cbce9e2..0000000000 --- a/org.restlet/src/main/java/org/restlet/routing/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Classes related to call routing. -

- @since Restlet 2.0 - @see User Guide - Routing package - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/security/package-info.java b/org.restlet/src/main/java/org/restlet/security/package-info.java new file mode 100644 index 0000000000..e0676fd58e --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/security/package-info.java @@ -0,0 +1,9 @@ +/** + * Classes related to security. + * + * @since Restlet 2.0 + * @see User + * Guide - Security package + */ +package org.restlet.security; diff --git a/org.restlet/src/main/java/org/restlet/security/package.html b/org.restlet/src/main/java/org/restlet/security/package.html deleted file mode 100644 index 446c81c83e..0000000000 --- a/org.restlet/src/main/java/org/restlet/security/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Classes related to security. -

- @since Restlet 2.0 - @see User Guide - Security package - - \ No newline at end of file diff --git a/org.restlet/src/main/java/org/restlet/service/package-info.java b/org.restlet/src/main/java/org/restlet/service/package-info.java new file mode 100644 index 0000000000..c61859f763 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/service/package-info.java @@ -0,0 +1,9 @@ +/** + * Services used by applications and components. + * + * @since Restlet 1.0 + * @see User + * Guide - Service package + */ +package org.restlet.service; diff --git a/org.restlet/src/main/java/org/restlet/service/package.html b/org.restlet/src/main/java/org/restlet/service/package.html deleted file mode 100644 index 5c2be8f415..0000000000 --- a/org.restlet/src/main/java/org/restlet/service/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Services used by applications and components. -

- @since Restlet 1.0 - @see User Guide - Service package - - diff --git a/org.restlet/src/main/java/org/restlet/util/package-info.java b/org.restlet/src/main/java/org/restlet/util/package-info.java new file mode 100644 index 0000000000..b35cb27be8 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/util/package-info.java @@ -0,0 +1,6 @@ +/** + * Various utility classes. + * + * @since Restlet 1.0 + */ +package org.restlet.util; diff --git a/org.restlet/src/main/java/org/restlet/util/package.html b/org.restlet/src/main/java/org/restlet/util/package.html deleted file mode 100644 index b8e7177896..0000000000 --- a/org.restlet/src/main/java/org/restlet/util/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - - Various utility classes. -

- @since Restlet 1.0 - - diff --git a/org.restlet/src/test/java/org/restlet/security/package-info.java b/org.restlet/src/test/java/org/restlet/security/package-info.java new file mode 100644 index 0000000000..6b9b889420 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/security/package-info.java @@ -0,0 +1,6 @@ +/** + * JUnit tests. + * + * @since 1.0 + */ +package org.restlet.security; diff --git a/org.restlet/src/test/java/org/restlet/security/package.html b/org.restlet/src/test/java/org/restlet/security/package.html deleted file mode 100644 index 04a9abdb4e..0000000000 --- a/org.restlet/src/test/java/org/restlet/security/package.html +++ /dev/null @@ -1,6 +0,0 @@ - - -JUnit tests. -@since 1.0 - - \ No newline at end of file From d97460ad1c9e812f716360076fddbb81a6073d31 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 15:49:32 +0200 Subject: [PATCH 12/30] Take into account Sonar comments --- .../main/java/org/restlet/engine/Engine.java | 67 +++++++++---------- .../io/UnclosableOutputStreamTestCase.java | 10 +-- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/engine/Engine.java b/org.restlet/src/main/java/org/restlet/engine/Engine.java index 2bd9d6c9d9..ab5c782b91 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Engine.java +++ b/org.restlet/src/main/java/org/restlet/engine/Engine.java @@ -26,9 +26,11 @@ import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; +import org.restlet.Server; import org.restlet.data.ChallengeScheme; import org.restlet.data.Method; import org.restlet.data.Protocol; +import org.restlet.engine.connector.ConnectorHelper; import org.restlet.engine.io.IoUtils; import org.restlet.engine.log.LoggerFacade; @@ -121,23 +123,18 @@ public static Thread createThreadWithLocalVariables(final Runnable runnable, Str final Integer currentVirtualHost = org.restlet.routing.VirtualHost.getCurrent(); final Response currentResponse = Response.getCurrent(); - Runnable r = - new Runnable() { + Runnable r = () -> { + // Copy the thread local variables + Response.setCurrent(currentResponse); + Context.setCurrent(currentContext); + org.restlet.routing.VirtualHost.setCurrent(currentVirtualHost); + org.restlet.Application.setCurrent(currentApplication); - @Override - public void run() { - // Copy the thread local variables - Response.setCurrent(currentResponse); - Context.setCurrent(currentContext); - org.restlet.routing.VirtualHost.setCurrent(currentVirtualHost); - org.restlet.Application.setCurrent(currentApplication); - - try { - // Run the user task - runnable.run(); - } finally { - Engine.clearThreadLocalVariables(); - } + try { + // Run the user task + runnable.run(); + } finally { + Engine.clearThreadLocalVariables(); } }; @@ -385,7 +382,7 @@ public static void setRestletLogLevel(Level restletLogLevel) { private final List registeredProtocols; /** List of available server connectors. */ - private final List> + private final List> registeredServers; /** User class loader to use for dynamic class loading. */ @@ -529,26 +526,22 @@ public org.restlet.engine.connector.ConnectorHelper createHelper( * @return The new helper. */ @SuppressWarnings("unchecked") - public org.restlet.engine.connector.ConnectorHelper createHelper( - org.restlet.Server server, String helperClass) { - org.restlet.engine.connector.ConnectorHelper result = null; + public ConnectorHelper createHelper(Server server, String helperClass) { + ConnectorHelper result = null; if (!server.getProtocols().isEmpty()) { - org.restlet.engine.connector.ConnectorHelper connector = null; - for (final Iterator> - iter = getRegisteredServers().iterator(); - (result == null) && iter.hasNext(); ) { + ConnectorHelper connector = null; + for (final Iterator> iter = getRegisteredServers().iterator(); (result == null) && iter.hasNext(); ) { connector = iter.next(); - if ((helperClass == null) - || connector.getClass().getCanonicalName().equals(helperClass)) { - if (new HashSet<>(connector.getProtocols()) - .containsAll(server.getProtocols())) { + if (((helperClass == null) + || connector.getClass().getCanonicalName().equals(helperClass)) + && new HashSet<>(connector.getProtocols()).containsAll(server.getProtocols())) { try { result = connector .getClass() - .getConstructor(org.restlet.Server.class) + .getConstructor(Server.class) .newInstance(server); } catch (Exception e) { Context.getCurrentLogger() @@ -558,7 +551,7 @@ public org.restlet.engine.connector.ConnectorHelper createHe e); } } - } + } if (result == null) { @@ -600,7 +593,7 @@ private void discoverAuthenticators() throws IOException { */ private void discoverConnectors() throws IOException { registerHelpers(DESCRIPTOR_CLIENT_PATH, getRegisteredClients(), Client.class); - registerHelpers(DESCRIPTOR_SERVER_PATH, getRegisteredServers(), org.restlet.Server.class); + registerHelpers(DESCRIPTOR_SERVER_PATH, getRegisteredServers(), Server.class); registerDefaultConnectors(); } @@ -739,7 +732,7 @@ public List getRegisteredProtocols( * * @return The list of available server connectors. */ - public List> + public List> getRegisteredServers() { return this.registeredServers; } @@ -805,9 +798,9 @@ public void registerHelper( .getConstructor(constructorClass) .newInstance(constructorClass.cast(null))); } - } catch (Throwable t) { + } catch (Exception exception) { Context.getCurrentLogger() - .log(Level.INFO, "Unable to register the helper " + provider, t); + .log(Level.INFO, "Unable to register the helper " + provider, exception); } } } @@ -883,7 +876,9 @@ protected java.net.URLConnection openConnection(java.net.URL url) return new java.net.URLConnection(url) { @Override - public void connect() throws IOException {} + public void connect() { + // no connection to open + } @Override public InputStream getInputStream() throws IOException { @@ -1010,7 +1005,7 @@ public void setRegisteredProtocols( * @param registeredServers The list of available server helpers. */ public void setRegisteredServers( - List> + List> registeredServers) { synchronized (this.registeredServers) { if (registeredServers != this.registeredServers) { diff --git a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java index da1f94c6c9..eb7cb078e9 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java @@ -22,7 +22,7 @@ * * @author Kevin Conaway */ -public class UnclosableOutputStreamTestCase { +class UnclosableOutputStreamTestCase { static class MockOutputStream extends OutputStream { boolean closed = false; @@ -33,11 +33,13 @@ public void close() { } @Override - public void write(int b) {} + public void write(int b) { + // Do nothing, only closing is relevant for this test + } } @Test - public void testClose() throws IOException { + void testClose() throws IOException { final MockOutputStream stream = new MockOutputStream(); final OutputStream out = new UnclosableOutputStream(stream); out.close(); @@ -48,7 +50,7 @@ public void testClose() throws IOException { } @Test - public void testWrite() throws IOException { + void testWrite() throws IOException { final ByteArrayOutputStream stream = new ByteArrayOutputStream(); final OutputStream out = new UnclosableOutputStream(stream); From 9385d4b613584fe9ff9c2ed1009a0a03de005f39 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 15:53:32 +0200 Subject: [PATCH 13/30] Take into account Sonar comments --- .../main/java/org/restlet/util/Series.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/util/Series.java b/org.restlet/src/main/java/org/restlet/util/Series.java index 847283ad93..54f74a14d5 100644 --- a/org.restlet/src/main/java/org/restlet/util/Series.java +++ b/org.restlet/src/main/java/org/restlet/util/Series.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.util; @@ -16,6 +16,7 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; + import org.restlet.Context; /** @@ -114,7 +115,7 @@ public void copyTo(Map params) { } else { // Second value found for this entry // Create a list of values - values = new ArrayList(); + values = new ArrayList<>(); values.add(currentValue); params.put(param.getName(), values); } @@ -162,19 +163,15 @@ public T createEntry(String name, String value) { * @return True if both values are equal. */ private boolean equals(String value1, String value2, boolean ignoreCase) { - boolean result = (value1 == value2); - - if (!result) { - if ((value1 != null) && (value2 != null)) { - if (ignoreCase) { - result = value1.equalsIgnoreCase(value2); - } else { - result = value1.equals(value2); - } + if ((value1 != null) && (value2 != null)) { + if (ignoreCase) { + return value1.equalsIgnoreCase(value2); + } else { + return value1.equals(value2); } } - return result; + return false; } /** From 4ac83c69b300e9a932cae5126c4f73828e405ef9 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 19:43:59 +0200 Subject: [PATCH 14/30] Take into account Sonar comments --- .../main/java/org/restlet/engine/Engine.java | 48 +++--- .../restlet/engine/adapter/ServerCall.java | 7 +- .../application/CorsResponseHelper.java | 2 +- .../application/DecodeRepresentation.java | 10 +- .../engine/application/RangeFilter.java | 113 +++++++------- .../engine/application/StrictConneg.java | 21 +-- .../component/ComponentClientDispatcher.java | 142 ++++++++++-------- .../engine/connector/HttpClientHelper.java | 13 +- .../main/java/org/restlet/util/Series.java | 5 +- 9 files changed, 193 insertions(+), 168 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/engine/Engine.java b/org.restlet/src/main/java/org/restlet/engine/Engine.java index ab5c782b91..b77d351bb7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Engine.java +++ b/org.restlet/src/main/java/org/restlet/engine/Engine.java @@ -123,7 +123,8 @@ public static Thread createThreadWithLocalVariables(final Runnable runnable, Str final Integer currentVirtualHost = org.restlet.routing.VirtualHost.getCurrent(); final Response currentResponse = Response.getCurrent(); - Runnable r = () -> { + Runnable r = + () -> { // Copy the thread local variables Response.setCurrent(currentResponse); Context.setCurrent(currentContext); @@ -382,8 +383,7 @@ public static void setRestletLogLevel(Level restletLogLevel) { private final List registeredProtocols; /** List of available server connectors. */ - private final List> - registeredServers; + private final List> registeredServers; /** User class loader to use for dynamic class loading. */ private volatile ClassLoader userClassLoader; @@ -531,27 +531,28 @@ public ConnectorHelper createHelper(Server server, String helperClass) { if (!server.getProtocols().isEmpty()) { ConnectorHelper connector = null; - for (final Iterator> iter = getRegisteredServers().iterator(); (result == null) && iter.hasNext(); ) { + for (final Iterator> iter = getRegisteredServers().iterator(); + (result == null) && iter.hasNext(); ) { connector = iter.next(); if (((helperClass == null) - || connector.getClass().getCanonicalName().equals(helperClass)) - && new HashSet<>(connector.getProtocols()).containsAll(server.getProtocols())) { - try { - result = - connector - .getClass() - .getConstructor(Server.class) - .newInstance(server); - } catch (Exception e) { - Context.getCurrentLogger() - .log( - Level.SEVERE, - "Exception while instantiation the server connector.", - e); - } + || connector.getClass().getCanonicalName().equals(helperClass)) + && new HashSet<>(connector.getProtocols()) + .containsAll(server.getProtocols())) { + try { + result = + connector + .getClass() + .getConstructor(Server.class) + .newInstance(server); + } catch (Exception e) { + Context.getCurrentLogger() + .log( + Level.SEVERE, + "Exception while instantiation the server connector.", + e); } - + } } if (result == null) { @@ -732,8 +733,7 @@ public List getRegisteredProtocols( * * @return The list of available server connectors. */ - public List> - getRegisteredServers() { + public List> getRegisteredServers() { return this.registeredServers; } @@ -1004,9 +1004,7 @@ public void setRegisteredProtocols( * * @param registeredServers The list of available server helpers. */ - public void setRegisteredServers( - List> - registeredServers) { + public void setRegisteredServers(List> registeredServers) { synchronized (this.registeredServers) { if (registeredServers != this.registeredServers) { this.registeredServers.clear(); diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java index 4cf634ffda..04d8ec5a83 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java @@ -223,9 +223,10 @@ public Representation getRequestEntity() { Context.getCurrentLogger() .log( Level.WARNING, - "Error during Content-Disposition header parsing. Header: " - + header.getValue(), - ioe); + ioe, + () -> + "Error during Content-Disposition header parsing. Header: " + + header.getValue()); } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java index 3c47499313..5b6c98ae20 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java @@ -79,7 +79,7 @@ public void addCorsResponseHeaders(Request request, Response response) { if (!allowedOrigins.contains("*") && !allowedOrigins.contains(origin)) { // Origin isn't allowed - LOGGER.fine("Origin " + origin + " not allowed for CORS request"); + LOGGER.fine(() -> "Origin " + origin + " not allowed for CORS request"); return; } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java index 34a5ee4117..8340e65443 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java @@ -14,6 +14,7 @@ import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -61,10 +62,17 @@ public static List getSupportedEncodings() { */ public DecodeRepresentation(Representation wrappedRepresentation) { super(wrappedRepresentation); - this.decoding = getSupportedEncodings().containsAll(wrappedRepresentation.getEncodings()); + this.decoding = + new HashSet<>(getSupportedEncodings()) + .containsAll(wrappedRepresentation.getEncodings()); this.wrappedEncodings = new CopyOnWriteArrayList<>(wrappedRepresentation.getEncodings()); } + @Override + public boolean equals(final Object obj) { + return super.equals(obj); + } + @Override public long getAvailableSize() { return IoUtils.getAvailableSize(this); diff --git a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java index 01d313d923..38b39c73f0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java @@ -36,67 +36,61 @@ public RangeFilter(Context context) { @Override protected void afterHandle(Request request, Response response) { - if (getRangeService().isEnabled()) { - response.getServerInfo().setAcceptingRanges(true); + response.getServerInfo().setAcceptingRanges(getRangeService().isEnabled()); - if (request.getMethod().isSafe() && response.isEntityAvailable()) { - Range responseRange = response.getEntity().getRange(); - boolean rangedEntity = responseRange != null && isBytesRange(responseRange); + if (!getRangeService().isEnabled()) { + return; + } + if (!request.getMethod().isSafe() || !response.isEntityAvailable()) { + return; + } + if (!response.getStatus().isSuccess()) { + return; + } - if (response.getStatus().isSuccess()) { - if (Status.SUCCESS_PARTIAL_CONTENT.equals(response.getStatus())) { - if (!rangedEntity) { - getLogger() - .warning( - "When returning a \"206 Partial content\" status, your response entity must be properly ranged."); - } else { - // We assume that the response entity has been properly ranged. - } - } else if (request.getRanges().size() - > 1) { // At this time, lists of ranges are not supported. - // Return a server error as this feature isn't supported yet - response.setStatus(Status.SERVER_ERROR_NOT_IMPLEMENTED); - getLogger().warning("Multiple ranges are not supported at this time."); - response.setEntity(null); - } else if (request.getRanges().size() == 1 - && (!request.getConditions().hasSomeRange() - || request.getConditions() - .getRangeStatus(response.getEntity()) - .isSuccess())) { - Range requestedRange = request.getRanges().getFirst(); + Range responseRange = response.getEntity().getRange(); + boolean rangedEntity = responseRange != null && isBytesRange(responseRange); - if ((!response.getEntity().hasKnownSize()) - && ((requestedRange.getIndex() == Range.INDEX_LAST - || requestedRange.getSize() == Range.SIZE_MAX) - && !(requestedRange.getIndex() == Range.INDEX_LAST - && requestedRange.getSize() == Range.SIZE_MAX))) { - // The end index cannot be properly computed - response.setStatus(Status.SERVER_ERROR_INTERNAL); - getLogger() - .warning( - "Unable to serve this range since at least the end index of the range cannot be computed."); - response.setEntity(null); - } else if (!requestedRange.equals(responseRange)) { - if (rangedEntity) { - getLogger() - .info( - "The range of the response entity is not equal to the requested one."); - } + if (Status.SUCCESS_PARTIAL_CONTENT.equals(response.getStatus())) { + if (!rangedEntity) { + getLogger() + .warning( + "When returning a \"206 Partial content\" status, your response entity must be properly ranged."); + } + } else if (request.getRanges().size() > 1) { + multipleRangesNotSupported(response); + } else if (request.getRanges().size() == 1 + && (!request.getConditions().hasSomeRange() + || request.getConditions() + .getRangeStatus(response.getEntity()) + .isSuccess())) { + Range requestedRange = request.getRanges().getFirst(); - if (response.getEntity().hasKnownSize() - && requestedRange.getSize() - > response.getEntity().getAvailableSize()) { - requestedRange.setSize(Range.SIZE_MAX); - } + if ((!response.getEntity().hasKnownSize()) + && ((requestedRange.getIndex() == Range.INDEX_LAST + || requestedRange.getSize() == Range.SIZE_MAX) + && !(requestedRange.getIndex() == Range.INDEX_LAST + && requestedRange.getSize() == Range.SIZE_MAX))) { + // The end index cannot be properly computed + response.setStatus(Status.SERVER_ERROR_INTERNAL); + getLogger() + .warning( + "Unable to serve this range since at least the end index of the range cannot be computed."); + response.setEntity(null); + } else if (!requestedRange.equals(responseRange)) { + if (rangedEntity) { + getLogger() + .info( + "The range of the response entity is not equal to the requested one."); + } - response.setEntity( - new RangeRepresentation(response.getEntity(), requestedRange)); - response.setStatus(Status.SUCCESS_PARTIAL_CONTENT); - } - } - } else { - // Ignore error responses + if (response.getEntity().hasKnownSize() + && requestedRange.getSize() > response.getEntity().getAvailableSize()) { + requestedRange.setSize(Range.SIZE_MAX); } + + response.setEntity(new RangeRepresentation(response.getEntity(), requestedRange)); + response.setStatus(Status.SUCCESS_PARTIAL_CONTENT); } } } @@ -109,4 +103,13 @@ protected void afterHandle(Request request, Response response) { public RangeService getRangeService() { return getApplication().getRangeService(); } + + private void multipleRangesNotSupported(final Response response) { + // At this time, lists of ranges are not supported. + // Return a server error as this feature isn't supported yet + getLogger().warning("Multiple ranges are not supported at this time."); + response.setStatus(Status.SERVER_ERROR_NOT_IMPLEMENTED); + response.setEntity(null); + } + } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java index 3d18f60e76..f2a7bca842 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.engine.application; @@ -12,6 +12,7 @@ import java.util.List; import java.util.Set; import java.util.logging.Level; + import org.restlet.Context; import org.restlet.Request; import org.restlet.data.CharacterSet; @@ -139,7 +140,7 @@ private float doScoreAnnotation(MethodAnnotationInfo annotation) { if (constraintParam.getName().equals(actualParam.getName()) && ((constraintParam.getValue() == null) - || constraintParam.getValue().equals(actualParam.getValue()))) { + || constraintParam.getValue().equals(actualParam.getValue()))) { // Actual match found! constraintMatched = true; matchedParams.add(actualParam); @@ -310,10 +311,10 @@ public float scoreVariant(Variant variant) { // Return the weighted average score result = ((languageScore * 4.0F) - + (mediaTypeScore * 3.0F) - + (characterSetScore * 2.0F) - + (encodingScore * 1.0F) - + (annotationScore * 2.0F)) + + (mediaTypeScore * 3.0F) + + (characterSetScore * 2.0F) + + (encodingScore * 1.0F) + + (annotationScore * 2.0F)) / 12.0F; // Take into account the affinity with the input // entity @@ -322,9 +323,9 @@ public float scoreVariant(Variant variant) { // Return the weighted average score result = ((languageScore * 4.0F) - + (mediaTypeScore * 3.0F) - + (characterSetScore * 2.0F) - + (encodingScore * 1.0F)) + + (mediaTypeScore * 3.0F) + + (characterSetScore * 2.0F) + + (encodingScore * 1.0F)) / 10.0F; } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java index 43e840e676..c0d66566a7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java @@ -42,68 +42,9 @@ public ComponentClientDispatcher(ComponentContext componentContext) { @Override protected int doHandle(Request request, Response response) { int result = CONTINUE; - Protocol protocol = request.getProtocol(); - - if (protocol.equals(Protocol.RIAP)) { - // Let's dispatch it - LocalReference cr = new LocalReference(request.getResourceRef()); - Component component = getComponent(); - - if (component != null) { - if (cr.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { - // This causes the baseRef of the resource reference to be - // set as if it had actually arrived from a server - // connector. - request.getResourceRef() - .setBaseRef(request.getResourceRef().getHostIdentifier()); - - // Ask the private internal route to handle the call - component.getInternalRouter().handle(request, response); - } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_HOST) { - VirtualHost host = null; - VirtualHost currentHost = null; - final Integer hostHashCode = VirtualHost.getCurrent(); - - // Lookup the virtual host - for (final Iterator hostIter = - getComponent().getHosts().iterator(); - (host == null) && hostIter.hasNext(); ) { - currentHost = hostIter.next(); - - if (currentHost.hashCode() == hostHashCode) { - host = currentHost; - } - } - - if ((host == null) - && (component.getDefaultHost() != null) - && component.getDefaultHost().hashCode() == hostHashCode) { - host = component.getDefaultHost(); - } - - if (host != null) { - // This causes the baseRef of the resource reference to - // be set as if it had actually arrived from a server - // connector. - request.getResourceRef() - .setBaseRef(request.getResourceRef().getHostIdentifier()); - - // Ask the virtual host to handle the call - host.handle(request, response); - } else { - getLogger() - .warning( - "No virtual host is available to route the RIAP Host request."); - result = STOP; - } - } else { - getLogger().warning("Unknown RIAP authority. Only \"component\" is supported."); - result = STOP; - } - } else { - getLogger().warning("No component is available to route the RIAP request."); - result = STOP; - } + + if (Protocol.RIAP.equals(request.getProtocol())) { + result = doHandleRiapRequest(request, response); } else { getComponentContext().getComponentHelper().getClientRouter().handle(request, response); } @@ -111,6 +52,83 @@ protected int doHandle(Request request, Response response) { return result; } + private int doHandleRiapRequest(final Request request, final Response response) { + Component component = getComponent(); + if (component == null) { + getLogger().warning("No component is available to route the RIAP request."); + return STOP; + } + + // Let's dispatch it + final int result; + + LocalReference cr = new LocalReference(request.getResourceRef()); + if (cr.getRiapAuthorityType() == LocalReference.RIAP_COMPONENT) { + result = doHandleRiapComponentRequest(component, request, response); + } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_HOST) { + result = doHandleRiapHostRequest(component, request, response); + } else { + getLogger().warning("Unknown RIAP authority. Only \"component\" and \"host\" are supported: " + cr.getRiapAuthorityType()); + result = STOP; + } + return result; + } + + private int doHandleRiapHostRequest(final Component component, final Request request, final Response response) { + int result = CONTINUE; + + VirtualHost host = null; + VirtualHost currentHost; + + final Integer hostHashCode = VirtualHost.getCurrent(); + + // Look up the virtual host + for (final Iterator hostIter = component.getHosts().iterator(); + (host == null) && hostIter.hasNext(); ) { + currentHost = hostIter.next(); + + if (currentHost.hashCode() == hostHashCode) { + host = currentHost; + } + } + + if ((host == null) + && (component.getDefaultHost() != null) + && component.getDefaultHost().hashCode() == hostHashCode) { + host = component.getDefaultHost(); + } + + if (host != null) { + // This causes the baseRef of the resource reference to + // be set as if it had actually arrived from a server + // connector. + request.getResourceRef() + .setBaseRef(request.getResourceRef().getHostIdentifier()); + + // Ask the virtual host to handle the call + host.handle(request, response); + } else { + getLogger() + .warning( + "No virtual host is available to route the RIAP Host request."); + result = STOP; + } + return result; + } + + private static int doHandleRiapComponentRequest(final Component component, final Request request, final Response response) { + // This causes the baseRef of the resource reference to be + // set as if it had actually arrived from a server + // connector. + request.getResourceRef() + .setBaseRef(request.getResourceRef().getHostIdentifier()); + + // Ask the private internal route to handle the call + component.getInternalRouter().handle(request, response); + + return CONTINUE; + } + /** * Returns the parent component. * diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java index a70252485e..9162920a15 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java @@ -278,7 +278,7 @@ protected HttpClient createHttpClient() { HttpTransportProtocol.fromName(getHttpClientTransportMode()); final HttpClientTransport httpTransport = switch (httpTransportProtocol) { - case HTTP1_1 -> getHttpTransportForHttp1_1(); + case HTTP1_1 -> getHttpTransportForHttp11(); case HTTP2 -> getHttpClientTransportForHttp2(); case HTTP3 -> getHttpClientTransportForHttp3(sslContextFactory); case DYNAMIC -> getHttpClientTransportForDynamicMode(sslContextFactory); @@ -341,7 +341,7 @@ protected HttpClient createHttpClient() { return result; } - private static HttpClientTransportOverHTTP getHttpTransportForHttp1_1() { + private static HttpClientTransportOverHTTP getHttpTransportForHttp11() { return new HttpClientTransportOverHTTP(); } @@ -650,8 +650,7 @@ public String getUserAgentField() { * @return True if the connect operation is blocking. */ public boolean isConnectBlocking() { - return Boolean.parseBoolean( - getHelpedParameters().getFirstValue("connectBlocking", "false")); + return Boolean.parseBoolean(getHelpedParameters().getFirstValue("connectBlocking")); } /** @@ -660,8 +659,7 @@ public boolean isConnectBlocking() { * @return Whether to support cookies. */ public boolean isCookieSupported() { - return Boolean.parseBoolean( - getHelpedParameters().getFirstValue("cookieSupported", "false")); + return Boolean.parseBoolean(getHelpedParameters().getFirstValue("cookieSupported")); } /** @@ -696,8 +694,7 @@ public boolean isFollowRedirects() { * @return Whether request events must be strictly ordered. */ public boolean isStrictEventOrdering() { - return Boolean.parseBoolean( - getHelpedParameters().getFirstValue("strictEventOrdering", "false")); + return Boolean.parseBoolean(getHelpedParameters().getFirstValue("strictEventOrdering")); } @Override diff --git a/org.restlet/src/main/java/org/restlet/util/Series.java b/org.restlet/src/main/java/org/restlet/util/Series.java index 54f74a14d5..7810d4dd79 100644 --- a/org.restlet/src/main/java/org/restlet/util/Series.java +++ b/org.restlet/src/main/java/org/restlet/util/Series.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.util; @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; - import org.restlet.Context; /** From 7721dcd8a72b4b0a5c380c03f329844370711077 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 20:20:55 +0200 Subject: [PATCH 15/30] Take into account Sonar comments --- .../engine/application/RangeFilter.java | 1 - .../engine/application/StrictConneg.java | 21 ++- .../component/ComponentClientDispatcher.java | 23 +-- .../restlet/engine/component/HostRoute.java | 142 +++++++++--------- 4 files changed, 93 insertions(+), 94 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java index 38b39c73f0..ed0dcb6058 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java @@ -111,5 +111,4 @@ private void multipleRangesNotSupported(final Response response) { response.setStatus(Status.SERVER_ERROR_NOT_IMPLEMENTED); response.setEntity(null); } - } diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java index f2a7bca842..3d18f60e76 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2024 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.engine.application; @@ -12,7 +12,6 @@ import java.util.List; import java.util.Set; import java.util.logging.Level; - import org.restlet.Context; import org.restlet.Request; import org.restlet.data.CharacterSet; @@ -140,7 +139,7 @@ private float doScoreAnnotation(MethodAnnotationInfo annotation) { if (constraintParam.getName().equals(actualParam.getName()) && ((constraintParam.getValue() == null) - || constraintParam.getValue().equals(actualParam.getValue()))) { + || constraintParam.getValue().equals(actualParam.getValue()))) { // Actual match found! constraintMatched = true; matchedParams.add(actualParam); @@ -311,10 +310,10 @@ public float scoreVariant(Variant variant) { // Return the weighted average score result = ((languageScore * 4.0F) - + (mediaTypeScore * 3.0F) - + (characterSetScore * 2.0F) - + (encodingScore * 1.0F) - + (annotationScore * 2.0F)) + + (mediaTypeScore * 3.0F) + + (characterSetScore * 2.0F) + + (encodingScore * 1.0F) + + (annotationScore * 2.0F)) / 12.0F; // Take into account the affinity with the input // entity @@ -323,9 +322,9 @@ public float scoreVariant(Variant variant) { // Return the weighted average score result = ((languageScore * 4.0F) - + (mediaTypeScore * 3.0F) - + (characterSetScore * 2.0F) - + (encodingScore * 1.0F)) + + (mediaTypeScore * 3.0F) + + (characterSetScore * 2.0F) + + (encodingScore * 1.0F)) / 10.0F; } } diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java index c0d66566a7..5c02cebf23 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java @@ -68,13 +68,17 @@ private int doHandleRiapRequest(final Request request, final Response response) } else if (cr.getRiapAuthorityType() == LocalReference.RIAP_HOST) { result = doHandleRiapHostRequest(component, request, response); } else { - getLogger().warning("Unknown RIAP authority. Only \"component\" and \"host\" are supported: " + cr.getRiapAuthorityType()); + getLogger() + .warning( + "Unknown RIAP authority. Only \"component\" and \"host\" are supported: " + + cr.getRiapAuthorityType()); result = STOP; } return result; } - private int doHandleRiapHostRequest(final Component component, final Request request, final Response response) { + private int doHandleRiapHostRequest( + final Component component, final Request request, final Response response) { int result = CONTINUE; VirtualHost host = null; @@ -84,7 +88,7 @@ private int doHandleRiapHostRequest(final Component component, final Request req // Look up the virtual host for (final Iterator hostIter = component.getHosts().iterator(); - (host == null) && hostIter.hasNext(); ) { + (host == null) && hostIter.hasNext(); ) { currentHost = hostIter.next(); if (currentHost.hashCode() == hostHashCode) { @@ -102,26 +106,23 @@ private int doHandleRiapHostRequest(final Component component, final Request req // This causes the baseRef of the resource reference to // be set as if it had actually arrived from a server // connector. - request.getResourceRef() - .setBaseRef(request.getResourceRef().getHostIdentifier()); + request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); // Ask the virtual host to handle the call host.handle(request, response); } else { - getLogger() - .warning( - "No virtual host is available to route the RIAP Host request."); + getLogger().warning("No virtual host is available to route the RIAP Host request."); result = STOP; } return result; } - private static int doHandleRiapComponentRequest(final Component component, final Request request, final Response response) { + private static int doHandleRiapComponentRequest( + final Component component, final Request request, final Response response) { // This causes the baseRef of the resource reference to be // set as if it had actually arrived from a server // connector. - request.getResourceRef() - .setBaseRef(request.getResourceRef().getHostIdentifier()); + request.getResourceRef().setBaseRef(request.getResourceRef().getHostIdentifier()); // Ask the private internal route to handle the call component.getInternalRouter().handle(request, response); diff --git a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java index a1c9708665..5c2be68f8a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java @@ -12,6 +12,7 @@ import java.util.regex.Pattern; import org.restlet.Request; import org.restlet.Response; +import org.restlet.data.Reference; import org.restlet.routing.Route; import org.restlet.routing.Router; import org.restlet.routing.VirtualHost; @@ -74,14 +75,42 @@ public VirtualHost getVirtualHost() { } /** - * Matches a formatted string against a regex pattern, in a case-insensitive manner. + * Matches a domain against a regex pattern, in a case-insensitive manner. * * @param regex The pattern to use. - * @param formattedString The formatted string to match. + * @param domain The domain name to match. * @return True if the formatted string matched the pattern. */ - private boolean matches(String regex, String formattedString) { - return Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(formattedString).matches(); + private boolean matchesDomain(String regex, String domain) { + return Pattern.compile(regex, Pattern.CASE_INSENSITIVE) + .matcher(domain == null ? "" : domain) + .matches(); + } + + /** + * Matches a port number against a regex pattern. + * + * @param regex The pattern to use. + * @param port The port to match. + * @return True if the port matched the pattern. + */ + private boolean matchesPort(String regex, int port) { + return Pattern.compile(regex, Pattern.CASE_INSENSITIVE) + .matcher(port == -1 ? "" : Integer.toString(port)) + .matches(); + } + + /** + * Matches a scheme against a regex pattern, in a case-insensitive manner. + * + * @param regex The pattern to use. + * @param scheme The scheme to match. + * @return True if the scheme matched the pattern. + */ + private boolean matchesScheme(String regex, String scheme) { + return Pattern.compile(regex, Pattern.CASE_INSENSITIVE) + .matcher(scheme == null ? "" : scheme) + .matches(); } /** @@ -93,94 +122,65 @@ private boolean matches(String regex, String formattedString) { */ @Override public float score(Request request, Response response) { - float result = 0F; + final float result; // Prepare the value to be matched - String hostDomain = ""; - String hostPort = ""; - String hostScheme = ""; + String hostScheme = null; + String hostDomain = null; + int hostPort = -1; if (request.getHostRef() != null) { hostDomain = request.getHostRef().getHostDomain(); - - if (hostDomain == null) { - hostDomain = ""; - } - - int basePortValue = request.getHostRef().getHostPort(); - - if (basePortValue == -1) { - basePortValue = request.getHostRef().getSchemeProtocol().getDefaultPort(); - } - - hostPort = Integer.toString(basePortValue); - hostScheme = request.getHostRef().getScheme(); - - if (hostScheme == null) { - hostScheme = ""; - } + hostPort = getHostPortOrProtocolDefaultPort(request.getHostRef()); } if (request.getResourceRef() != null) { - String resourceDomain = request.getResourceRef().getHostDomain(); - - if (resourceDomain == null) { - resourceDomain = ""; - } - - int resourcePortValue = request.getResourceRef().getHostPort(); - - if (resourcePortValue == -1 && request.getResourceRef().getSchemeProtocol() != null) { - resourcePortValue = request.getResourceRef().getSchemeProtocol().getDefaultPort(); - } - - String resourcePort = - (resourcePortValue == -1) ? "" : Integer.toString(resourcePortValue); - String resourceScheme = request.getResourceRef().getScheme(); - - if (resourceScheme == null) { - resourceScheme = ""; - } + String resourceDomain = request.getResourceRef().getHostDomain(); + final int resourcePort = getHostPortOrProtocolDefaultPort(request.getResourceRef()); String serverAddress = response.getServerInfo().getAddress(); - - if (serverAddress == null) { - serverAddress = ""; + int serverPort = response.getServerInfo().getPort(); + if (serverPort == -1) { + serverPort = request.getProtocol().getDefaultPort(); } - int serverPortValue = response.getServerInfo().getPort(); - - if (serverPortValue == -1) { - serverPortValue = request.getProtocol().getDefaultPort(); - } - - String serverPort = Integer.toString(serverPortValue); - // Check if all the criteria match - if (matches(getVirtualHost().getHostDomain(), hostDomain) - && matches(getVirtualHost().getHostPort(), hostPort) - && matches(getVirtualHost().getHostScheme(), hostScheme) - && matches(getVirtualHost().getResourceDomain(), resourceDomain) - && matches(getVirtualHost().getResourcePort(), resourcePort) - && matches(getVirtualHost().getResourceScheme(), resourceScheme) - && matches(getVirtualHost().getServerAddress(), serverAddress) - && matches(getVirtualHost().getServerPort(), serverPort)) { + if (matchesDomain(getVirtualHost().getHostDomain(), hostDomain) + && matchesPort(getVirtualHost().getHostPort(), hostPort) + && matchesScheme(getVirtualHost().getHostScheme(), hostScheme) + && matchesDomain(getVirtualHost().getResourceDomain(), resourceDomain) + && matchesPort(getVirtualHost().getResourcePort(), resourcePort) + && matchesScheme(getVirtualHost().getResourceScheme(), resourceScheme) + && matchesDomain(getVirtualHost().getServerAddress(), serverAddress) + && matchesPort(getVirtualHost().getServerPort(), serverPort)) { result = 1F; + } else { + result = 0F; } + } else { + result = 0F; } // Log the result of the matching - if (getLogger().isLoggable(Level.FINER)) { - getLogger() - .finer( - "Call score for the \"" - + getVirtualHost().getName() - + "\" host: " - + result); - } + getLogger() + .finer( + () -> + "Call score for the \"" + + getVirtualHost().getName() + + "\" host: " + + result); return result; } + + private int getHostPortOrProtocolDefaultPort(final Reference reference) { + int hostPort = reference.getHostPort(); + + if (hostPort == -1 && reference.getSchemeProtocol() != null) { + hostPort = reference.getSchemeProtocol().getDefaultPort(); + } + return hostPort; + } } From 4df358b252c791f7a16de13085830f7a2b46a697 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 23:33:17 +0200 Subject: [PATCH 16/30] Take into account Sonar comments --- .../engine/converter/ConverterUtils.java | 14 +- .../engine/converter/DefaultConverter.java | 50 +++--- .../restlet/engine/header/ContentType.java | 25 ++- .../restlet/engine/header/CookieReader.java | 29 ++-- .../engine/header/CookieSettingReader.java | 89 +++++----- .../engine/header/CookieSettingWriter.java | 9 +- .../restlet/engine/header/HeaderReader.java | 164 +++++++++--------- .../restlet/engine/header/HeaderUtils.java | 110 ++++++------ .../restlet/engine/header/HeaderWriter.java | 2 +- .../engine/header/PreferenceReader.java | 12 +- .../restlet/engine/header/WarningWriter.java | 5 +- .../restlet/engine/internal/Activator.java | 14 +- .../java/org/restlet/engine/io/IoUtils.java | 43 ++--- .../java/org/restlet/engine/ssl/SslUtils.java | 142 +++++++++------ 14 files changed, 373 insertions(+), 335 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java index 5058da778d..ac4867d130 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java @@ -51,8 +51,11 @@ public static ConverterHelper getBestHelper(Object source, Variant target, Resou Context.getCurrentLogger() .log( Level.SEVERE, - "Unable get the score of the " + ch + " converter helper.", - e); + e, + () -> + "Unable get the score of the " + + ch + + " converter helper."); } } } @@ -107,8 +110,11 @@ public static List getVariants(Class sourceClass, Variant target Context.getCurrentLogger() .log( Level.FINE, - "Unable get the variants of the " + ch + " converter helper.", - e); + e, + () -> + "Unable get the variants of the " + + ch + + " converter helper."); } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java index 724fd9ab39..df06c4616f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java @@ -77,32 +77,32 @@ public List> getObjectClasses(Variant source) { @Override public List getVariants(Class source) { - List result = null; + if (source == null) { + return null; + } - if (source != null) { - if (String.class.isAssignableFrom(source) - || StringRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (File.class.isAssignableFrom(source) - || FileRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (InputStream.class.isAssignableFrom(source) - || InputRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (Reader.class.isAssignableFrom(source) - || ReaderRepresentation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (Representation.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_ALL); - } else if (Form.class.isAssignableFrom(source)) { - result = addVariant(result, VARIANT_FORM); - } else if (Serializable.class.isAssignableFrom(source)) { - if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { - result = addVariant(result, VARIANT_OBJECT); - } - if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { - result = addVariant(result, VARIANT_OBJECT_XML); - } + List result = null; + if (String.class.isAssignableFrom(source) + || StringRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (File.class.isAssignableFrom(source) + || FileRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (InputStream.class.isAssignableFrom(source) + || InputRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (Reader.class.isAssignableFrom(source) + || ReaderRepresentation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (Representation.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_ALL); + } else if (Form.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_FORM); + } else if (Serializable.class.isAssignableFrom(source)) { + if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { + result = addVariant(result, VARIANT_OBJECT); + } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { + result = addVariant(result, VARIANT_OBJECT_XML); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java b/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java index eee65d69c6..cdcca5c141 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java @@ -44,34 +44,31 @@ public static MediaType readMediaType(String contentType) { /** * Writes the HTTP "Content-Type" header. * - * @param mediaType The representation media type. + * @param mediaType The media type. * @param characterSet The representation character set. * @return The HTTP "Content-Type" header. */ public static String writeHeader(MediaType mediaType, CharacterSet characterSet) { - StringBuilder result = new StringBuilder(mediaType.toString()); - - // Specify the character set parameter if required - // TODO I wonder if the given parameter "characterSet" overrides the mediaType's charset'? - /* - if ((mediaType.getParameters().getFirstValue("charset") == null) && (characterSet != null)) { - result = result + "; charset=" + characterSet.getName(); - } - */ + final StringBuilder result = new StringBuilder(mediaType.toString()); + String mediaTypeCharset = null; for (Parameter param : mediaType.getParameters()) { if (param == null) { continue; } - if (characterSet != null && param.getName().equals("charset")) { - // TODO I wonder if the given parameter "characterSet" overrides the mediaType's - // charset'? - result.append("; ").append(param.getName()).append("=").append(characterSet); + if ("charset".equals(param.getName())) { + mediaTypeCharset = param.getValue(); } else { result.append("; ").append(param.getName()).append("=").append(param.getValue()); } } + if (characterSet != null) { + result.append("; charset=").append(characterSet.getName()); + } else if (mediaTypeCharset != null) { + result.append("; charset=").append(mediaTypeCharset); + } + return result.toString(); } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java index 2c9f10e5b9..078ef7ac6c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java @@ -74,7 +74,7 @@ private Parameter readPair(boolean readAttribute) throws IOException { nextChar = read(); if (readingName) { - if ((HeaderUtils.isSpace(nextChar)) && (nameBuffer.isEmpty())) { + if (nameBuffer.isEmpty() && HeaderUtils.isSpace(nextChar)) { // Skip spaces } else if ((nextChar == -1) || (nextChar == ';') || (nextChar == ',')) { if (!nameBuffer.isEmpty()) { @@ -101,7 +101,7 @@ private Parameter readPair(boolean readAttribute) throws IOException { } } else { // reading value - if (HeaderUtils.isSpace(nextChar) && valueBuffer.isEmpty()) { + if (valueBuffer.isEmpty() && HeaderUtils.isSpace(nextChar)) { // Skip spaces } else if ((nextChar == -1) || (nextChar == ';')) { // End of a pair @@ -129,17 +129,7 @@ public Cookie readValue() throws IOException { if (pair != null && this.globalVersion == -1) { // Cookies version not yet detected - if (NAME_VERSION.equalsIgnoreCase(pair.getName())) { - if (pair.getValue() != null) { - this.globalVersion = Integer.parseInt(pair.getValue()); - } else { - throw new IOException( - "Empty cookies version attribute detected. Please check your cookie header"); - } - } else { - // Set the default version for old Netscape cookies - this.globalVersion = 0; - } + this.globalVersion = getVersion(pair); } while ((pair != null) && (pair.getName().isEmpty() || pair.getName().charAt(0) == '$')) { @@ -169,4 +159,17 @@ public Cookie readValue() throws IOException { return result; } + + private int getVersion(final Parameter pair) throws IOException { + if (NAME_VERSION.equalsIgnoreCase(pair.getName())) { + if (pair.getValue() != null) { + return Integer.parseInt(pair.getValue()); + } else { + throw new IOException( + "Empty cookies version attribute detected. Please check your cookie header"); + } + } else { // Set the default version for old Netscape cookies + return 0; + } + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java index f8797a83df..1c7ec00050 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.Date; +import java.util.logging.Level; import org.restlet.Context; import org.restlet.data.CookieSetting; import org.restlet.data.Parameter; @@ -23,6 +24,8 @@ /** * Cookie setting header reader. * + *

The commentURL and port attributes are not supported yet. + * * @author Jerome Louvel */ public class CookieSettingReader extends HeaderReader { @@ -31,8 +34,6 @@ public class CookieSettingReader extends HeaderReader { private static final String NAME_SET_COMMENT = "comment"; - private static final String NAME_SET_COMMENT_URL = "commentURL"; - private static final String NAME_SET_DISCARD = "discard"; private static final String NAME_SET_DOMAIN = "domain"; @@ -43,8 +44,6 @@ public class CookieSettingReader extends HeaderReader { private static final String NAME_SET_PATH = "path"; - private static final String NAME_SET_PORT = "port"; - private static final String NAME_SET_SECURE = "secure"; private static final String NAME_SET_VERSION = "version"; @@ -52,7 +51,7 @@ public class CookieSettingReader extends HeaderReader { /** * Parses the given String to a CookieSetting * - * @param cookieSetting + * @param cookieSetting The cookie setting as String to parse. * @return the CookieSetting parsed from the String * @throws IllegalArgumentException Thrown if the String cannot be parsed as CookieSetting. */ @@ -70,7 +69,7 @@ public static CookieSetting read(String cookieSetting) throws IllegalArgumentExc private volatile Parameter cachedPair; /** The global cookie specification version. */ - private volatile int globalVersion; + private final int globalVersion; /** * Constructor. @@ -107,7 +106,7 @@ private Parameter readPair() throws IOException { nextChar = read(); if (readingName) { - if ((HeaderUtils.isSpace(nextChar)) && (nameBuffer.isEmpty())) { + if (nameBuffer.isEmpty() && HeaderUtils.isSpace(nextChar)) { // Skip spaces } else if ((nextChar == -1) || (nextChar == ';') || (nextChar == ',')) { if (!nameBuffer.isEmpty()) { @@ -129,7 +128,7 @@ private Parameter readPair() throws IOException { } } else { // reading value - if ((HeaderUtils.isSpace(nextChar)) && (valueBuffer.isEmpty())) { + if (valueBuffer.isEmpty() && HeaderUtils.isSpace(nextChar)) { // Skip spaces } else if ((nextChar == -1) || (nextChar == ';')) { // End of pair @@ -152,7 +151,7 @@ private Parameter readPair() throws IOException { @Override public CookieSetting readValue() throws IOException { - Parameter pair = null; + Parameter pair; // Unexpected special attributes // Silently ignore it as it may have been introduced by new specifications @@ -166,42 +165,26 @@ public CookieSetting readValue() throws IOException { CookieSetting result = new CookieSetting(pair.getName(), pair.getValue()); while ((pair = readPair()) != null) { - if (pair.getName().equalsIgnoreCase(NAME_SET_PATH)) { + if (NAME_SET_PATH.equalsIgnoreCase(pair.getName())) { result.setPath(pair.getValue()); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_DOMAIN)) { + } else if (NAME_SET_DOMAIN.equalsIgnoreCase(pair.getName())) { result.setDomain(pair.getValue()); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_COMMENT)) { + } else if (NAME_SET_COMMENT.equalsIgnoreCase(pair.getName())) { result.setComment(pair.getValue()); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_COMMENT_URL)) { - // No yet supported - } else if (pair.getName().equalsIgnoreCase(NAME_SET_DISCARD)) { + } else if (NAME_SET_DISCARD.equalsIgnoreCase(pair.getName())) { result.setMaxAge(-1); - } else if (pair.getName().equalsIgnoreCase(NAME_SET_EXPIRES)) { - Date expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1036); - - if (expires == null) { - expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1123); - } - + } else if (NAME_SET_EXPIRES.equalsIgnoreCase(pair.getName())) { + Date expires = parseExpiresDate(pair); if (expires == null) { - expires = DateUtils.parse(pair.getValue(), FORMAT_ASC_TIME); - } - - if (expires != null) { - final Date current = new Date(System.currentTimeMillis()); - if (DateUtils.after(current, expires)) { - result.setMaxAge((int) ((expires.getTime() - current.getTime()) / 1000)); - } else { - result.setMaxAge(0); - } - } else { - // Ignore the expires header + // Ignore the "expires" header Context.getCurrentLogger() .warning( "Ignoring cookie setting expiration date. Unable to parse the date: " + pair.getValue()); + } else { + result.setMaxAge(fromExpirationDateToMaxAge(expires)); } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_MAX_AGE)) { + } else if (NAME_SET_MAX_AGE.equalsIgnoreCase(pair.getName())) { try { result.setMaxAge(Integer.parseInt(pair.getValue())); } catch (NumberFormatException numberFormatException) { @@ -213,24 +196,46 @@ public CookieSetting readValue() throws IOException { + "\", used Integer.MAX_VALUE instead: " + Integer.MAX_VALUE); } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_PORT)) { - // No yet supported - } else if (pair.getName().equalsIgnoreCase(NAME_SET_SECURE)) { + } else if (NAME_SET_SECURE.equalsIgnoreCase(pair.getName())) { if (StringUtils.isNullOrEmpty(pair.getValue())) { result.setSecure(true); } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_ACCESS_RESTRICTED)) { + } else if (NAME_SET_ACCESS_RESTRICTED.equalsIgnoreCase(pair.getName())) { if (StringUtils.isNullOrEmpty(pair.getValue())) { result.setAccessRestricted(true); } - } else if (pair.getName().equalsIgnoreCase(NAME_SET_VERSION)) { + } else if (NAME_SET_VERSION.equalsIgnoreCase(pair.getName())) { result.setVersion(Integer.parseInt(pair.getValue())); } else { - // Unexpected special attribute - // Silently ignore it as it may have been introduced by new specifications + // Silently ignore the parameter as it may have been introduced by new + // specifications + Context.getCurrentLogger() + .log( + Level.FINE, + "The \"{0}\" attribute is not supported yet for cookie settings. Ignoring it.", + pair.getName()); } } return result; } + + private static int fromExpirationDateToMaxAge(final Date expires) { + final Date current = new Date(System.currentTimeMillis()); + + return DateUtils.after(current, expires) + ? (int) ((expires.getTime() - current.getTime()) / 1000) + : 0; + } + + private static Date parseExpiresDate(final Parameter pair) { + Date expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1036); + if (expires == null) { + expires = DateUtils.parse(pair.getValue(), FORMAT_RFC_1123); + } + if (expires == null) { + expires = DateUtils.parse(pair.getValue(), FORMAT_ASC_TIME); + } + return expires; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java index 19c87f19ab..2702425eb9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java @@ -87,18 +87,17 @@ public CookieSettingWriter append(CookieSetting cookieSetting) throws IllegalArg Date expires = new Date(expiresTime); append("; Expires="); - appendValue(DateUtils.format(expires, DateUtils.FORMAT_RFC_1123.get(0)), version); + appendValue( + DateUtils.format(expires, DateUtils.FORMAT_RFC_1123.getFirst()), version); } else { append("; Max-Age="); appendValue(Integer.toString(cookieSetting.getMaxAge()), version); } } else if ((maxAge == -1) && (version > 0)) { - // Discard the cookie at the end of the user's session (RFC - // 2965) + // Discard the cookie at the end of the user's session (RFC 2965) append("; Discard"); } else { - // NetScape cookies automatically expire at the end of the - // user's session + // NetScape cookies automatically expire at the end of the user's session } // Append the domain diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java index 3c5ed3ba6e..69cbfc889f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java @@ -49,8 +49,8 @@ public class HeaderReader { * @param resultClass The NamedValue class to return. * @return The new NamedValue. */ - private static > NV createNamedValue( - Class resultClass, String name) { + private static > N createNamedValue( + Class resultClass, String name) { return createNamedValue(resultClass, name, null); } @@ -62,8 +62,8 @@ private static > NV createNamedValue( * @param resultClass The named value class to return. * @return The new named value. */ - private static > NV createNamedValue( - Class resultClass, String name, String value) { + private static > N createNamedValue( + Class resultClass, String name, String value) { try { return resultClass.getConstructor(String.class, String.class).newInstance(name, value); } catch (Exception e) { @@ -95,54 +95,55 @@ public static Date readDate(String date, boolean cookie) { * @throws IOException */ public static Header readHeader(CharSequence header) throws IOException { - Header result = null; + if (header.isEmpty()) { + return null; + } - if (!header.isEmpty()) { - // Detect the end of headers - int start = 0; - int index = 0; - int next = header.charAt(index++); + int index = 0; + int next = header.charAt(index++); - if (isCarriageReturn(next)) { - next = header.charAt(index++); + if (isCarriageReturn(next)) { // Detect the end of headers + next = header.charAt(index); - if (!isLineFeed(next)) { - throw new IOException( - "Invalid end of headers. Line feed missing after the carriage return."); - } + if (isLineFeed(next)) { + return null; } else { - result = new Header(); + throw new IOException( + "Invalid end of headers. Line feed missing after the carriage return."); + } + } - // Parse the header name - while ((index < header.length()) && (next != ':')) { - next = header.charAt(index++); - } + final Header result = new Header(); - if (next != ':') { - // Colon character is mandatory - throw new IOException( - "Unable to parse the header name. End of line reached too early."); - } + // Parse the header name + while ((index < header.length()) && (next != ':')) { + next = header.charAt(index++); + } - result.setName(header.subSequence(start, index - 1).toString()); + if (next != ':') { + // Colon character is mandatory + throw new IOException( + "Unable to parse the header name. End of line reached too early."); + } - if (index == header.length()) { - // No more content to read. - return result; - } - next = header.charAt(index++); + int start = 0; + result.setName(header.subSequence(start, index - 1).toString()); - while ((index < header.length()) && isSpace(next)) { - // Skip any separator space between colon and header value - next = header.charAt(index++); - } - if (index < header.length()) { - start = index - 1; + if (index == header.length()) { + // No more content to read. + return result; + } + next = header.charAt(index++); - // Parse the header value - result.setValue(header.subSequence(start, header.length()).toString()); - } - } + while ((index < header.length()) && isSpace(next)) { + // Skip any separator space between colon and header value + next = header.charAt(index++); + } + if (index < header.length()) { + start = index - 1; + + // Parse the header value + result.setValue(header.subSequence(start, header.length()).toString()); } return result; @@ -157,61 +158,60 @@ public static Header readHeader(CharSequence header) throws IOException { * @throws IOException */ public static Header readHeader(InputStream is, StringBuilder sb) throws IOException { - Header result = null; - // Detect the end of headers int next = is.read(); - if (isCarriageReturn(next)) { + if (isCarriageReturn(next)) { // Detect the end of headers next = is.read(); - if (!isLineFeed(next)) { + if (isLineFeed(next)) { + return null; + } else { throw new IOException( "Invalid end of headers. Line feed missing after the carriage return."); } - } else { - result = new Header(); - - // Parse the header name - while ((next != -1) && (next != ':')) { - sb.append((char) next); - next = is.read(); - } + } - if (next == -1) { - throw new IOException( - "Unable to parse the header name. End of stream reached too early."); - } + final Header result = new Header(); - result.setName(sb.toString()); - sb.delete(0, sb.length()); + // Parse the header name + while ((next != -1) && (next != ':')) { + sb.append((char) next); next = is.read(); + } - while (isSpace(next)) { - // Skip any separator space between colon and header value - next = is.read(); - } + if (next == -1) { + throw new IOException( + "Unable to parse the header name. End of stream reached too early."); + } - // Parse the header value - while ((next != -1) && (!isCarriageReturn(next))) { - sb.append((char) next); - next = is.read(); - } + result.setName(sb.toString()); + sb.delete(0, sb.length()); + next = is.read(); - if (next == -1) { - throw new IOException( - "Unable to parse the header value. End of stream reached too early."); - } + while (isSpace(next)) { // Skip any separator space between colon and header value + next = is.read(); + } + // Parse the header value + while ((next != -1) && (!isCarriageReturn(next))) { + sb.append((char) next); next = is.read(); + } - if (isLineFeed(next)) { - result.setValue(sb.toString()); - sb.delete(0, sb.length()); - } else { - throw new IOException( - "Unable to parse the HTTP header value. The carriage return must be followed by a line feed."); - } + if (next == -1) { + throw new IOException( + "Unable to parse the header value. End of stream reached too early."); + } + + next = is.read(); + + if (isLineFeed(next)) { + result.setValue(sb.toString()); + sb.delete(0, sb.length()); + } else { + throw new IOException( + "Unable to parse the HTTP header value. The carriage return must be followed by a line feed."); } return result; @@ -441,9 +441,9 @@ public String readDigits() { * @return The next pair as a parameter. * @throws IOException */ - public > NV readNamedValue(Class resultClass) + public > N readNamedValue(Class resultClass) throws IOException { - NV result = null; + N result = null; String name = readToken(); int nextChar = read(); diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java index 1a3564708e..c5300d80ff 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java @@ -104,6 +104,7 @@ import org.restlet.data.Reference; import org.restlet.data.Tag; import org.restlet.engine.Engine; +import org.restlet.engine.security.AuthenticatorUtils; import org.restlet.engine.util.CaseInsensitiveHashSet; import org.restlet.engine.util.DateUtils; import org.restlet.engine.util.StringUtils; @@ -336,9 +337,12 @@ public static void addHeader(String headerName, String headerValue, Series "Unable to format the " + headerName + " header"); } } } @@ -534,8 +538,7 @@ public static void addRequestHeaders(Request request, Series

headers) { if (challengeResponse != null) { String authHeader = - org.restlet.engine.security.AuthenticatorUtils.formatResponse( - challengeResponse, request, headers); + AuthenticatorUtils.formatResponse(challengeResponse, request, headers); if (authHeader != null) { addHeader(HEADER_AUTHORIZATION, authHeader, headers); @@ -546,8 +549,7 @@ public static void addRequestHeaders(Request request, Series
headers) { if (proxyChallengeResponse != null) { String authHeader = - org.restlet.engine.security.AuthenticatorUtils.formatResponse( - proxyChallengeResponse, request, headers); + AuthenticatorUtils.formatResponse(proxyChallengeResponse, request, headers); if (authHeader != null) { addHeader(HEADER_PROXY_AUTHORIZATION, authHeader, headers); @@ -585,8 +587,7 @@ public static void addResponseHeaders(Response response, Series
headers) for (ChallengeRequest challengeRequest : response.getProxyChallengeRequests()) { addHeader( HEADER_PROXY_AUTHENTICATE, - org.restlet.engine.security.AuthenticatorUtils.formatRequest( - challengeRequest, response, headers), + AuthenticatorUtils.formatRequest(challengeRequest, response, headers), headers); } } @@ -614,8 +615,7 @@ public static void addResponseHeaders(Response response, Series
headers) for (ChallengeRequest challengeRequest : response.getChallengeRequests()) { addHeader( HEADER_WWW_AUTHENTICATE, - org.restlet.engine.security.AuthenticatorUtils.formatRequest( - challengeRequest, response, headers), + AuthenticatorUtils.formatRequest(challengeRequest, response, headers), headers); } } @@ -669,8 +669,7 @@ public static void addResponseHeaders(Response response, Series
headers) if (response.getAuthenticationInfo() != null) { addHeader( HEADER_AUTHENTICATION_INFO, - org.restlet.engine.security.AuthenticatorUtils.formatAuthenticationInfo( - response.getAuthenticationInfo()), + AuthenticatorUtils.formatAuthenticationInfo(response.getAuthenticationInfo()), headers); } @@ -696,7 +695,7 @@ public static void addResponseHeaders(Response response, Series
headers) */ public static void keepExtensionHeadersOnly(Message message) { Series
headers = message.getHeaders(); - Series
extensionHeaders = new Series
(Header.class); + Series
extensionHeaders = new Series<>(Header.class); for (Header header : headers) { if (!STANDARD_HEADERS.contains(header.getName())) { extensionHeaders.add(header); @@ -740,9 +739,10 @@ public static void copyResponseTransportHeaders(Series
headers, Response Context.getCurrentLogger() .log( Level.WARNING, - "Error during Age header parsing. Header: " - + header.getValue(), - nfe); + nfe, + () -> + "Error during Age header parsing. Header: " + + header.getValue()); } } else if (HEADER_DATE.equalsIgnoreCase(header.getName())) { Date date = DateUtils.parse(header.getValue()); @@ -766,9 +766,10 @@ public static void copyResponseTransportHeaders(Series
headers, Response Context.getCurrentLogger() .log( Level.WARNING, - "Error during Retry-After header parsing. Header: " - + header.getValue(), - nfe); + nfe, + () -> + "Error during Retry-After header parsing. Header: " + + header.getValue()); } } @@ -782,24 +783,22 @@ public static void copyResponseTransportHeaders(Series
headers, Response Context.getCurrentLogger() .log( Level.WARNING, - "Error during cookie setting parsing. Header: " - + header.getValue(), - e); + e, + () -> + "Error during cookie setting parsing. Header: " + + header.getValue()); } } else if (HEADER_WWW_AUTHENTICATE.equalsIgnoreCase(header.getName())) { List crs = - org.restlet.engine.security.AuthenticatorUtils.parseRequest( - response, header.getValue(), headers); + AuthenticatorUtils.parseRequest(response, header.getValue(), headers); response.getChallengeRequests().addAll(crs); } else if (HEADER_PROXY_AUTHENTICATE.equalsIgnoreCase(header.getName())) { List crs = - org.restlet.engine.security.AuthenticatorUtils.parseRequest( - response, header.getValue(), headers); + AuthenticatorUtils.parseRequest(response, header.getValue(), headers); response.getProxyChallengeRequests().addAll(crs); } else if (HEADER_AUTHENTICATION_INFO.equalsIgnoreCase(header.getName())) { AuthenticationInfo authenticationInfo = - org.restlet.engine.security.AuthenticatorUtils.parseAuthenticationInfo( - header.getValue()); + AuthenticatorUtils.parseAuthenticationInfo(header.getValue()); response.setAuthenticationInfo(authenticationInfo); } else if (HEADER_SERVER.equalsIgnoreCase(header.getName())) { response.getServerInfo().setAgent(header.getValue()); @@ -888,9 +887,10 @@ public static Representation extractEntityHeaders( Context.getCurrentLogger() .log( Level.WARNING, - "Error during Content-Disposition header parsing. Header: " - + header.getValue(), - ioe); + ioe, + () -> + "Error during Content-Disposition header parsing. Header: " + + header.getValue()); } } else if (HEADER_CONTENT_RANGE.equalsIgnoreCase(header.getName())) { org.restlet.engine.header.RangeReader.update(header.getValue(), result); @@ -1157,31 +1157,29 @@ public static boolean isSemiColon(int character) { * @return True if the given character is a separator. */ public static boolean isSeparator(int character) { - switch (character) { - case '(': - case ')': - case '<': - case '>': - case '@': - case ',': - case ';': - case ':': - case '\\': - case '"': - case '/': - case '[': - case ']': - case '?': - case '=': - case '{': - case '}': - case ' ': - case '\t': - return true; - - default: - return false; - } + return switch (character) { + case '(', + ')', + '<', + '>', + '@', + ',', + ';', + ':', + '\\', + '"', + '/', + '[', + ']', + '?', + '=', + '{', + '}', + ' ', + '\t' -> + true; + default -> false; + }; } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java index db4f0168d7..59f6c87114 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java @@ -88,7 +88,7 @@ public HeaderWriter append(int i) { } /** - * Appends a long. + * Appends a long value. * * @param l The value to append. * @return This writer. diff --git a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java index 79b7f25864..0d3437fc77 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java @@ -159,7 +159,7 @@ public static float readQuality(String quality) { } /** The type of metadata read. */ - private volatile int type; + private final int type; /** * Constructor. @@ -199,10 +199,11 @@ protected Preference createPreference(CharSequence metadata, Series mediaParams = extractMediaParams(parameters); @@ -222,10 +223,11 @@ protected Preference createPreference(CharSequence metadata, Series warnings) { @Override public WarningWriter append(Warning warning) { - String agent = warning.getAgent(); - String text = warning.getText(); if (warning.getStatus() == null) { throw new IllegalArgumentException("Can't write warning. Invalid status code detected"); } + String agent = warning.getAgent(); + String text = warning.getText(); + if ((agent == null) || (agent.isEmpty())) { throw new IllegalArgumentException("Can't write warning. Invalid agent detected"); } diff --git a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java index d7e3660b2a..068d27c2be 100644 --- a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java +++ b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java @@ -19,8 +19,8 @@ import org.restlet.engine.Engine; /** - * OSGi activator. It registers the NRE into the Restlet API and also introspect the bundles to find - * connector or authentication helpers. + * OSGi activator. It registers the NRE into the Restlet API and also introspects the bundles to + * find connector or authentication helpers. * * @author Jerome Louvel */ @@ -123,14 +123,8 @@ public void start(BundleContext context) throws Exception { // Listen to installed bundles context.addBundleListener( event -> { - switch (event.getType()) { - case BundleEvent.INSTALLED: - registerHelpers(event.getBundle()); - break; - case BundleEvent.UNINSTALLED: - break; - default: - break; + if (event.getType() == BundleEvent.INSTALLED) { + registerHelpers(event.getBundle()); } }); diff --git a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java index 42296291fe..c01c82ce93 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java @@ -149,38 +149,39 @@ public static boolean delete(java.io.File file, boolean recursive) { * @return True if the deletion was successful or if the file or directory didn't exist. */ public static boolean delete(java.io.File file, boolean recursive, boolean garbageCollect) { + if (!file.exists()) { + return true; + } + boolean result = true; boolean runGC = garbageCollect; - if (file.exists()) { - if (file.isDirectory()) { - java.io.File[] entries = file.listFiles(); - - // Check if the directory is empty - if (entries != null && entries.length > 0) { - if (recursive) { - for (int i = 0; result && (i < entries.length); i++) { - if (runGC) { - System.gc(); - runGC = false; - } + if (file.isDirectory()) { + java.io.File[] entries = file.listFiles(); - result = delete(entries[i], true, false); + // Check if the directory is empty + if (entries != null && entries.length > 0) { + if (recursive) { + for (int i = 0; result && (i < entries.length); i++) { + if (runGC) { + System.gc(); + runGC = false; } - } else { - result = false; + + result = delete(entries[i], true, false); } + } else { + result = false; } } + } - if (runGC) { - System.gc(); - runGC = false; - } - - result = result && file.delete(); + if (runGC) { + System.gc(); } + result = result && file.delete(); + return result; } diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java index c7369d68b2..bd463346f7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java @@ -14,7 +14,9 @@ import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; import org.restlet.Context; +import org.restlet.data.Parameter; import org.restlet.engine.RestletHelper; +import org.restlet.util.Series; /** * Various HTTPS utilities. @@ -97,71 +99,101 @@ public static Integer extractKeySize(String sslCipherSuite) { */ public static SslContextFactory getSslContextFactory(RestletHelper helper) { - SslContextFactory result = - (SslContextFactory) - ((helper.getContext() == null) - ? null - : helper.getContext().getAttributes().get("sslContextFactory")); + SslContextFactory result = getSslContextFactoryFromContext(helper.getContext()); if (result == null) { - String[] sslContextFactoryNames = - helper.getHelpedParameters().getValuesArray("sslContextFactory"); - - if (sslContextFactoryNames != null) { - for (String sslContextFactoryName : sslContextFactoryNames) { - if ((result == null) && (sslContextFactoryName != null)) { - try { - Class sslContextFactoryClass = - Class.forName(sslContextFactoryName) - .asSubclass(SslContextFactory.class); - result = sslContextFactoryClass.getDeclaredConstructor().newInstance(); - result.init(helper.getHelpedParameters()); - } catch (ClassNotFoundException e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Unable to find SslContextFactory class: " - + sslContextFactoryName, - e); - } catch (ClassCastException e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Class " - + sslContextFactoryName - + " does not implement SslContextFactory", - e); - } catch (InstantiationException | NoSuchMethodException e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Could not instantiate class " - + sslContextFactoryName - + " with default constructor", - e); - } catch (IllegalAccessException e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Illegal access when instantiating class " - + sslContextFactoryName, - e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - } - } + final Series helpedParameters = helper.getHelpedParameters(); + result = getSslContextFactoryFromHelpedParameters(helpedParameters); } if (result == null) { - result = new DefaultSslContextFactory(); - result.init(helper.getHelpedParameters()); + result = getDefaultSslContextFactory(helper); } return result; } + private static SslContextFactory getDefaultSslContextFactory(final RestletHelper helper) { + SslContextFactory result; + result = new DefaultSslContextFactory(); + result.init(helper.getHelpedParameters()); + return result; + } + + private static SslContextFactory getSslContextFactoryFromHelpedParameters( + final Series helpedParameters) { + + String[] sslContextFactoryNames = helpedParameters.getValuesArray("sslContextFactory"); + if (sslContextFactoryNames == null) { + return null; + } + + SslContextFactory result = null; + for (String sslContextFactoryName : sslContextFactoryNames) { + if (result == null && sslContextFactoryName != null) { + result = getSslContextFactoryByClassName(helpedParameters, sslContextFactoryName); + } + } + return result; + } + + private static SslContextFactory getSslContextFactoryByClassName( + final Series helpedParameters, final String sslContextFactoryName) { + try { + Class sslContextFactoryClass = + Class.forName(sslContextFactoryName).asSubclass(SslContextFactory.class); + SslContextFactory result = + sslContextFactoryClass.getDeclaredConstructor().newInstance(); + result.init(helpedParameters); + return result; + } catch (ClassNotFoundException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + e, + () -> + "Unable to find SslContextFactory class: " + + sslContextFactoryName); + } catch (ClassCastException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + e, + () -> + "Class " + + sslContextFactoryName + + " does not implement SslContextFactory"); + } catch (InstantiationException | NoSuchMethodException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + e, + () -> + "Could not instantiate class " + + sslContextFactoryName + + " with default constructor"); + } catch (IllegalAccessException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + e, + () -> + "Illegal access when instantiating class " + + sslContextFactoryName); + } catch (InvocationTargetException e) { + throw new RuntimeException( + "Cannot instantiate SslContextFactory class: " + sslContextFactoryName, e); + } + + return null; + } + + private static SslContextFactory getSslContextFactoryFromContext(final Context context) { + return context == null + ? null + : (SslContextFactory) context.getAttributes().get("sslContextFactory"); + } + /** * Private constructor to ensure that the class acts as a true utility class i.e., it isn't * instantiable and extensible. From 90de15dcb36dca72548f5a6c736b2d976c7901cc Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sat, 4 Apr 2026 23:40:27 +0200 Subject: [PATCH 17/30] Take into account Sonar comments --- .../java/org/restlet/engine/converter/ConverterHelper.java | 2 +- .../org/restlet/engine/converter/DefaultConverter.java | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java index e3fba6eb5f..815769f5a0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java @@ -107,7 +107,7 @@ public List addVariants( /** * Returns the list of variants that can be converted from a given object class. The preferred - * variant should be set in first position. + * variant should be set in the first position. * * @param source The source object class. * @return The list of variants that can be converted. diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java index df06c4616f..8eb1e973e3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java @@ -77,9 +77,7 @@ public List> getObjectClasses(Variant source) { @Override public List getVariants(Class source) { - if (source == null) { - return null; - } + if (source == null) { return List.of(); } List result = null; if (String.class.isAssignableFrom(source) @@ -101,7 +99,8 @@ public List getVariants(Class source) { } else if (Serializable.class.isAssignableFrom(source)) { if (ObjectRepresentation.VARIANT_OBJECT_BINARY_SUPPORTED) { result = addVariant(result, VARIANT_OBJECT); - } else if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { + } + if (ObjectRepresentation.VARIANT_OBJECT_XML_SUPPORTED) { result = addVariant(result, VARIANT_OBJECT_XML); } } From f4aa083e63937ae59d04bda9d32b9cfe8c00c8a9 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 14:14:55 +0200 Subject: [PATCH 18/30] Take into account Sonar comments --- copyright.md | 2 +- .../restlet/ext/crypto/AwsAuthenticator.java | 2 +- .../ext/crypto/CookieAuthenticator.java | 2 +- .../ext/crypto/DigestAuthenticator.java | 2 +- .../org/restlet/ext/crypto/DigestUtils.java | 2 +- .../restlet/ext/crypto/DigestVerifier.java | 2 +- .../restlet/ext/crypto/internal/AwsUtils.java | 2 +- .../ext/crypto/internal/AwsVerifier.java | 2 +- .../ext/crypto/internal/CryptoUtils.java | 2 +- .../crypto/internal/HttpAwsQueryHelper.java | 2 +- .../ext/crypto/internal/HttpAwsS3Helper.java | 2 +- .../internal/HttpAzureSharedKeyHelper.java | 2 +- .../HttpAzureSharedKeyLiteHelper.java | 2 +- .../ext/crypto/internal/HttpDigestHelper.java | 2 +- .../crypto/internal/HttpDigestVerifier.java | 2 +- .../crypto/CookieAuthenticatorTestCase.java | 2 +- .../ext/crypto/DigestVerifierTestCase.java | 2 +- .../ext/crypto/HttpDigestTestCase.java | 2 +- .../internal/HttpAwsS3HelperTestCase.java | 2 +- .../internal/HttpAwsS3HostNameTestCase.java | 2 +- .../internal/HttpAwsS3SigningTestCase.java | 2 +- .../internal/HttpAwsS3VerifierTestCase.java | 2 +- .../internal/HttpDigestHelperTestCase.java | 2 +- .../ext/freemarker/ContextTemplateLoader.java | 2 +- .../ext/freemarker/FreemarkerConverter.java | 2 +- .../ext/freemarker/TemplateFilter.java | 2 +- .../freemarker/TemplateRepresentation.java | 2 +- .../internal/ResolverHashModel.java | 2 +- .../ext/freemarker/internal/ScalarModel.java | 2 +- .../ext/freemarker/FreeMarkerTestCase.java | 2 +- .../freemarker/TemplateFilterTestCase.java | 2 +- .../org/restlet/ext/gson/GsonConverter.java | 2 +- .../restlet/ext/gson/GsonRepresentation.java | 2 +- .../org/restlet/ext/gson/GsonTestCase.java | 2 +- .../ext/jaas/ChallengeCallbackHandler.java | 2 +- .../java/org/restlet/ext/jaas/JaasUtils.java | 2 +- .../org/restlet/ext/jaas/JaasVerifier.java | 2 +- .../restlet/ext/jackson/JacksonConverter.java | 2 +- .../ext/jackson/JacksonRepresentation.java | 2 +- .../jackson/internal/XmlFactoryProvider.java | 2 +- .../org/restlet/ext/jackson/Customer.java | 2 +- .../java/org/restlet/ext/jackson/Invoice.java | 2 +- .../restlet/ext/jackson/JacksonTestCase.java | 2 +- .../org/restlet/ext/jackson/MyException.java | 2 +- .../org/restlet/ext/json/JsonConverter.java | 2 +- .../restlet/ext/json/JsonRepresentation.java | 2 +- .../org/restlet/ext/json/JsonpFilter.java | 2 +- .../restlet/ext/json/JsonpRepresentation.java | 2 +- .../restlet/ext/json/JsonpFilterTestCase.java | 2 +- .../ext/json/JsonpRepresentationTestCase.java | 2 +- .../org/restlet/ext/slf4j/Slf4jLogger.java | 2 +- .../restlet/ext/slf4j/Slf4jLoggerFacade.java | 2 +- .../restlet/ext/spring/SpringBeanFinder.java | 2 +- .../restlet/ext/spring/SpringBeanRouter.java | 2 +- .../restlet/ext/spring/SpringComponent.java | 2 +- .../org/restlet/ext/spring/SpringContext.java | 2 +- .../org/restlet/ext/spring/SpringFinder.java | 2 +- .../org/restlet/ext/spring/SpringHost.java | 2 +- .../restlet/ext/spring/SpringResource.java | 2 +- .../org/restlet/ext/spring/SpringRouter.java | 2 +- .../org/restlet/ext/spring/SpringServer.java | 2 +- .../ext/spring/SpringBeanFinderTestCase.java | 2 +- .../ext/spring/SpringBeanRouterTestCase.java | 2 +- .../restlet/ext/spring/SpringTestCase.java | 2 +- .../ext/spring/resources/OrderResource.java | 2 +- .../ext/spring/resources/OrdersResource.java | 2 +- .../ext/spring/resources/UserResource.java | 2 +- .../restlet/ext/thymeleaf/TemplateFilter.java | 2 +- .../ext/thymeleaf/TemplateRepresentation.java | 2 +- .../ext/thymeleaf/ThymeleafConverter.java | 2 +- .../ext/thymeleaf/ThymeleafTestCase.java | 2 +- .../RepresentationResourceLoader.java | 2 +- .../restlet/ext/velocity/TemplateFilter.java | 2 +- .../ext/velocity/TemplateRepresentation.java | 2 +- .../ext/velocity/VelocityConverter.java | 2 +- .../ext/velocity/TemplateFilterTestCase.java | 2 +- .../ext/velocity/VelocityTestCase.java | 2 +- .../restlet/ext/xml/DomRepresentation.java | 2 +- .../java/org/restlet/ext/xml/NodeList.java | 2 +- .../restlet/ext/xml/SaxRepresentation.java | 2 +- .../ext/xml/TransformRepresentation.java | 2 +- .../java/org/restlet/ext/xml/Transformer.java | 2 +- .../org/restlet/ext/xml/XmlConverter.java | 2 +- .../restlet/ext/xml/XmlRepresentation.java | 2 +- .../java/org/restlet/ext/xml/XmlWriter.java | 2 +- .../ext/xml/internal/AbstractXmlReader.java | 2 +- .../ext/xml/internal/ContextResolver.java | 2 +- .../ext/xml/ResolvingTransformerTestCase.java | 2 +- .../xml/TransformRepresentationTestCase.java | 2 +- .../restlet/ext/xml/TransformerTestCase.java | 2 +- .../main/java/org/restlet/Application.java | 2 +- .../src/main/java/org/restlet/Client.java | 2 +- .../src/main/java/org/restlet/Component.java | 2 +- .../src/main/java/org/restlet/Connector.java | 2 +- .../src/main/java/org/restlet/Context.java | 2 +- .../src/main/java/org/restlet/Message.java | 2 +- .../src/main/java/org/restlet/Request.java | 2 +- .../src/main/java/org/restlet/Response.java | 2 +- .../src/main/java/org/restlet/Restlet.java | 2 +- .../src/main/java/org/restlet/Server.java | 2 +- .../src/main/java/org/restlet/Uniform.java | 2 +- .../org/restlet/data/AuthenticationInfo.java | 2 +- .../java/org/restlet/data/CacheDirective.java | 2 +- .../org/restlet/data/ChallengeMessage.java | 2 +- .../org/restlet/data/ChallengeRequest.java | 2 +- .../org/restlet/data/ChallengeResponse.java | 2 +- .../org/restlet/data/ChallengeScheme.java | 2 +- .../java/org/restlet/data/CharacterSet.java | 2 +- .../java/org/restlet/data/ClientInfo.java | 2 +- .../java/org/restlet/data/Conditions.java | 2 +- .../main/java/org/restlet/data/Cookie.java | 2 +- .../java/org/restlet/data/CookieSetting.java | 2 +- .../main/java/org/restlet/data/Digest.java | 2 +- .../main/java/org/restlet/data/Dimension.java | 2 +- .../java/org/restlet/data/Disposition.java | 2 +- .../main/java/org/restlet/data/Encoding.java | 2 +- .../java/org/restlet/data/Expectation.java | 2 +- .../src/main/java/org/restlet/data/Form.java | 2 +- .../main/java/org/restlet/data/Header.java | 2 +- .../main/java/org/restlet/data/Language.java | 2 +- .../java/org/restlet/data/LocalReference.java | 2 +- .../main/java/org/restlet/data/MediaType.java | 2 +- .../main/java/org/restlet/data/Metadata.java | 2 +- .../main/java/org/restlet/data/Method.java | 2 +- .../main/java/org/restlet/data/Parameter.java | 2 +- .../java/org/restlet/data/Preference.java | 2 +- .../main/java/org/restlet/data/Product.java | 2 +- .../main/java/org/restlet/data/Protocol.java | 2 +- .../src/main/java/org/restlet/data/Range.java | 2 +- .../java/org/restlet/data/RecipientInfo.java | 2 +- .../main/java/org/restlet/data/Reference.java | 2 +- .../java/org/restlet/data/ReferenceList.java | 2 +- .../java/org/restlet/data/ServerInfo.java | 2 +- .../main/java/org/restlet/data/Status.java | 2 +- .../src/main/java/org/restlet/data/Tag.java | 2 +- .../main/java/org/restlet/data/Warning.java | 2 +- .../org/restlet/engine/CompositeHelper.java | 2 +- .../main/java/org/restlet/engine/Edition.java | 2 +- .../main/java/org/restlet/engine/Engine.java | 2 +- .../main/java/org/restlet/engine/Helper.java | 2 +- .../org/restlet/engine/RestletHelper.java | 2 +- .../org/restlet/engine/adapter/Adapter.java | 2 +- .../java/org/restlet/engine/adapter/Call.java | 2 +- .../restlet/engine/adapter/ClientAdapter.java | 2 +- .../restlet/engine/adapter/ClientCall.java | 2 +- .../engine/adapter/HttpClientHelper.java | 2 +- .../restlet/engine/adapter/HttpRequest.java | 2 +- .../restlet/engine/adapter/HttpResponse.java | 2 +- .../engine/adapter/HttpServerHelper.java | 2 +- .../engine/adapter/JettyClientCall.java | 2 +- .../restlet/engine/adapter/JettyHandler.java | 2 +- .../engine/adapter/JettyServerCall.java | 2 +- .../restlet/engine/adapter/ServerAdapter.java | 2 +- .../restlet/engine/adapter/ServerCall.java | 2 +- .../engine/application/ApplicationHelper.java | 2 +- .../restlet/engine/application/Conneg.java | 2 +- .../engine/application/CorsFilter.java | 2 +- .../application/CorsResponseHelper.java | 2 +- .../application/DecodeRepresentation.java | 2 +- .../restlet/engine/application/Decoder.java | 2 +- .../application/EncodeRepresentation.java | 2 +- .../restlet/engine/application/Encoder.java | 2 +- .../engine/application/FlexibleConneg.java | 2 +- .../engine/application/MetadataExtension.java | 2 +- .../engine/application/RangeFilter.java | 2 +- .../application/RangeRepresentation.java | 2 +- .../engine/application/StatusFilter.java | 2 +- .../engine/application/StatusInfo.java | 2 +- .../engine/application/StrictConneg.java | 2 +- .../engine/application/TunnelFilter.java | 2 +- .../restlet/engine/component/ClientRoute.java | 2 +- .../engine/component/ClientRouter.java | 2 +- .../component/ComponentClientDispatcher.java | 2 +- .../engine/component/ComponentContext.java | 2 +- .../engine/component/ComponentHelper.java | 2 +- .../component/ComponentServerDispatcher.java | 2 +- .../restlet/engine/component/HostRoute.java | 2 +- .../engine/component/InternalRouter.java | 2 +- .../engine/component/ServerRouter.java | 2 +- .../engine/connector/ClientHelper.java | 2 +- .../engine/connector/ConnectorHelper.java | 2 +- .../engine/connector/HttpClientHelper.java | 2 +- .../engine/connector/HttpProtocolHelper.java | 2 +- .../engine/connector/HttpServerHelper.java | 2 +- .../engine/connector/HttpsServerHelper.java | 2 +- .../engine/connector/JettyServerHelper.java | 2 +- .../org/restlet/engine/connector/Method.java | 2 +- .../engine/connector/ProtocolHelper.java | 2 +- .../engine/connector/ServerHelper.java | 2 +- .../engine/converter/ConverterHelper.java | 2 +- .../engine/converter/ConverterUtils.java | 2 +- .../engine/converter/DefaultConverter.java | 6 +- .../converter/StatusInfoHtmlConverter.java | 2 +- .../engine/header/CacheDirectiveReader.java | 2 +- .../engine/header/CacheDirectiveWriter.java | 2 +- .../engine/header/ChallengeRequestReader.java | 2 +- .../engine/header/ChallengeWriter.java | 2 +- .../restlet/engine/header/ContentType.java | 2 +- .../engine/header/ContentTypeReader.java | 2 +- .../restlet/engine/header/CookieReader.java | 2 +- .../engine/header/CookieSettingReader.java | 2 +- .../engine/header/CookieSettingWriter.java | 2 +- .../restlet/engine/header/CookieWriter.java | 2 +- .../org/restlet/engine/header/DateWriter.java | 2 +- .../engine/header/DimensionReader.java | 2 +- .../engine/header/DimensionWriter.java | 2 +- .../engine/header/DispositionReader.java | 2 +- .../engine/header/DispositionWriter.java | 2 +- .../restlet/engine/header/EncodingReader.java | 2 +- .../restlet/engine/header/EncodingWriter.java | 2 +- .../engine/header/ExpectationReader.java | 2 +- .../engine/header/ExpectationWriter.java | 2 +- .../engine/header/HeaderConstants.java | 2 +- .../restlet/engine/header/HeaderReader.java | 2 +- .../restlet/engine/header/HeaderUtils.java | 2 +- .../restlet/engine/header/HeaderWriter.java | 2 +- .../restlet/engine/header/LanguageReader.java | 2 +- .../restlet/engine/header/LanguageWriter.java | 2 +- .../restlet/engine/header/MetadataWriter.java | 2 +- .../restlet/engine/header/MethodReader.java | 2 +- .../restlet/engine/header/MethodWriter.java | 2 +- .../engine/header/PreferenceReader.java | 2 +- .../engine/header/PreferenceWriter.java | 2 +- .../restlet/engine/header/ProductReader.java | 2 +- .../restlet/engine/header/ProductWriter.java | 2 +- .../restlet/engine/header/RangeReader.java | 2 +- .../restlet/engine/header/RangeWriter.java | 2 +- .../engine/header/RecipientInfoReader.java | 2 +- .../engine/header/RecipientInfoWriter.java | 2 +- .../restlet/engine/header/StringReader.java | 2 +- .../restlet/engine/header/StringWriter.java | 2 +- .../org/restlet/engine/header/TagReader.java | 2 +- .../org/restlet/engine/header/TagWriter.java | 2 +- .../restlet/engine/header/TokenReader.java | 2 +- .../restlet/engine/header/WarningReader.java | 2 +- .../restlet/engine/header/WarningWriter.java | 2 +- .../restlet/engine/internal/Activator.java | 2 +- .../java/org/restlet/engine/io/IoUtils.java | 2 +- .../org/restlet/engine/io/PipeStream.java | 2 +- .../restlet/engine/io/RangeInputStream.java | 2 +- .../restlet/engine/io/ReaderInputStream.java | 2 +- .../engine/io/UnclosableInputStream.java | 2 +- .../engine/io/UnclosableOutputStream.java | 2 +- .../restlet/engine/io/WriterOutputStream.java | 2 +- .../engine/local/ClapClientHelper.java | 2 +- .../engine/local/DirectoryServerResource.java | 2 +- .../java/org/restlet/engine/local/Entity.java | 2 +- .../engine/local/EntityClientHelper.java | 2 +- .../engine/local/FileClientHelper.java | 2 +- .../org/restlet/engine/local/FileEntity.java | 2 +- .../engine/local/LocalClientHelper.java | 2 +- .../engine/local/RiapClientHelper.java | 2 +- .../engine/local/RiapServerHelper.java | 2 +- .../restlet/engine/local/ZipClientHelper.java | 2 +- .../restlet/engine/local/ZipEntryEntity.java | 2 +- .../engine/local/ZipEntryRepresentation.java | 2 +- .../engine/log/AccessLogFileHandler.java | 2 +- .../engine/log/AccessLogFormatter.java | 2 +- .../engine/log/DefaultAccessLogFormatter.java | 2 +- .../org/restlet/engine/log/IdentClient.java | 2 +- .../org/restlet/engine/log/LogFilter.java | 2 +- .../java/org/restlet/engine/log/LogUtils.java | 2 +- .../org/restlet/engine/log/LoggerFacade.java | 2 +- .../engine/log/LoggingThreadFactory.java | 2 +- .../restlet/engine/log/SimplerFormatter.java | 2 +- .../restlet/engine/log/SimplestFormatter.java | 2 +- .../engine/resource/AnnotationInfo.java | 2 +- .../engine/resource/AnnotationUtils.java | 2 +- .../resource/ClientInvocationHandler.java | 2 +- .../engine/resource/MethodAnnotationInfo.java | 2 +- .../resource/ThrowableAnnotationInfo.java | 2 +- .../restlet/engine/resource/VariantInfo.java | 2 +- .../engine/security/AuthenticatorHelper.java | 2 +- .../engine/security/AuthenticatorUtils.java | 2 +- .../engine/security/HttpBasicHelper.java | 2 +- .../RestletSslContextFactoryClient.java | 2 +- .../RestletSslContextFactoryServer.java | 2 +- .../restlet/engine/security/RoleMapping.java | 2 +- .../restlet/engine/ssl/DefaultSslContext.java | 2 +- .../engine/ssl/DefaultSslContextFactory.java | 2 +- .../restlet/engine/ssl/SslContextFactory.java | 2 +- .../java/org/restlet/engine/ssl/SslUtils.java | 2 +- .../engine/ssl/WrapperSslContextSpi.java | 2 +- .../ssl/WrapperSslServerSocketFactory.java | 2 +- .../engine/ssl/WrapperSslSocketFactory.java | 2 +- .../engine/util/AlphaNumericComparator.java | 2 +- .../engine/util/AlphabeticalComparator.java | 2 +- .../restlet/engine/util/BeanInfoUtils.java | 2 +- .../org/restlet/engine/util/CallResolver.java | 2 +- .../engine/util/CaseInsensitiveHashSet.java | 2 +- .../engine/util/ChildClientDispatcher.java | 2 +- .../org/restlet/engine/util/ChildContext.java | 2 +- .../engine/util/ContextualRunnable.java | 2 +- .../org/restlet/engine/util/DateUtils.java | 2 +- .../engine/util/DefaultSaxHandler.java | 2 +- .../engine/util/EngineClassLoader.java | 2 +- .../org/restlet/engine/util/FormReader.java | 88 ++------ .../org/restlet/engine/util/FormUtils.java | 2 +- .../restlet/engine/util/ImmutableDate.java | 2 +- .../engine/util/InternetDateFormat.java | 2 +- .../org/restlet/engine/util/ListUtils.java | 2 +- .../org/restlet/engine/util/MapResolver.java | 2 +- .../java/org/restlet/engine/util/Pool.java | 2 +- .../restlet/engine/util/ReferenceUtils.java | 2 +- .../org/restlet/engine/util/SetUtils.java | 2 +- .../org/restlet/engine/util/StringUtils.java | 2 +- .../org/restlet/engine/util/SystemUtils.java | 2 +- .../engine/util/TemplateDispatcher.java | 2 +- .../util/WrapperScheduledExecutorService.java | 2 +- .../AppendableRepresentation.java | 2 +- .../BufferingRepresentation.java | 2 +- .../ByteArrayRepresentation.java | 2 +- .../CharacterRepresentation.java | 2 +- .../DigesterRepresentation.java | 2 +- .../representation/EmptyRepresentation.java | 2 +- .../representation/FileRepresentation.java | 2 +- .../representation/InputRepresentation.java | 2 +- .../MultiPartRepresentation.java | 2 +- .../representation/ObjectRepresentation.java | 2 +- .../representation/OutputRepresentation.java | 2 +- .../representation/ReaderRepresentation.java | 2 +- .../representation/Representation.java | 2 +- .../representation/RepresentationInfo.java | 2 +- .../representation/StreamRepresentation.java | 2 +- .../representation/StringRepresentation.java | 2 +- .../org/restlet/representation/Variant.java | 2 +- .../representation/WriterRepresentation.java | 2 +- .../org/restlet/resource/ClientProxy.java | 2 +- .../org/restlet/resource/ClientResource.java | 2 +- .../java/org/restlet/resource/Delete.java | 2 +- .../java/org/restlet/resource/Directory.java | 2 +- .../java/org/restlet/resource/Finder.java | 2 +- .../main/java/org/restlet/resource/Get.java | 2 +- .../java/org/restlet/resource/Options.java | 2 +- .../main/java/org/restlet/resource/Patch.java | 2 +- .../main/java/org/restlet/resource/Post.java | 2 +- .../main/java/org/restlet/resource/Put.java | 2 +- .../java/org/restlet/resource/Resource.java | 2 +- .../restlet/resource/ResourceException.java | 2 +- .../java/org/restlet/resource/Result.java | 2 +- .../org/restlet/resource/ServerResource.java | 2 +- .../java/org/restlet/resource/Status.java | 2 +- .../java/org/restlet/routing/Extractor.java | 2 +- .../main/java/org/restlet/routing/Filter.java | 2 +- .../java/org/restlet/routing/Redirector.java | 2 +- .../main/java/org/restlet/routing/Route.java | 2 +- .../main/java/org/restlet/routing/Router.java | 2 +- .../java/org/restlet/routing/Template.java | 2 +- .../org/restlet/routing/TemplateRoute.java | 2 +- .../java/org/restlet/routing/Validator.java | 2 +- .../java/org/restlet/routing/Variable.java | 2 +- .../java/org/restlet/routing/VirtualHost.java | 2 +- .../org/restlet/security/Authenticator.java | 2 +- .../java/org/restlet/security/Authorizer.java | 2 +- .../security/CertificateAuthenticator.java | 2 +- .../security/ChallengeAuthenticator.java | 2 +- .../security/ConfidentialAuthorizer.java | 2 +- .../java/org/restlet/security/Enroler.java | 2 +- .../main/java/org/restlet/security/Group.java | 2 +- .../org/restlet/security/LocalVerifier.java | 2 +- .../org/restlet/security/MapVerifier.java | 2 +- .../org/restlet/security/MemoryRealm.java | 2 +- .../restlet/security/MethodAuthorizer.java | 2 +- .../main/java/org/restlet/security/Realm.java | 2 +- .../main/java/org/restlet/security/Role.java | 2 +- .../org/restlet/security/RoleAuthorizer.java | 2 +- .../org/restlet/security/SecretVerifier.java | 2 +- .../main/java/org/restlet/security/User.java | 2 +- .../java/org/restlet/security/Verifier.java | 2 +- .../org/restlet/service/ConnectorService.java | 2 +- .../org/restlet/service/ConnegService.java | 2 +- .../org/restlet/service/ConverterService.java | 2 +- .../java/org/restlet/service/CorsService.java | 2 +- .../org/restlet/service/DecoderService.java | 2 +- .../org/restlet/service/EncoderService.java | 2 +- .../java/org/restlet/service/LogService.java | 2 +- .../org/restlet/service/MetadataService.java | 2 +- .../org/restlet/service/RangeService.java | 2 +- .../java/org/restlet/service/Service.java | 2 +- .../org/restlet/service/StatusService.java | 2 +- .../java/org/restlet/service/TaskService.java | 2 +- .../org/restlet/service/TunnelService.java | 2 +- .../java/org/restlet/util/ClientList.java | 2 +- .../java/org/restlet/util/NamedValue.java | 2 +- .../restlet/util/NamedValuesCollector.java | 85 +++++++ .../main/java/org/restlet/util/Resolver.java | 2 +- .../main/java/org/restlet/util/RouteList.java | 2 +- .../main/java/org/restlet/util/Series.java | 52 +---- .../java/org/restlet/util/ServerList.java | 2 +- .../java/org/restlet/util/ServiceList.java | 2 +- .../java/org/restlet/util/WrapperList.java | 31 ++- .../java/org/restlet/util/WrapperMap.java | 2 +- .../restlet/util/WrapperRepresentation.java | 2 +- .../java/org/restlet/util/WrapperRequest.java | 2 +- .../org/restlet/util/WrapperResponse.java | 2 +- .../java/org/restlet/util/WrapperRestlet.java | 2 +- .../restlet/ApplicationContextTestCase.java | 2 +- .../java/org/restlet/Bug1145TestCase.java | 2 +- .../test/java/org/restlet/CallTestCase.java | 2 +- .../java/org/restlet/RestartTestCase.java | 2 +- .../data/AuthenticationInfoTestCase.java | 2 +- .../org/restlet/data/ClientInfoTestCase.java | 2 +- .../java/org/restlet/data/CookieTestCase.java | 2 +- .../org/restlet/data/FileClientTestCase.java | 2 +- .../restlet/data/FileReferenceTestCase.java | 2 +- .../java/org/restlet/data/FormTestCase.java | 2 +- .../org/restlet/data/LanguageTestCase.java | 2 +- .../org/restlet/data/MediaTypeTestCase.java | 2 +- .../java/org/restlet/data/MethodTestCase.java | 2 +- .../restlet/data/ProductTokenTestCase.java | 2 +- .../java/org/restlet/data/RangeTestCase.java | 2 +- .../restlet/data/RecipientInfoTestCase.java | 2 +- .../org/restlet/data/ReferenceTestCase.java | 2 +- .../restlet/data/RiapConnectorsTestCase.java | 2 +- .../java/org/restlet/data/RiapTestCase.java | 2 +- .../java/org/restlet/data/StatusTestCase.java | 2 +- .../java/org/restlet/data/TagTestCase.java | 2 +- .../org/restlet/data/ZipClientTestCase.java | 2 +- .../java/org/restlet/engine/EngineTest.java | 2 +- .../CorsResponseFilterTestCase.java | 2 +- .../RangeRepresentationTestCase.java | 2 +- .../application/TunnelFilterTestCase.java | 2 +- .../engine/connector/AsyncTestCase.java | 2 +- .../connector/BaseConnectorsTestCase.java | 2 +- .../connector/ChunkedEncodingPutTestCase.java | 2 +- .../connector/ChunkedEncodingTestCase.java | 2 +- .../engine/connector/GetChunkedTestCase.java | 2 +- .../connector/GetQueryParamTestCase.java | 2 +- .../restlet/engine/connector/GetTestCase.java | 2 +- .../HttpTransportProtocolsTestCase.java | 2 +- .../MultiPartRepresentationTestCase.java | 2 +- .../engine/connector/PostPutTestCase.java | 2 +- .../RemoteClientAddressTestCase.java | 2 +- .../ServerMaxConnectionsTestCase.java | 2 +- .../connector/ShutdownHookTestCase.java | 10 +- .../connector/SslBaseConnectorsTestCase.java | 2 +- .../SslClientContextGetTestCase.java | 2 +- .../engine/connector/SslGetTestCase.java | 2 +- .../engine/header/ContentTypeTestCase.java | 2 +- .../engine/header/CookieReaderTestCase.java | 2 +- .../header/DispositionReaderTestCase.java | 2 +- .../header/DispositionWriterTestCase.java | 2 +- .../restlet/engine/header/HeaderTestCase.java | 2 +- .../engine/header/HeaderUtilsTestCase.java | 2 +- .../header/PreferenceReaderTestCase.java | 2 +- .../engine/header/RangeReaderTestCase.java | 2 +- .../restlet/engine/io/IoUtilsTestCase.java | 2 +- .../engine/io/ReaderInputStreamTestCase.java | 2 +- .../io/UnclosableInputStreamTestCase.java | 2 +- .../io/UnclosableOutputStreamTestCase.java | 2 +- .../AbstractAnnotatedServerResource03.java | 2 +- .../jetty/resource/AnnotatedInterface03.java | 2 +- .../resource/AnnotatedInterface03_01.java | 2 +- .../resource/AnnotatedInterface03_02.java | 2 +- .../resource/AnnotatedResource09TestCase.java | 2 +- .../resource/AnnotatedResource10TestCase.java | 2 +- .../resource/AnnotatedResource11TestCase.java | 2 +- .../resource/JettyConnectorTestCase.java | 2 +- .../engine/jetty/resource/MyResource09.java | 2 +- .../engine/jetty/resource/MyResource10.java | 2 +- .../engine/jetty/resource/MyResource11.java | 2 +- .../engine/jetty/resource/SIMethod.java | 2 +- .../engine/jetty/resource/SNIMethod.java | 2 +- .../engine/jetty/resource/USIMethod.java | 2 +- .../engine/jetty/resource/USNIMethod.java | 2 +- .../resource/AnnotationUtilsTestCase.java | 2 +- .../util/AlphaNumericComparatorTestCase.java | 2 +- .../engine/util/DateUtilsTestCase.java | 2 +- .../restlet/engine/util/FormReaderTest.java | 208 ++++++++++++++++++ .../engine/util/HtmlEncodingTestCase.java | 2 +- .../AppendableRepresentationTestCase.java | 2 +- .../DigesterRepresentationTestCase.java | 2 +- .../FileRepresentationTestCase.java | 2 +- .../AbstractAnnotatedResourceTestCase.java | 2 +- ...ctAnnotatedResourceWithFinderTestCase.java | 2 +- ...bstractGenericAnnotatedServerResource.java | 2 +- .../resource/AnnotatedResource01TestCase.java | 2 +- .../resource/AnnotatedResource02TestCase.java | 2 +- .../resource/AnnotatedResource03TestCase.java | 2 +- .../resource/AnnotatedResource04TestCase.java | 2 +- .../resource/AnnotatedResource05TestCase.java | 2 +- .../resource/AnnotatedResource06TestCase.java | 2 +- .../resource/AnnotatedResource07TestCase.java | 2 +- .../resource/AnnotatedResource08TestCase.java | 2 +- .../resource/AnnotatedResource12TestCase.java | 2 +- .../resource/AnnotatedResource13TestCase.java | 2 +- .../resource/AnnotatedResource14TestCase.java | 2 +- .../AnnotatedResource15OkTestCase.java | 2 +- .../resource/AnnotatedResource15TestCase.java | 2 +- .../resource/AnnotatedResource16TestCase.java | 2 +- .../resource/AnnotatedResource17TestCase.java | 2 +- .../resource/AnnotatedResource18TestCase.java | 2 +- .../resource/AnnotatedResource20TestCase.java | 2 +- .../restlet/resource/DirectoryTestCase.java | 2 +- .../GenericAnnotatedServerResource.java | 2 +- .../resource/GenericServerResource16.java | 2 +- .../resource/GenericServerResource17.java | 2 +- .../java/org/restlet/resource/MyBean.java | 2 +- .../org/restlet/resource/MyException01.java | 2 +- .../org/restlet/resource/MyException02.java | 2 +- .../org/restlet/resource/MyResource01.java | 2 +- .../org/restlet/resource/MyResource02.java | 2 +- .../org/restlet/resource/MyResource03.java | 2 +- .../org/restlet/resource/MyResource04.java | 2 +- .../org/restlet/resource/MyResource05.java | 2 +- .../org/restlet/resource/MyResource06.java | 2 +- .../org/restlet/resource/MyResource07.java | 2 +- .../org/restlet/resource/MyResource08.java | 2 +- .../org/restlet/resource/MyResource12.java | 2 +- .../org/restlet/resource/MyResource17.java | 2 +- .../org/restlet/resource/MyResource20.java | 2 +- .../restlet/resource/MyServerResource01.java | 2 +- .../restlet/resource/MyServerResource12.java | 2 +- .../restlet/resource/MyServerResource15.java | 2 +- .../resource/MyServerResource15Ok.java | 2 +- .../restlet/resource/MyServerResource16.java | 2 +- .../restlet/resource/MyServerResource17.java | 2 +- .../restlet/resource/MyServerResource18.java | 2 +- .../restlet/resource/MyServerResource20.java | 2 +- .../routing/AbstractFilterTestCase.java | 2 +- .../org/restlet/routing/FilterTestCase.java | 2 +- .../java/org/restlet/routing/MockFilter.java | 2 +- .../java/org/restlet/routing/MockRestlet.java | 2 +- .../org/restlet/routing/RedirectTestCase.java | 2 +- .../restlet/routing/RouteListTestCase.java | 2 +- .../org/restlet/routing/TemplateTestCase.java | 2 +- .../org/restlet/routing/TraceRestlet.java | 2 +- .../restlet/routing/ValidatorTestCase.java | 2 +- .../restlet/security/HelloWorldRestlet.java | 2 +- .../restlet/security/HttpBasicTestCase.java | 2 +- .../org/restlet/security/MemoryRealmTest.java | 2 +- .../org/restlet/security/RoleTestCase.java | 2 +- .../org/restlet/security/SaasApplication.java | 2 +- .../org/restlet/security/SaasComponent.java | 2 +- .../restlet/security/SecurityTestCase.java | 2 +- .../service/ConnegServiceTestCase.java | 2 +- .../service/MetadataServiceTestCase.java | 2 +- .../service/StatusServiceTestCase.java | 2 +- .../service/UserAgentTestResource.java | 2 +- .../UserAgentTunnelFilterTestCase.java | 2 +- .../java/org/restlet/util/SeriesTest.java | 172 +++++++++++++++ 541 files changed, 1044 insertions(+), 674 deletions(-) create mode 100644 org.restlet/src/main/java/org/restlet/util/NamedValuesCollector.java create mode 100644 org.restlet/src/test/java/org/restlet/engine/util/FormReaderTest.java create mode 100644 org.restlet/src/test/java/org/restlet/util/SeriesTest.java diff --git a/copyright.md b/copyright.md index d764ff95bb..61357b1dcc 100644 --- a/copyright.md +++ b/copyright.md @@ -3,7 +3,7 @@ Copyright notice Version 5.0, October 2024 -Copyright 2005-2024 Qlik +Copyright 2005-2026 Qlik The contents of this open source project are subject to the terms of the Apache 2.0 open source license available at http://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java index b3dec60f05..d5b448f9e3 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/AwsAuthenticator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java index e49e4afa94..cb38076388 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/CookieAuthenticator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java index 03659e87b1..7a6c959867 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestAuthenticator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java index 1a0925a69a..16cf37e8d1 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java index a411cd9724..f5f4cd92be 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/DigestVerifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java index be42c85cc7..5d6770be96 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java index d75e94a8a5..ae5548d0f0 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/AwsVerifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java index 9684a6a7e0..1534462157 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/CryptoUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java index 2297026d01..d2e5747c8e 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java index a9e66ffac6..7097b396cb 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAwsS3Helper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java index 017099b390..777fbf7858 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java index fc55fcebda..ba42b724e5 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java index b4e4ff58af..086fede101 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java index 2321d54c0f..f19fa60056 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestVerifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java index 9ca3492a3a..f34d6d9d54 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java index dea8f70856..34a23ad672 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java index 6655877873..1092c68402 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java index 02a2a7342c..6c37f968d3 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java index 7f37b39837..1f293045af 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java index 6cebc54389..9e398533df 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java index 9f0bee8f21..d8b33a3a91 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java index 01c7abc381..74cb7c0ee8 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java index c0e5bee590..2b7bdf9ca4 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/ContextTemplateLoader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java index c5957e5a4b..823c38f8e9 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/FreemarkerConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java index 287477a760..8c5e7b4c56 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java index de0a4cccf9..0d02d10535 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/TemplateRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java index 7ce1e3f7f3..abcba8319f 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ResolverHashModel.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java index 9abfc66a6e..ee44773146 100644 --- a/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java +++ b/org.restlet.ext.freemarker/src/main/java/org/restlet/ext/freemarker/internal/ScalarModel.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java index 78c1b3a0a9..804965c553 100644 --- a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java index de2a8f098a..4cec96e1b8 100644 --- a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java index ecffae14ce..8397361ca0 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java index b2a41da8ac..9ba5dae9d3 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java index 6f7f448465..9a5dcfd898 100644 --- a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java +++ b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java index 72de5cd9ea..7cfe1bbdc7 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java index c73c22d0f7..fe0daa4dcf 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java index f06d6622f6..f3909a2137 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/JaasVerifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java index 6e4fe1b215..562618c1e5 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java index 2a02815499..bdb7c5f311 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java index 3a5ad836ba..374dada276 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/internal/XmlFactoryProvider.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java index afa70a5709..4c7873fb7b 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Customer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java index 0c46b7a397..b1da7abd0a 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/Invoice.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java index b5e7a122aa..e26cccadfe 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java index a89a5119d8..2519fae704 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/MyException.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java index 533bd44d64..e123956c3e 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java index 0956e66e7f..d3eea56b13 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java index 58d71d5860..b6f428a7de 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java index 2fd58226ad..8b50db8aae 100644 --- a/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java +++ b/org.restlet.ext.json/src/main/java/org/restlet/ext/json/JsonpRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java index b355045015..7b27595e9a 100644 --- a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java index fe6852972d..1cd2b828d1 100644 --- a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java index 81df37f1b1..b918cc398a 100644 --- a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java +++ b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLogger.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java index 6e7e2652c3..31f8c61b99 100644 --- a/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java +++ b/org.restlet.ext.slf4j/src/main/java/org/restlet/ext/slf4j/Slf4jLoggerFacade.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java index 3dba7e7376..7e542fb48f 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanFinder.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java index b0226063e8..f66f988f91 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringBeanRouter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java index a89552c1b2..43ade660ce 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java index 10ebf62cd0..3dd4a3abf9 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringContext.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java index af4bc1ce9d..fef6481175 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringFinder.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java index 0ff27d3dfb..436927da36 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringHost.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java index 08ae2315bc..b44ac07203 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java index a919923a04..c091837b56 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringRouter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java index 19d0da7b37..4f51f904fd 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringServer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java index b34925811e..cfea3f6c72 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java index 882871f61a..4fb34aa174 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java index 6ec21ffdae..a8b05271bf 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java index 79e0ae2b1d..6a1660f7df 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrderResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java index 6eabc73585..c5ddac3221 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/OrdersResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java index 44c2dfdd36..0acae60057 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/resources/UserResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java index 75e49efed3..f1c05f4d4c 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java index febe93ef3e..32c2c6a9fd 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java index b368ca0eeb..05e93327f7 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java index 2848050f21..2b2be67715 100644 --- a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java +++ b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java index cd7a57f55c..b25b0cee91 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/RepresentationResourceLoader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java index 13606a61ff..7047860c74 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java index da8131b040..8ecf647104 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java index 64e29bee1e..4bde848a59 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java index 10c753b9c8..918d4808ba 100644 --- a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java +++ b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java index b82aa15af9..b81ba7463f 100644 --- a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java +++ b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java index 9e38350132..cd2e0049c3 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/DomRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java index 62cac6381c..7700c89569 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/NodeList.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java index dcba96dd9d..bb81fe078b 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java index 3e6e39ab72..89c7b23bee 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java index 930edba0a1..22003d312f 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/Transformer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java index 5994a36d06..af9b6ecb88 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java index d5c12b0c19..71921ae941 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java index 1eefa6e1a2..bf30f98992 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java index 7d3ca76aa8..d0b7410849 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/AbstractXmlReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java index 8a0d505964..9d79f1f7e2 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/internal/ContextResolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java index 203f577cdb..05cf15e8ef 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java index c0cc337ffc..ee351583de 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java index 499d08fe7c..ab28dee2de 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Application.java b/org.restlet/src/main/java/org/restlet/Application.java index ad5ed4c35d..bf4b119219 100644 --- a/org.restlet/src/main/java/org/restlet/Application.java +++ b/org.restlet/src/main/java/org/restlet/Application.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Client.java b/org.restlet/src/main/java/org/restlet/Client.java index 4205ff9710..79d008356b 100644 --- a/org.restlet/src/main/java/org/restlet/Client.java +++ b/org.restlet/src/main/java/org/restlet/Client.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Component.java b/org.restlet/src/main/java/org/restlet/Component.java index 6681fa42ce..45d7d659bc 100644 --- a/org.restlet/src/main/java/org/restlet/Component.java +++ b/org.restlet/src/main/java/org/restlet/Component.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Connector.java b/org.restlet/src/main/java/org/restlet/Connector.java index 9810ac6917..6ca4f34750 100644 --- a/org.restlet/src/main/java/org/restlet/Connector.java +++ b/org.restlet/src/main/java/org/restlet/Connector.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Context.java b/org.restlet/src/main/java/org/restlet/Context.java index 86b727ccd9..6afb3b08aa 100644 --- a/org.restlet/src/main/java/org/restlet/Context.java +++ b/org.restlet/src/main/java/org/restlet/Context.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Message.java b/org.restlet/src/main/java/org/restlet/Message.java index b84dcb09c3..44aded1c44 100644 --- a/org.restlet/src/main/java/org/restlet/Message.java +++ b/org.restlet/src/main/java/org/restlet/Message.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Request.java b/org.restlet/src/main/java/org/restlet/Request.java index e0f558276b..488f724934 100644 --- a/org.restlet/src/main/java/org/restlet/Request.java +++ b/org.restlet/src/main/java/org/restlet/Request.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Response.java b/org.restlet/src/main/java/org/restlet/Response.java index f1e5819ef3..f13a9cf05b 100644 --- a/org.restlet/src/main/java/org/restlet/Response.java +++ b/org.restlet/src/main/java/org/restlet/Response.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Restlet.java b/org.restlet/src/main/java/org/restlet/Restlet.java index 139538a5e6..086f557eb8 100644 --- a/org.restlet/src/main/java/org/restlet/Restlet.java +++ b/org.restlet/src/main/java/org/restlet/Restlet.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Server.java b/org.restlet/src/main/java/org/restlet/Server.java index 055fc952a2..21db1d8b16 100644 --- a/org.restlet/src/main/java/org/restlet/Server.java +++ b/org.restlet/src/main/java/org/restlet/Server.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/Uniform.java b/org.restlet/src/main/java/org/restlet/Uniform.java index 4584aea91f..6870439d6c 100644 --- a/org.restlet/src/main/java/org/restlet/Uniform.java +++ b/org.restlet/src/main/java/org/restlet/Uniform.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java b/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java index 50759d3c47..fe95757f20 100644 --- a/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/AuthenticationInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/CacheDirective.java b/org.restlet/src/main/java/org/restlet/data/CacheDirective.java index c6a932c167..9165154b98 100644 --- a/org.restlet/src/main/java/org/restlet/data/CacheDirective.java +++ b/org.restlet/src/main/java/org/restlet/data/CacheDirective.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java index 2c29ef3713..f93d25ad2e 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeMessage.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java index 5a3a30b400..0229901fe6 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java index 8b05ae6ee4..4ebaf8be23 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java b/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java index 323705eeef..282b4fb575 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeScheme.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java index 5c9a042f1b..0a0beeee39 100644 --- a/org.restlet/src/main/java/org/restlet/data/CharacterSet.java +++ b/org.restlet/src/main/java/org/restlet/data/CharacterSet.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java index 5455f574e4..3fba8ca3db 100644 --- a/org.restlet/src/main/java/org/restlet/data/ClientInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/ClientInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Conditions.java b/org.restlet/src/main/java/org/restlet/data/Conditions.java index 4d41a8fb5c..cab4802b82 100644 --- a/org.restlet/src/main/java/org/restlet/data/Conditions.java +++ b/org.restlet/src/main/java/org/restlet/data/Conditions.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Cookie.java b/org.restlet/src/main/java/org/restlet/data/Cookie.java index b42e290e76..70de428008 100644 --- a/org.restlet/src/main/java/org/restlet/data/Cookie.java +++ b/org.restlet/src/main/java/org/restlet/data/Cookie.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java index 622aad2219..bf01add5b0 100644 --- a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java +++ b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Digest.java b/org.restlet/src/main/java/org/restlet/data/Digest.java index 2b611aef08..f780680150 100644 --- a/org.restlet/src/main/java/org/restlet/data/Digest.java +++ b/org.restlet/src/main/java/org/restlet/data/Digest.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Dimension.java b/org.restlet/src/main/java/org/restlet/data/Dimension.java index 82ee020ca7..cd309d3a1b 100644 --- a/org.restlet/src/main/java/org/restlet/data/Dimension.java +++ b/org.restlet/src/main/java/org/restlet/data/Dimension.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Disposition.java b/org.restlet/src/main/java/org/restlet/data/Disposition.java index 0fe519bf16..e1ad0fb54f 100644 --- a/org.restlet/src/main/java/org/restlet/data/Disposition.java +++ b/org.restlet/src/main/java/org/restlet/data/Disposition.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Encoding.java b/org.restlet/src/main/java/org/restlet/data/Encoding.java index 336e9a7946..2b575eaf7c 100644 --- a/org.restlet/src/main/java/org/restlet/data/Encoding.java +++ b/org.restlet/src/main/java/org/restlet/data/Encoding.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Expectation.java b/org.restlet/src/main/java/org/restlet/data/Expectation.java index b7b8c1c94b..6e098048d5 100644 --- a/org.restlet/src/main/java/org/restlet/data/Expectation.java +++ b/org.restlet/src/main/java/org/restlet/data/Expectation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Form.java b/org.restlet/src/main/java/org/restlet/data/Form.java index baf8fcb062..dc7632aa3d 100644 --- a/org.restlet/src/main/java/org/restlet/data/Form.java +++ b/org.restlet/src/main/java/org/restlet/data/Form.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Header.java b/org.restlet/src/main/java/org/restlet/data/Header.java index b0fd0934a6..7d4528e82a 100644 --- a/org.restlet/src/main/java/org/restlet/data/Header.java +++ b/org.restlet/src/main/java/org/restlet/data/Header.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Language.java b/org.restlet/src/main/java/org/restlet/data/Language.java index b96d6bb6ec..cc8cd1badf 100644 --- a/org.restlet/src/main/java/org/restlet/data/Language.java +++ b/org.restlet/src/main/java/org/restlet/data/Language.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/LocalReference.java b/org.restlet/src/main/java/org/restlet/data/LocalReference.java index 9f48c60936..be4cb8d4aa 100644 --- a/org.restlet/src/main/java/org/restlet/data/LocalReference.java +++ b/org.restlet/src/main/java/org/restlet/data/LocalReference.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/MediaType.java b/org.restlet/src/main/java/org/restlet/data/MediaType.java index efe7bd7e82..185fb30786 100644 --- a/org.restlet/src/main/java/org/restlet/data/MediaType.java +++ b/org.restlet/src/main/java/org/restlet/data/MediaType.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Metadata.java b/org.restlet/src/main/java/org/restlet/data/Metadata.java index ffa4a2d9db..9bc1ac9bef 100644 --- a/org.restlet/src/main/java/org/restlet/data/Metadata.java +++ b/org.restlet/src/main/java/org/restlet/data/Metadata.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Method.java b/org.restlet/src/main/java/org/restlet/data/Method.java index 097066956e..378d1cff1e 100644 --- a/org.restlet/src/main/java/org/restlet/data/Method.java +++ b/org.restlet/src/main/java/org/restlet/data/Method.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Parameter.java b/org.restlet/src/main/java/org/restlet/data/Parameter.java index eb9ac9d84d..afd199c605 100644 --- a/org.restlet/src/main/java/org/restlet/data/Parameter.java +++ b/org.restlet/src/main/java/org/restlet/data/Parameter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Preference.java b/org.restlet/src/main/java/org/restlet/data/Preference.java index 330d3a4c5c..72b2f01375 100644 --- a/org.restlet/src/main/java/org/restlet/data/Preference.java +++ b/org.restlet/src/main/java/org/restlet/data/Preference.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Product.java b/org.restlet/src/main/java/org/restlet/data/Product.java index f21e438384..d3c11731dc 100644 --- a/org.restlet/src/main/java/org/restlet/data/Product.java +++ b/org.restlet/src/main/java/org/restlet/data/Product.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Protocol.java b/org.restlet/src/main/java/org/restlet/data/Protocol.java index b9ee9627ba..04e0d034ac 100644 --- a/org.restlet/src/main/java/org/restlet/data/Protocol.java +++ b/org.restlet/src/main/java/org/restlet/data/Protocol.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Range.java b/org.restlet/src/main/java/org/restlet/data/Range.java index 70160b5446..fccc3d5343 100644 --- a/org.restlet/src/main/java/org/restlet/data/Range.java +++ b/org.restlet/src/main/java/org/restlet/data/Range.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java b/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java index 085a788bcb..011941c3f3 100644 --- a/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/RecipientInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Reference.java b/org.restlet/src/main/java/org/restlet/data/Reference.java index bd32a08d59..6355870f37 100644 --- a/org.restlet/src/main/java/org/restlet/data/Reference.java +++ b/org.restlet/src/main/java/org/restlet/data/Reference.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java index 53d5e0bda1..55896cacf7 100644 --- a/org.restlet/src/main/java/org/restlet/data/ReferenceList.java +++ b/org.restlet/src/main/java/org/restlet/data/ReferenceList.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/ServerInfo.java b/org.restlet/src/main/java/org/restlet/data/ServerInfo.java index 9653d56bac..75264de959 100644 --- a/org.restlet/src/main/java/org/restlet/data/ServerInfo.java +++ b/org.restlet/src/main/java/org/restlet/data/ServerInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Status.java b/org.restlet/src/main/java/org/restlet/data/Status.java index 3e43337320..3347dd8198 100644 --- a/org.restlet/src/main/java/org/restlet/data/Status.java +++ b/org.restlet/src/main/java/org/restlet/data/Status.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Tag.java b/org.restlet/src/main/java/org/restlet/data/Tag.java index daa216d59f..4b0b6854a6 100644 --- a/org.restlet/src/main/java/org/restlet/data/Tag.java +++ b/org.restlet/src/main/java/org/restlet/data/Tag.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/data/Warning.java b/org.restlet/src/main/java/org/restlet/data/Warning.java index 3b9599edda..adc5f678ff 100644 --- a/org.restlet/src/main/java/org/restlet/data/Warning.java +++ b/org.restlet/src/main/java/org/restlet/data/Warning.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java index 348e1fe452..1d185ce5a0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/Edition.java b/org.restlet/src/main/java/org/restlet/engine/Edition.java index 5efd1df29a..c88e91e1ca 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Edition.java +++ b/org.restlet/src/main/java/org/restlet/engine/Edition.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/Engine.java b/org.restlet/src/main/java/org/restlet/engine/Engine.java index b77d351bb7..2874e73da2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Engine.java +++ b/org.restlet/src/main/java/org/restlet/engine/Engine.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/Helper.java b/org.restlet/src/main/java/org/restlet/engine/Helper.java index 801c615bbf..52275aeeef 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Helper.java +++ b/org.restlet/src/main/java/org/restlet/engine/Helper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java b/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java index e65dfc0a78..71b77a74e0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/RestletHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java index 4d9cc47e9c..38bbabd770 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/Adapter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java index fd3e09f029..cd513dc79a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/Call.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java index 844294ee71..cee7ea2a59 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java index 3821b336a9..8bce9a0bd0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientCall.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java index 6d2f538a6d..7d78746986 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java index 7f7fff90e5..c191320323 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpRequest.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java index 5fb3cef68c..2b088a2f76 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpResponse.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java index 72a509355b..bb2a032d9c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/HttpServerHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java index 50398660b5..2eea2fec83 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyClientCall.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java index cd07ca4706..a0df627ab8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyHandler.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java index 600389d2d6..9d2fdad5f8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/JettyServerCall.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java index beb45358f4..646918b348 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerAdapter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java index 04d8ec5a83..c3b1ed114a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ServerCall.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java b/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java index 549ecb1bd5..2aa0e774b8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/ApplicationHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java b/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java index 025de6fbb4..c1c69dc787 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Conneg.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java index 1c58948f2f..2b20acd9b4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/CorsFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java index 5b6c98ae20..381c800b05 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/CorsResponseHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java index 8340e65443..3b8e5cad6b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/DecodeRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java b/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java index df577a18a1..9cc04dec9d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Decoder.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java index eabdb44bed..8177f12faa 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java b/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java index 7f83cbb7f9..c2821d9529 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/Encoder.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java index 4fb6b29e31..dd1efbe768 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java b/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java index 8802bc5f33..8f621ee592 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/MetadataExtension.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java index ed0dcb6058..4be55ad554 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/RangeFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java index 21d93f7087..b9bb54bce9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/RangeRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java index 4503a5fa3c..6cb7531a82 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StatusFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java b/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java index 4baf352bdb..8c2dc59dd7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StatusInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java index 3d18f60e76..be1cdffb77 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java b/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java index 64a4f2c730..c410a6fac1 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/TunnelFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java b/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java index 78aeb4973f..de5adc3c58 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ClientRoute.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java index a2e176c176..7dc7fb3c8c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ClientRouter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java index 5c02cebf23..5197dda7f2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentClientDispatcher.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java index b6ac2822c9..7f7cc51f9b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentContext.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java index eb88cd961b..517922b9a5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java index 5e7adbd257..144067e60c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ComponentServerDispatcher.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java index 5c2be68f8a..67a85d4317 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/HostRoute.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java index a75f0a5606..5d5f45d6d6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/InternalRouter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java b/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java index fc04f2c7bc..22801ef188 100644 --- a/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java +++ b/org.restlet/src/main/java/org/restlet/engine/component/ServerRouter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java index 891befa915..7d2d4fe425 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java index 6b7daee07d..84ec7396e7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java index 9162920a15..ae03874126 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java index 149eea4197..948a86643c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpProtocolHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java index 267dedd941..929e6c16cf 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpServerHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java index 0e33fb8da2..64ebdd6610 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java index 396374ed4e..5c8ba81d62 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/Method.java b/org.restlet/src/main/java/org/restlet/engine/connector/Method.java index d0acb00900..306942bef7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/Method.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/Method.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java index 85b281cc16..db17f4ffab 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ProtocolHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java index f5e28595ed..dac9e300cb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ServerHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java index 815769f5a0..c96becad43 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java index ac4867d130..cdf76b9272 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/ConverterUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java index 8eb1e973e3..689c3bc2b2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/DefaultConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 @@ -77,7 +77,9 @@ public List> getObjectClasses(Variant source) { @Override public List getVariants(Class source) { - if (source == null) { return List.of(); } + if (source == null) { + return List.of(); + } List result = null; if (String.class.isAssignableFrom(source) diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java index 437f2103c7..feb3969893 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java index f4da652c2a..f00fd1aac7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java index 5b48c5e585..9d92c9a0b4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CacheDirectiveWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java index fcf8fbd60c..1783589cd6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeRequestReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java index 73d9c50d6f..e6fccc6168 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ChallengeWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java b/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java index cdcca5c141..aa39df8c1e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ContentType.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java index b3c55883a6..809ff61aec 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ContentTypeReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java index 078ef7ac6c..ac95aa4723 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java index 1c7ec00050..ea2efeb471 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java index 2702425eb9..2ca6d6dc3d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieSettingWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java index 6ad145d210..ec8c968299 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/CookieWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java index fd265ffb6b..4cc0d8a27c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java b/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java index 5fb19c8427..4c0ef53ae6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DimensionReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java index 063d475da9..19d1d771b5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DimensionWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java b/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java index 68cd4dbbbf..d38eb763cd 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DispositionReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java index 4082811ca2..e5a740466f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DispositionWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java b/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java index afd0430a2e..6992d7235b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/EncodingReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java index 778307dc9f..ec6fa6fa7b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/EncodingWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java index 6cd522deaf..79180b00bc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java index a7018f1bb6..2488416d54 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ExpectationWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java index 98995f87c6..e02064219f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderConstants.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java index 69cbfc889f..02eba3f670 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java index c5300d80ff..ea42d28a84 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java index 59f6c87114..c868cb4c6f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/HeaderWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java b/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java index 1c35dfc0df..0ca2c843a7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/LanguageReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java index f21c8f873e..6a1790373b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/LanguageWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java index 84ebed348f..a89fecd24a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/MetadataWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java b/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java index ec84188f1d..6581a6b97e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/MethodReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java index 1ac8552156..5ca73904a0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/MethodWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java index 0d3437fc77..abf9131b99 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java index 1b08036560..3ec395b390 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/PreferenceWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java b/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java index cca7fdfea0..d8b5c3f4ee 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ProductReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java index b83c56b2bb..289f443a71 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/ProductWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java b/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java index 7b5d335cd2..7fdbd8782c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RangeReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java index e2d60dc87a..c660e4967e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RangeWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java index e28aa260a5..a6b622e8b3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java index f6ebf8a6d6..ddf05e7e3c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/RecipientInfoWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java b/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java index 13ba327a71..580d64283c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/StringReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java index f54b48d1c3..d28192b14f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/StringWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java b/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java index 5178acbbe6..66a07bb8d8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/TagReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java index 6d570997a7..1ef854c4da 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/TagWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java b/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java index a76def013d..89aeac23ed 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/TokenReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java b/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java index 90f68f4a60..300d0b8544 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/WarningReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java index 72ee3db3a0..1d1fb4d130 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/WarningWriter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java index 068d27c2be..638a1c293f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java +++ b/org.restlet/src/main/java/org/restlet/engine/internal/Activator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java index c01c82ce93..ba38db873f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/IoUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java b/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java index db4ac9770b..2b9f57c944 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/PipeStream.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java index 1ad0b08dda..8cb60b7944 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/RangeInputStream.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java index 9b06b17b85..4dca0ff066 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/ReaderInputStream.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java index 8cd4430e27..aff8437776 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableInputStream.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java index 7729715e59..5cd40e7f9e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/UnclosableOutputStream.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java b/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java index 04fc2401fc..95849efe88 100644 --- a/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java +++ b/org.restlet/src/main/java/org/restlet/engine/io/WriterOutputStream.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java index 5ba4338869..9e4fd361b4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java index 439bc6d460..f632273a3a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java index d49ca0f333..4b556c4d50 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java index 2b46331f44..70f60f2325 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java index 81a0b34bcc..76f642fbad 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java b/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java index ac3a2b6d72..ee6d539181 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/FileEntity.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java index 7bf7d7d6c5..cb29e1ec7f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/LocalClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java index 5c2b476b1f..8d763fa909 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/RiapClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java index 3f0a059e06..1e4edab94b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java index 3a46c99922..9753645986 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ZipClientHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java index c49e7b8ac7..ebaff61f99 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryEntity.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java index 4adfda06b4..0dcafbf827 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ZipEntryRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java index 4e81ad7d84..2e1b9d4d1a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFileHandler.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java index a3216b99b5..76f0255746 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/AccessLogFormatter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java index 0f0c837489..30518a7133 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/DefaultAccessLogFormatter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java b/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java index 2aecb77cb9..86c006dea3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/IdentClient.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java b/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java index b49d7a613d..11944aa64f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LogFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java b/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java index 03657723df..2fca83a5bb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LogUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java b/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java index d2826014e4..eef0b9dddd 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LoggerFacade.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java b/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java index b5f2f55ba7..49bc6d605a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java index a93424810a..1259b5b09b 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/SimplerFormatter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java b/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java index 6edb9d2a5d..7d95e046c1 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/SimplestFormatter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java index f871aa7df6..6e39327431 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java index e034f26c3b..83c08ee7a4 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java b/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java index 10c5708341..f2a5983686 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/ClientInvocationHandler.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java index 3a300d3ae3..7321e4959c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java index 28ab382acc..871ee9adca 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/ThrowableAnnotationInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java index fdbb3cf7e2..0bd6c797ba 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/VariantInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java index abf6e2d21f..baea3341f3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java index 8994223b17..63a2e6e4d5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java index 7cb59caae7..a13c39c7a3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java index dfa4ae52eb..ab9e612d78 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryClient.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java index 6f680d276c..1b835fbe32 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/RestletSslContextFactoryServer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java b/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java index 58896ba541..112dab7884 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/RoleMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java index bedb344a62..ed2d3ca41c 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContext.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java index 110602a523..77b58ccbbb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java index 737726c3b5..d47aca6757 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/SslContextFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java index bd463346f7..eea44c58af 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/SslUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java index 3505316b24..5619d54846 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslContextSpi.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java index 8fd97f163b..7c84788352 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslServerSocketFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java index e25dbe6a94..2511a3b9e6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/WrapperSslSocketFactory.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java b/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java index c5dbc99db8..b3d36e5719 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/AlphaNumericComparator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java b/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java index 53b956017a..05106a10c3 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/AlphabeticalComparator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java index ec40d30d72..f197179f7d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/BeanInfoUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java b/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java index 57802d34b8..09a1ab7321 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/CallResolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java b/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java index 6bc6bd4d9b..4ee7836744 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/CaseInsensitiveHashSet.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java index 2d5fd0766b..7dc7a0c7b1 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ChildClientDispatcher.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java b/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java index 405cf04360..f23d7e7d53 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ChildContext.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java b/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java index 5d86273c53..6526566259 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ContextualRunnable.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java index baab40cb01..6102f45c49 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/DateUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java index 73b04bade1..a786933516 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java b/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java index 49455bf161..2b45121bbb 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/EngineClassLoader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java b/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java index 8fc3c0191b..8b7a6d382d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/FormReader.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 @@ -12,8 +12,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.logging.Level; import org.restlet.Context; @@ -21,6 +19,7 @@ import org.restlet.data.Form; import org.restlet.data.Parameter; import org.restlet.representation.Representation; +import org.restlet.util.NamedValuesCollector; import org.restlet.util.Series; /** @@ -275,50 +274,21 @@ private boolean endOfCurrentParameterReached(int nextChar) { * @return The parameter value or list of values. * @throws IOException If the parameters could not be read. */ - @SuppressWarnings("unchecked") - public Object readParameter(String name) throws IOException { - Object result = null; + public Object readParameter(final String name) throws IOException { - if (this.stream != null) { - Parameter param = readNextParameter(); - - while (param != null) { - if (param.getName().equals(name)) { - if (result != null) { - final List values; + NamedValuesCollector collector = new NamedValuesCollector(name); - if (result instanceof List) { - // Multiple values already found for this parameter - values = (List) result; - } else { - // Second value found for this parameter - // Create a list of values - values = new ArrayList<>(); - values.add(result); - result = values; - } - - if (param.getValue() == null) { - values.add(Series.EMPTY_VALUE); - } else { - values.add(param.getValue()); - } - } else { - if (param.getValue() == null) { - result = Series.EMPTY_VALUE; - } else { - result = param.getValue(); - } - } - } + if (this.stream != null) { + Parameter parameter; - param = readNextParameter(); + while ((parameter = readNextParameter()) != null) { + collector.collect(parameter); } this.stream.close(); } - return result; + return collector.getCollectedValues().get(name); } /** @@ -329,45 +299,13 @@ public Object readParameter(String name) throws IOException { * @param parameters The parameters map controlling the reading. * @throws IOException If the parameters could not be read. */ - @SuppressWarnings("unchecked") public void readParameters(Map parameters) throws IOException { if (this.stream != null) { - Parameter param = readNextParameter(); - Object currentValue = null; - - while (param != null) { - if (parameters.containsKey(param.getName())) { - currentValue = parameters.get(param.getName()); - - if (currentValue != null) { - final List values; + NamedValuesCollector collector = new NamedValuesCollector(parameters); - if (currentValue instanceof List) { - // Multiple values already found for this parameter - values = (List) currentValue; - } else { - // Second value found for this parameter - // Create a list of values - values = new ArrayList<>(); - values.add(currentValue); - parameters.put(param.getName(), values); - } - - if (param.getValue() == null) { - values.add(Series.EMPTY_VALUE); - } else { - values.add(param.getValue()); - } - } else { - if (param.getValue() == null) { - parameters.put(param.getName(), Series.EMPTY_VALUE); - } else { - parameters.put(param.getName(), param.getValue()); - } - } - } - - param = readNextParameter(); + Parameter param; + while ((param = readNextParameter()) != null) { + collector.collect(param); } this.stream.close(); diff --git a/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java index efe4bf29d7..aa2bb35f68 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/FormUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java b/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java index 6bed2cf62a..f935c749e9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ImmutableDate.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java b/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java index 0392a8c527..587ab6d657 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/InternetDateFormat.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java index 97c860427e..ac9a22ebb0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ListUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java b/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java index 4ef8d1a091..b118598cb2 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/MapResolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java index 1a06dba496..d3a8576944 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/Pool.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/Pool.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java index b75bd07f62..a14ac20469 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/ReferenceUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java index 9534815ee6..44fc34fce9 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/SetUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java index dad6aee9e5..8316c366ec 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java index 1afc398387..3e794d02ec 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/SystemUtils.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java b/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java index 3b3978dd1b..5c35c545bc 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/TemplateDispatcher.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java b/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java index 37abbc4e5b..95e65689d7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/WrapperScheduledExecutorService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java index 5ea10542cf..ee1b448e48 100644 --- a/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/AppendableRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java index 4ffa2be0fb..43c806130b 100644 --- a/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java index 3a5592661a..a7fbdec915 100644 --- a/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/ByteArrayRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java index 1fe3ddd5ea..fbe663454b 100644 --- a/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/CharacterRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java index 0f4aff1fc7..8d3a9d0ef9 100644 --- a/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/DigesterRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java index fef0a44d6f..6441b8e4dc 100644 --- a/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/EmptyRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java index 7a5ae73584..9762b3bbf6 100644 --- a/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/FileRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java index c1be206e85..1b251aa3cb 100644 --- a/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/InputRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java index 9969fd1399..42317bae4b 100644 --- a/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java index 5d90361eb3..79e7d50512 100644 --- a/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/ObjectRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java index 852bb752bf..ec3d5b82c3 100644 --- a/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/OutputRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java index 7980dcba30..ce55314d3f 100644 --- a/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/ReaderRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/Representation.java b/org.restlet/src/main/java/org/restlet/representation/Representation.java index 2523871000..cd579c34ff 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Representation.java +++ b/org.restlet/src/main/java/org/restlet/representation/Representation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java b/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java index 2a57e63e7f..4e5dfd5ef2 100644 --- a/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java +++ b/org.restlet/src/main/java/org/restlet/representation/RepresentationInfo.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java index 4247be84ff..1ada9c7fbe 100644 --- a/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/StreamRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java index dabaa4fecf..e474cfe515 100644 --- a/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/Variant.java b/org.restlet/src/main/java/org/restlet/representation/Variant.java index c950cde2a1..6fa4613c84 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Variant.java +++ b/org.restlet/src/main/java/org/restlet/representation/Variant.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java index 4fe4fa29de..75743f1459 100644 --- a/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/WriterRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java b/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java index a527a7373f..0fbb71931c 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java +++ b/org.restlet/src/main/java/org/restlet/resource/ClientProxy.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/ClientResource.java b/org.restlet/src/main/java/org/restlet/resource/ClientResource.java index 4386d92b29..c0d7e3cca6 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ClientResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ClientResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Delete.java b/org.restlet/src/main/java/org/restlet/resource/Delete.java index 0550830bb6..b591cfc2f8 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Delete.java +++ b/org.restlet/src/main/java/org/restlet/resource/Delete.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Directory.java b/org.restlet/src/main/java/org/restlet/resource/Directory.java index 00565d0819..1bc87ae12e 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Directory.java +++ b/org.restlet/src/main/java/org/restlet/resource/Directory.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Finder.java b/org.restlet/src/main/java/org/restlet/resource/Finder.java index 66ef2fc78c..62d605911f 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Finder.java +++ b/org.restlet/src/main/java/org/restlet/resource/Finder.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Get.java b/org.restlet/src/main/java/org/restlet/resource/Get.java index 5b3c62d295..b2e9e9d752 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Get.java +++ b/org.restlet/src/main/java/org/restlet/resource/Get.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Options.java b/org.restlet/src/main/java/org/restlet/resource/Options.java index ec3b7fa8f0..826f0bdfcc 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Options.java +++ b/org.restlet/src/main/java/org/restlet/resource/Options.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Patch.java b/org.restlet/src/main/java/org/restlet/resource/Patch.java index 56a61cc69b..1cc40bceb9 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Patch.java +++ b/org.restlet/src/main/java/org/restlet/resource/Patch.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Post.java b/org.restlet/src/main/java/org/restlet/resource/Post.java index c2cab94af5..cbcb3c1b2e 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Post.java +++ b/org.restlet/src/main/java/org/restlet/resource/Post.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Put.java b/org.restlet/src/main/java/org/restlet/resource/Put.java index d84b5702f6..b6ebfac47e 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Put.java +++ b/org.restlet/src/main/java/org/restlet/resource/Put.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Resource.java b/org.restlet/src/main/java/org/restlet/resource/Resource.java index 8a9a82d8bb..6640f093f3 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Resource.java +++ b/org.restlet/src/main/java/org/restlet/resource/Resource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/ResourceException.java b/org.restlet/src/main/java/org/restlet/resource/ResourceException.java index 3ee8f8ada3..045b663302 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ResourceException.java +++ b/org.restlet/src/main/java/org/restlet/resource/ResourceException.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Result.java b/org.restlet/src/main/java/org/restlet/resource/Result.java index 8c92755707..3a7a6b1955 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Result.java +++ b/org.restlet/src/main/java/org/restlet/resource/Result.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java index 77ddcb9b7f..d414b50abe 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/resource/Status.java b/org.restlet/src/main/java/org/restlet/resource/Status.java index c9968cd772..922458139e 100644 --- a/org.restlet/src/main/java/org/restlet/resource/Status.java +++ b/org.restlet/src/main/java/org/restlet/resource/Status.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Extractor.java b/org.restlet/src/main/java/org/restlet/routing/Extractor.java index 6f4e316b97..d9287e9aca 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Extractor.java +++ b/org.restlet/src/main/java/org/restlet/routing/Extractor.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Filter.java b/org.restlet/src/main/java/org/restlet/routing/Filter.java index 21210621dd..232861ba71 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Filter.java +++ b/org.restlet/src/main/java/org/restlet/routing/Filter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Redirector.java b/org.restlet/src/main/java/org/restlet/routing/Redirector.java index 6f41791349..ca8285e83d 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Redirector.java +++ b/org.restlet/src/main/java/org/restlet/routing/Redirector.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Route.java b/org.restlet/src/main/java/org/restlet/routing/Route.java index e9c0b6832b..f54b07ad0e 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Route.java +++ b/org.restlet/src/main/java/org/restlet/routing/Route.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Router.java b/org.restlet/src/main/java/org/restlet/routing/Router.java index d6826b640c..f71c566d1c 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Router.java +++ b/org.restlet/src/main/java/org/restlet/routing/Router.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Template.java b/org.restlet/src/main/java/org/restlet/routing/Template.java index 6121ad3dc8..3120f9bdd2 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Template.java +++ b/org.restlet/src/main/java/org/restlet/routing/Template.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java b/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java index 0b80ae68f5..3860b33430 100644 --- a/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java +++ b/org.restlet/src/main/java/org/restlet/routing/TemplateRoute.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Validator.java b/org.restlet/src/main/java/org/restlet/routing/Validator.java index db5d61bf77..8170978255 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Validator.java +++ b/org.restlet/src/main/java/org/restlet/routing/Validator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/Variable.java b/org.restlet/src/main/java/org/restlet/routing/Variable.java index c16b9802cf..b2ac36233d 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Variable.java +++ b/org.restlet/src/main/java/org/restlet/routing/Variable.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java b/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java index c320432d33..8e0b97b992 100644 --- a/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java +++ b/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/Authenticator.java b/org.restlet/src/main/java/org/restlet/security/Authenticator.java index 57367189c2..669df4fc75 100644 --- a/org.restlet/src/main/java/org/restlet/security/Authenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/Authenticator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/Authorizer.java b/org.restlet/src/main/java/org/restlet/security/Authorizer.java index d8eba44e1e..602b3bfed6 100644 --- a/org.restlet/src/main/java/org/restlet/security/Authorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/Authorizer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java b/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java index 5dc6d366ad..b40907e599 100644 --- a/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java b/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java index 85670e71ba..395ef36348 100644 --- a/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java b/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java index df295091ca..0305605de9 100644 --- a/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/ConfidentialAuthorizer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/Enroler.java b/org.restlet/src/main/java/org/restlet/security/Enroler.java index 905a5a5352..bcb9159175 100644 --- a/org.restlet/src/main/java/org/restlet/security/Enroler.java +++ b/org.restlet/src/main/java/org/restlet/security/Enroler.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/Group.java b/org.restlet/src/main/java/org/restlet/security/Group.java index 9eae99c5cb..a8c640e838 100644 --- a/org.restlet/src/main/java/org/restlet/security/Group.java +++ b/org.restlet/src/main/java/org/restlet/security/Group.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java b/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java index fc1134f713..bc8ca8f693 100644 --- a/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java +++ b/org.restlet/src/main/java/org/restlet/security/LocalVerifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/MapVerifier.java b/org.restlet/src/main/java/org/restlet/security/MapVerifier.java index 2a1332f1ae..8a512c2adb 100644 --- a/org.restlet/src/main/java/org/restlet/security/MapVerifier.java +++ b/org.restlet/src/main/java/org/restlet/security/MapVerifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java b/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java index 0b952d162c..702f723ac8 100644 --- a/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java +++ b/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java b/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java index f8fbf3a4ef..ffcf61d6c7 100644 --- a/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/MethodAuthorizer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/Realm.java b/org.restlet/src/main/java/org/restlet/security/Realm.java index 3f1de7db9c..8f2c54fe48 100644 --- a/org.restlet/src/main/java/org/restlet/security/Realm.java +++ b/org.restlet/src/main/java/org/restlet/security/Realm.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/Role.java b/org.restlet/src/main/java/org/restlet/security/Role.java index 0f1d7279ad..3e47f23133 100644 --- a/org.restlet/src/main/java/org/restlet/security/Role.java +++ b/org.restlet/src/main/java/org/restlet/security/Role.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java b/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java index 3aecacee11..269d3f4113 100644 --- a/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java +++ b/org.restlet/src/main/java/org/restlet/security/RoleAuthorizer.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java b/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java index b49cd06271..2cb7d18f65 100644 --- a/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java +++ b/org.restlet/src/main/java/org/restlet/security/SecretVerifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/User.java b/org.restlet/src/main/java/org/restlet/security/User.java index f63d572af1..4f3436d484 100644 --- a/org.restlet/src/main/java/org/restlet/security/User.java +++ b/org.restlet/src/main/java/org/restlet/security/User.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/security/Verifier.java b/org.restlet/src/main/java/org/restlet/security/Verifier.java index 4ae3f13488..2671d67526 100644 --- a/org.restlet/src/main/java/org/restlet/security/Verifier.java +++ b/org.restlet/src/main/java/org/restlet/security/Verifier.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/ConnectorService.java b/org.restlet/src/main/java/org/restlet/service/ConnectorService.java index 0ecfea6302..96a6ba0134 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConnectorService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConnectorService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/ConnegService.java b/org.restlet/src/main/java/org/restlet/service/ConnegService.java index 96eb86b25d..80cf6af7e8 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConnegService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConnegService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/ConverterService.java b/org.restlet/src/main/java/org/restlet/service/ConverterService.java index cc3fec5700..069d8109b0 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConverterService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConverterService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/CorsService.java b/org.restlet/src/main/java/org/restlet/service/CorsService.java index 3b27d99512..766f8aabfe 100644 --- a/org.restlet/src/main/java/org/restlet/service/CorsService.java +++ b/org.restlet/src/main/java/org/restlet/service/CorsService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/DecoderService.java b/org.restlet/src/main/java/org/restlet/service/DecoderService.java index 721f74a2b5..1a5f4fba96 100644 --- a/org.restlet/src/main/java/org/restlet/service/DecoderService.java +++ b/org.restlet/src/main/java/org/restlet/service/DecoderService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/EncoderService.java b/org.restlet/src/main/java/org/restlet/service/EncoderService.java index 69c08a99ed..50179f7afc 100644 --- a/org.restlet/src/main/java/org/restlet/service/EncoderService.java +++ b/org.restlet/src/main/java/org/restlet/service/EncoderService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/LogService.java b/org.restlet/src/main/java/org/restlet/service/LogService.java index b9d5e44b42..0879a602d8 100644 --- a/org.restlet/src/main/java/org/restlet/service/LogService.java +++ b/org.restlet/src/main/java/org/restlet/service/LogService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/MetadataService.java b/org.restlet/src/main/java/org/restlet/service/MetadataService.java index 6b2ed24fd3..0026a01222 100644 --- a/org.restlet/src/main/java/org/restlet/service/MetadataService.java +++ b/org.restlet/src/main/java/org/restlet/service/MetadataService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/RangeService.java b/org.restlet/src/main/java/org/restlet/service/RangeService.java index c6402b2f5c..5f141016c5 100644 --- a/org.restlet/src/main/java/org/restlet/service/RangeService.java +++ b/org.restlet/src/main/java/org/restlet/service/RangeService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/Service.java b/org.restlet/src/main/java/org/restlet/service/Service.java index 78e2469d63..cdfa1c4a11 100644 --- a/org.restlet/src/main/java/org/restlet/service/Service.java +++ b/org.restlet/src/main/java/org/restlet/service/Service.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/StatusService.java b/org.restlet/src/main/java/org/restlet/service/StatusService.java index 9097b0826a..62a458b782 100644 --- a/org.restlet/src/main/java/org/restlet/service/StatusService.java +++ b/org.restlet/src/main/java/org/restlet/service/StatusService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/TaskService.java b/org.restlet/src/main/java/org/restlet/service/TaskService.java index 6c59becc4b..2ef6e8b87d 100644 --- a/org.restlet/src/main/java/org/restlet/service/TaskService.java +++ b/org.restlet/src/main/java/org/restlet/service/TaskService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/service/TunnelService.java b/org.restlet/src/main/java/org/restlet/service/TunnelService.java index dec7b6a0ca..83565db487 100644 --- a/org.restlet/src/main/java/org/restlet/service/TunnelService.java +++ b/org.restlet/src/main/java/org/restlet/service/TunnelService.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/ClientList.java b/org.restlet/src/main/java/org/restlet/util/ClientList.java index 8500854c50..df4cba5d39 100644 --- a/org.restlet/src/main/java/org/restlet/util/ClientList.java +++ b/org.restlet/src/main/java/org/restlet/util/ClientList.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/NamedValue.java b/org.restlet/src/main/java/org/restlet/util/NamedValue.java index 89773f1735..72072a27bd 100644 --- a/org.restlet/src/main/java/org/restlet/util/NamedValue.java +++ b/org.restlet/src/main/java/org/restlet/util/NamedValue.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/NamedValuesCollector.java b/org.restlet/src/main/java/org/restlet/util/NamedValuesCollector.java new file mode 100644 index 0000000000..6e2758c6fd --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/util/NamedValuesCollector.java @@ -0,0 +1,85 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Collects named values inside a given map. Only the values related to the existing keys will be + * added. + * + *

In case of multiple values for the same name, the map will contain a list of values. + */ +public class NamedValuesCollector { + + private final Map collector; + + /** + * Constructor. + * + * @param mapToUpdate The map to update + */ + public NamedValuesCollector(final Map mapToUpdate) { + this.collector = mapToUpdate; + } + + /** + * Constructor. + * + * @param names The list of names to collect. + */ + public NamedValuesCollector(final String... names) { + this.collector = new HashMap<>(); + for (String name : names) { + collector.put(name, null); + } + } + + @SuppressWarnings("unchecked") + public void collect(final NamedValue parameter) { + if (collector.containsKey(parameter.getName())) { + Object currentValue = collector.get(parameter.getName()); + + if (currentValue != null) { + List values; + + if (currentValue instanceof List) { // Multiple values already found for this entry + values = (List) currentValue; + } else { + // Second value found for this entry + // Create a list of values + values = new ArrayList<>(); + values.add(currentValue); + collector.put(parameter.getName(), values); + } + + values.add( + parameter.getValue() == null ? Series.EMPTY_VALUE : parameter.getValue()); + } else { + collector.put( + parameter.getName(), + parameter.getValue() == null ? Series.EMPTY_VALUE : parameter.getValue()); + } + } + } + + /** + * Returns either the map transmitted to the constructor or a new map containing the collected + * values. + * + * @return Either the map transmitted to the constructor or a new map containing the collected + * values + */ + public Map getCollectedValues() { + return collector; + } +} diff --git a/org.restlet/src/main/java/org/restlet/util/Resolver.java b/org.restlet/src/main/java/org/restlet/util/Resolver.java index 698d00c4e5..41571c8328 100644 --- a/org.restlet/src/main/java/org/restlet/util/Resolver.java +++ b/org.restlet/src/main/java/org/restlet/util/Resolver.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/RouteList.java b/org.restlet/src/main/java/org/restlet/util/RouteList.java index 04718dafd8..bf3eefd69f 100644 --- a/org.restlet/src/main/java/org/restlet/util/RouteList.java +++ b/org.restlet/src/main/java/org/restlet/util/RouteList.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/Series.java b/org.restlet/src/main/java/org/restlet/util/Series.java index 7810d4dd79..2a5ce01789 100644 --- a/org.restlet/src/main/java/org/restlet/util/Series.java +++ b/org.restlet/src/main/java/org/restlet/util/Series.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 @@ -8,7 +8,6 @@ */ package org.restlet.util; -import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -92,46 +91,13 @@ public boolean add(String name, String value) { * If a matching parameter is found, its value is put in the map.
* If multiple values are found, a list is created and set in the map. * - * @param params The map controlling the copy. + * @param parameters The map controlling the copy. */ - @SuppressWarnings("unchecked") - public void copyTo(Map params) { - NamedValue param; - Object currentValue = null; + public void copyTo(Map parameters) { + NamedValuesCollector collector = new NamedValuesCollector(parameters); for (T t : this) { - param = t; - - if (params.containsKey(param.getName())) { - currentValue = params.get(param.getName()); - - if (currentValue != null) { - List values = null; - - if (currentValue instanceof List) { - // Multiple values already found for this entry - values = (List) currentValue; - } else { - // Second value found for this entry - // Create a list of values - values = new ArrayList<>(); - values.add(currentValue); - params.put(param.getName(), values); - } - - if (param.getValue() == null) { - values.add(Series.EMPTY_VALUE); - } else { - values.add(param.getValue()); - } - } else { - if (param.getValue() == null) { - params.put(param.getName(), Series.EMPTY_VALUE); - } else { - params.put(param.getName(), param.getValue()); - } - } - } + collector.collect(t); } } @@ -259,7 +225,7 @@ public String getFirstValue(String name, String defaultValue) { * @return The set of parameter names. */ public Set getNames() { - Set result = new HashSet(); + Set result = new HashSet<>(); for (NamedValue param : this) { result.add(param.getName()); @@ -383,7 +349,7 @@ public String[] getValuesArray(String name, String defaultValue) { * @return The map of name, value pairs. */ public Map getValuesMap() { - Map result = new LinkedHashMap(); + Map result = new LinkedHashMap<>(); for (NamedValue param : this) { if (!result.containsKey(param.getName())) { @@ -520,7 +486,7 @@ public T set(String name, String value, boolean ignoreCase) { */ @Override public Series subList(int fromIndex, int toIndex) { - return new Series(this.entryClass, getDelegate().subList(fromIndex, toIndex)); + return new Series<>(this.entryClass, getDelegate().subList(fromIndex, toIndex)); } /** @@ -541,7 +507,7 @@ public Series subList(String name) { * @return The list of values. */ public Series subList(String name, boolean ignoreCase) { - Series result = new Series(this.entryClass); + Series result = new Series<>(this.entryClass); for (T param : this) { if (equals(param.getName(), name, ignoreCase)) { diff --git a/org.restlet/src/main/java/org/restlet/util/ServerList.java b/org.restlet/src/main/java/org/restlet/util/ServerList.java index 8af54c934a..758a3f1c64 100644 --- a/org.restlet/src/main/java/org/restlet/util/ServerList.java +++ b/org.restlet/src/main/java/org/restlet/util/ServerList.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/ServiceList.java b/org.restlet/src/main/java/org/restlet/util/ServiceList.java index e9e22151f9..9d7247b232 100644 --- a/org.restlet/src/main/java/org/restlet/util/ServiceList.java +++ b/org.restlet/src/main/java/org/restlet/util/ServiceList.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperList.java b/org.restlet/src/main/java/org/restlet/util/WrapperList.java index 9fbd09f109..587870ffc1 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperList.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperList.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 @@ -17,7 +17,7 @@ /** * List wrapper. Modifiable list that delegates all methods to a wrapped list. This allows an easy - * sub-classing. By default, it wraps a thread-safe {@link Vector} instance. + * subclassing. By default, it wraps a thread-safe {@link Vector} instance. * * @author Jerome Louvel * @see The decorator (aka wrapper) pattern @@ -39,7 +39,7 @@ public WrapperList() { * @param initialCapacity The initial list capacity. */ public WrapperList(int initialCapacity) { - this(new Vector(initialCapacity)); + this(new Vector<>(initialCapacity)); } /** @@ -71,7 +71,7 @@ public void add(int index, E element) { } /** - * Appends all of the elements in the specified collection to the end of this list. + * Appends the elements of the specified collection at the end of this list. * * @param elements The collection of elements to append. */ @@ -80,8 +80,7 @@ public boolean addAll(Collection elements) { } /** - * Inserts all of the elements in the specified collection into this list at the specified - * position. + * Inserts the elements of the specified collection into this list at the specified position. * * @param index The insertion position. * @param elements The collection of elements to insert. @@ -90,7 +89,7 @@ public boolean addAll(int index, Collection elements) { return getDelegate().addAll(index, elements); } - /** Removes all of the elements from this list. */ + /** Removes the elements from this list. */ public void clear() { getDelegate().clear(); } @@ -106,10 +105,10 @@ public boolean contains(Object element) { } /** - * Returns true if this list contains all of the elements of the specified collection. + * Returns true if this list contains all the elements of the specified collection. * * @param elements The collection of elements to find. - * @return True if this list contains all of the elements of the specified collection. + * @return True if this list contains all the elements of the specified collection. */ public boolean containsAll(Collection elements) { return getDelegate().containsAll(elements); @@ -173,9 +172,9 @@ public boolean isEmpty() { } /** - * Returns an iterator over the elements in this list in proper sequence. + * Returns an iterator over the elements in this list in a proper sequence. * - * @return An iterator over the elements in this list in proper sequence. + * @return An iterator over the elements in this list in a proper sequence. */ public Iterator iterator() { return getDelegate().iterator(); @@ -190,16 +189,16 @@ public int lastIndexOf(Object element) { } /** - * Returns a list iterator of the elements in this list (in proper sequence). + * Returns a list iterator of the elements in this list (in a proper sequence). * - * @return A list iterator of the elements in this list (in proper sequence). + * @return A list iterator of the elements in this list (in a proper sequence). */ public ListIterator listIterator() { return getDelegate().listIterator(); } /** - * Returns a list iterator of the elements in this list (in proper sequence), starting at the + * Returns a list iterator of the elements in this list (in a proper sequence), starting at the * specified position in this list. * * @param index The starting position. @@ -274,13 +273,13 @@ public int size() { * @return The sub-list. */ public List subList(int fromIndex, int toIndex) { - return new WrapperList(getDelegate().subList(fromIndex, toIndex)); + return new WrapperList<>(getDelegate().subList(fromIndex, toIndex)); } /** * Returns an array containing all the elements in this list in a proper sequence. * - * @return An array containing all the elements in this list in proper sequence. + * @return An array containing all the elements in this list in a proper sequence. */ public Object[] toArray() { return getDelegate().toArray(); diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java index d54b36cd73..a473a30758 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperMap.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperMap.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java b/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java index 7f7acf46e2..f012099e6b 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRepresentation.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java index 2c17e7fa7b..bef197704b 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java b/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java index 625ef6a2aa..1495fa3b9b 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperResponse.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java b/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java index d927aba30a..8a7958c0d6 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRestlet.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java b/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java index 42c93019a6..a07394b851 100644 --- a/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java +++ b/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java b/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java index f43ae5be83..3915b3325f 100644 --- a/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java +++ b/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/CallTestCase.java b/org.restlet/src/test/java/org/restlet/CallTestCase.java index d7d11b0cac..ed6ea947b2 100644 --- a/org.restlet/src/test/java/org/restlet/CallTestCase.java +++ b/org.restlet/src/test/java/org/restlet/CallTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/RestartTestCase.java b/org.restlet/src/test/java/org/restlet/RestartTestCase.java index 34cdaba794..3cfab88a00 100644 --- a/org.restlet/src/test/java/org/restlet/RestartTestCase.java +++ b/org.restlet/src/test/java/org/restlet/RestartTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java index fa6d919e02..b6e6a4afd6 100644 --- a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java index ab4b78f734..5a9f180c01 100644 --- a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java b/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java index f91e6c22b2..291ec23fb1 100644 --- a/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java b/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java index 9cf02b67e3..ced7cf2e4f 100644 --- a/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java b/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java index 88c8513b3c..f90e336824 100644 --- a/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/FormTestCase.java b/org.restlet/src/test/java/org/restlet/data/FormTestCase.java index 84a356cb39..787fb44340 100644 --- a/org.restlet/src/test/java/org/restlet/data/FormTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FormTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java b/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java index 8fa815d50b..f4390e4711 100644 --- a/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java index db6caf35f7..e32ca832c8 100644 --- a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java b/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java index bb40a3b55c..c8b1687663 100644 --- a/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java index d16b458d27..1ab7df730d 100644 --- a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java b/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java index 1cf24cc345..9a8a083eb0 100644 --- a/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java index 44272ff247..9b9b678d3c 100644 --- a/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java index e40d455e71..c568f6ffa6 100644 --- a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java b/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java index 63cdc851e9..827a04484d 100644 --- a/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java b/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java index afe5e57cb7..44b2fa1717 100644 --- a/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java b/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java index 7584c6e4a2..7251fa447b 100644 --- a/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/TagTestCase.java b/org.restlet/src/test/java/org/restlet/data/TagTestCase.java index ba84f34eed..0976b678ba 100644 --- a/org.restlet/src/test/java/org/restlet/data/TagTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/TagTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java b/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java index afcb155f34..98650970b9 100644 --- a/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/EngineTest.java b/org.restlet/src/test/java/org/restlet/engine/EngineTest.java index 7bb6f97ca8..64372a0b1b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/EngineTest.java +++ b/org.restlet/src/test/java/org/restlet/engine/EngineTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java index c73943935c..dd07f7ce32 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java index c2c6d7c223..b835daa5db 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java index e649aea819..b3ea9a2d89 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java index 4ee2f908ed..441398e9ad 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java index cd15fad0f6..ac0aefa3ba 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/BaseConnectorsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java index 1482cf3dd4..04d78ff715 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingPutTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java index 248225c22c..0bed46cf58 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ChunkedEncodingTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java index bcec722f35..8260772fc5 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java index 8dcf022f40..fba3ffdf1c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java index 55ca0ada9e..1c19e43301 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java index 24b3f87814..fdd4276792 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java index d5161ba71e..ec7a3b6397 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java index fa63595684..403f0184f3 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/PostPutTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java index 17c4aecf06..fb43597c6f 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/RemoteClientAddressTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java index 494c63e8eb..d71857fabf 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ServerMaxConnectionsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java index a5bdcbe47f..824d18184f 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2014 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 @@ -30,26 +30,26 @@ import org.restlet.engine.Engine; import org.restlet.resource.ClientResource; -public class ShutdownHookTestCase { +class ShutdownHookTestCase { private static final Logger LOGGER = Logger.getLogger("ShutdownHookTest"); private static boolean shouldDebug = false; @BeforeEach - public void setUp() { + void setUp() { LOGGER.setLevel(Level.FINE); Engine.clearThreadLocalVariables(); Engine.register(true); } @AfterEach - public void tearDown() { + void tearDown() { Engine.clearThreadLocalVariables(); Engine.register(true); } /** Validates that the server stops immediately when no requests are currently handled. */ @Test - public void whenServerIsNotHandlingRequestThenItStopsImmediately() throws Exception { + void whenServerIsNotHandlingRequestThenItStopsImmediately() throws Exception { // Given a server resource that takes 1 min to send a response final Restlet hangingRestlet = newHangingRestlet(Duration.ofMinutes(1)); // Given a server with a 3-seconds graceful shutdown diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java index 9508ff7428..ea7f3dfb67 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/SslBaseConnectorsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java index 3da8479d42..2cf2dd42a1 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/SslClientContextGetTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java index e83c2eeff7..dd701dcdc5 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/SslGetTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java index 0f3a913ce7..6a543d2691 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java index e92d041f10..fa8e794fd7 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java index 8ff00dd1c6..af2a1c05db 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java index 3f395e47e2..09118d4abb 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java index 919b2165a9..2d90fb9c30 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java index 596d1267e2..d557a7fb00 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java index d80c11081f..d0532044ad 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java index 42c54c9e37..a3333c046e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java index 3d0baab0d1..858426219d 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java index 1df41acd30..de72d5a998 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java index dc14abc1b1..007e9eae9c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java index eb7cb078e9..d38f822597 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableOutputStreamTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java index a4e92a4fed..6a00aa6ead 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AbstractAnnotatedServerResource03.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java index 8aea26ab6e..3a5a84fd9d 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java index e4c7b29f02..e888496f8f 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_01.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java index 18fbd1f4e5..4e6d7136a7 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedInterface03_02.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java index 7acb0033e1..0b2ecebb58 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java index 817209fdbd..8207aa2b64 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java index fc04a8e294..173d4ddf89 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java index 76664bb584..a703947806 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/JettyConnectorTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java index 83c3a7d33d..b4dae75fff 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource09.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java index 0d6ce92e91..78e6fd734c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource10.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java index 79daee5191..2996357469 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java index 9d2aa7a16a..71fc739a5c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SIMethod.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java index 50fc9d0c50..7ddabbd8ce 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/SNIMethod.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java index f620f23f8b..4ae5939759 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USIMethod.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java index 96b0f6d33b..56c03c343b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/USNIMethod.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java index cfca87ad49..1675ff355e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java index df14276d87..cf35a3bac5 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java index 806d3eb2cb..435cc5628a 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/engine/util/FormReaderTest.java b/org.restlet/src/test/java/org/restlet/engine/util/FormReaderTest.java new file mode 100644 index 0000000000..5a74311b68 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/util/FormReaderTest.java @@ -0,0 +1,208 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.engine.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.restlet.util.Series; + +class FormReaderTest { + + @Nested + class ReadParameter { + /** Returns null when no parameter matches the requested name. */ + @Test + void shouldReturnNullWhenNameNotFound() throws IOException { + FormReader reader = new FormReader("foo=bar&baz=qux", '&'); + + Object result = reader.readParameter("missing"); + + assertNull(result); + } + + /** Returns the value string when the name is found exactly once. */ + @Test + void shouldReturnSingleValue() throws IOException { + FormReader reader = new FormReader("color=red&size=large", '&'); + + Object result = reader.readParameter("color"); + + assertEquals("red", result); + } + + /** Returns {@link Series#EMPTY_VALUE} when the parameter has no value (no '=' sign). */ + @Test + void shouldReturnEmptyValueForParameterWithoutValue() throws IOException { + FormReader reader = new FormReader("flag&other=val", '&'); + + Object result = reader.readParameter("flag"); + + assertSame(Series.EMPTY_VALUE, result); + } + + /** Returns a List containing both values when the name appears twice. */ + @Test + void shouldReturnListForDuplicateNames() throws IOException { + FormReader reader = new FormReader("accept=text/html&accept=application/json", '&'); + + Object result = reader.readParameter("accept"); + + assertInstanceOf(List.class, result); + @SuppressWarnings("unchecked") + List list = (List) result; + assertEquals(2, list.size()); + assertEquals("text/html", list.get(0)); + assertEquals("application/json", list.get(1)); + } + + /** A third occurrence is appended to the existing list (not nested). */ + @Test + void shouldReturnListForTriplicateNames() throws IOException { + FormReader reader = new FormReader("x=1&x=2&x=3", '&'); + + Object result = reader.readParameter("x"); + + assertInstanceOf(List.class, result); + @SuppressWarnings("unchecked") + List list = (List) result; + assertEquals(3, list.size()); + assertEquals("1", list.get(0)); + assertEquals("2", list.get(1)); + assertEquals("3", list.get(2)); + } + + /** Null values in a multi-value entry are stored as {@link Series#EMPTY_VALUE}. */ + @Test + void nullValuesInListStoredAsEmptyValue() throws IOException { + FormReader reader = new FormReader("h&h", '&'); + + Object result = reader.readParameter("h"); + + assertInstanceOf(List.class, result); + @SuppressWarnings("unchecked") + List list = (List) result; + assertEquals(2, list.size()); + assertSame(Series.EMPTY_VALUE, list.get(0)); + assertSame(Series.EMPTY_VALUE, list.get(1)); + } + } + + @Nested + class ReadParameters { + /** A parameter whose name is not a key in the map is silently ignored. */ + @Test + void shouldIgnoreUnknownName() throws IOException { + FormReader reader = new FormReader("foo=bar&baz=qux", '&'); + Map params = map("other"); + + reader.readParameters(params); + + assertNull(params.get("other")); + } + + /** A matching parameter stores its value directly (not in a list). */ + @Test + void shouldStoreSingleValue() throws IOException { + FormReader reader = new FormReader("color=red&size=large", '&'); + Map params = map("color"); + + reader.readParameters(params); + + assertEquals("red", params.get("color")); + } + + /** A parameter with no value stores {@link Series#EMPTY_VALUE} as a sentinel. */ + @Test + void shouldStoreEmptyValueWhenNullValue() throws IOException { + FormReader reader = new FormReader("flag&other=val", '&'); + Map params = map("flag"); + + reader.readParameters(params); + + assertSame(Series.EMPTY_VALUE, params.get("flag")); + } + + /** Two parameters with the same name produce a List containing both values. */ + @Test + void shouldProduceListWhenDuplicateNames() throws IOException { + FormReader reader = new FormReader("accept=text/html&accept=application/json", '&'); + Map params = map("accept"); + + reader.readParameters(params); + + assertInstanceOf(List.class, params.get("accept")); + @SuppressWarnings("unchecked") + List list = (List) params.get("accept"); + assertEquals(2, list.size()); + assertEquals("text/html", list.get(0)); + assertEquals("application/json", list.get(1)); + } + + /** Three parameters with the same name all end up in a single growing List. */ + @Test + void shouldStoreListOfThreeWhenTriplicateNames() throws IOException { + FormReader reader = new FormReader("x=1&x=2&x=3", '&'); + Map params = map("x"); + + reader.readParameters(params); + + @SuppressWarnings("unchecked") + List list = (List) params.get("x"); + assertEquals(3, list.size()); + assertEquals("1", list.get(0)); + assertEquals("2", list.get(1)); + assertEquals("3", list.get(2)); + } + + /** Null values in a multi-value list entry are stored as {@link Series#EMPTY_VALUE}. */ + @Test + void shouldStoreEmptyValuesWhenNullValuesInList() throws IOException { + FormReader reader = new FormReader("h&h", '&'); + Map params = map("h"); + + reader.readParameters(params); + + @SuppressWarnings("unchecked") + List list = (List) params.get("h"); + assertEquals(2, list.size()); + assertSame(Series.EMPTY_VALUE, list.get(0)); + assertSame(Series.EMPTY_VALUE, list.get(1)); + } + + /** Only keys already present in the map are populated; unrelated parameters are skipped. */ + @Test + void shouldPopulateOnlyMapKeys() throws IOException { + FormReader reader = new FormReader("a=1&b=2&c=3", '&'); + Map params = map("a", "c"); + + reader.readParameters(params); + + assertEquals("1", params.get("a")); + assertEquals("3", params.get("c")); + assertEquals(2, params.size(), "Key 'b' should not have been inserted"); + } + } + + private static Map map(String... keys) { + Map m = new HashMap<>(); + for (String key : keys) { + m.put(key, null); + } + return m; + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java index e9a3d0c551..9df0c37600 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java index 7cf20d0f1c..927433aac6 100644 --- a/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java index d702633c34..2b3804f5cd 100644 --- a/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java index 89418f0100..95925707eb 100644 --- a/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java index f2eff9fd04..723ea64625 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java index 8c7c63e789..a59471b431 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AbstractAnnotatedResourceWithFinderTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java b/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java index b2aed7fdf2..08ca81f96f 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java +++ b/org.restlet/src/test/java/org/restlet/resource/AbstractGenericAnnotatedServerResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java index 55dc9db8a7..ad64771e5d 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java index 2f57e1b9dc..8e1be40824 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java index dffe263607..6cb1ab7f04 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java index e1b9d7a6a4..d6402875e3 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java index e5f4428d64..2aeeb625c2 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java index 3cc9ed0758..404264948b 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java index fd5b8b791a..b6119fc516 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java index 18a68064c5..7c6fdc2892 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java index 34c08bb163..34bce8a1a4 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java index 71e8313b28..b263e99230 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java index 9c27bb3c5e..665f9b84df 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java index 3ea1eddc50..5d18a91a62 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java index a733daf6d5..00afa564dc 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java index 8d137c0462..bd58e73909 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java index 627d60059b..bd909a0d00 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java index 9f117ae056..31136dffdd 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java index 9a9e6b116d..3a9f44198a 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java b/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java index 897396eea6..fa08185d37 100644 --- a/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java b/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java index ca28eecaa0..e858a66858 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericAnnotatedServerResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java index fac8c6417c..bcea0f70c7 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java index b528f6bbdb..cb987bee25 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyBean.java b/org.restlet/src/test/java/org/restlet/resource/MyBean.java index d8a24487c9..5a11b8352e 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyBean.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyBean.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyException01.java b/org.restlet/src/test/java/org/restlet/resource/MyException01.java index 2ef17c8d90..e73bd1fabe 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyException01.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyException01.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyException02.java b/org.restlet/src/test/java/org/restlet/resource/MyException02.java index a9ffd1cbe8..6a51f099f9 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyException02.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyException02.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource01.java b/org.restlet/src/test/java/org/restlet/resource/MyResource01.java index 2fef14e0a0..366a4187ee 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource01.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource01.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource02.java b/org.restlet/src/test/java/org/restlet/resource/MyResource02.java index d68ff18026..921faa6271 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource02.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource02.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource03.java b/org.restlet/src/test/java/org/restlet/resource/MyResource03.java index 0fa253dc13..a58ccc22eb 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource03.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource03.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource04.java b/org.restlet/src/test/java/org/restlet/resource/MyResource04.java index e6d898fac2..e096da6383 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource04.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource04.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource05.java b/org.restlet/src/test/java/org/restlet/resource/MyResource05.java index e06ad161fa..b085e3a0cf 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource05.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource05.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource06.java b/org.restlet/src/test/java/org/restlet/resource/MyResource06.java index c76c0dd99d..92b1f49996 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource06.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource06.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource07.java b/org.restlet/src/test/java/org/restlet/resource/MyResource07.java index 8aca40b0aa..a1fdf51f88 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource07.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource07.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource08.java b/org.restlet/src/test/java/org/restlet/resource/MyResource08.java index e98533d9f4..9b7d3640d7 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource08.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource08.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource12.java b/org.restlet/src/test/java/org/restlet/resource/MyResource12.java index d4cc699cb6..09aae24e14 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource12.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource12.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource17.java b/org.restlet/src/test/java/org/restlet/resource/MyResource17.java index 7dc8622101..27e92ee1cd 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource17.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource17.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyResource20.java b/org.restlet/src/test/java/org/restlet/resource/MyResource20.java index d40ab009d9..1da8bfc1d4 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyResource20.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyResource20.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java index 175282e737..f690e0409e 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource01.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java index cc3270099b..4619097aea 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource12.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java index 8d4f58bfa8..aea87d3203 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java index 6ccbf0bc69..bd05c2c938 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource15Ok.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java index b46cc58290..efa9974772 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource16.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java index c837671ef5..892ed61a51 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource17.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java index cbc4106a19..59aec50486 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource18.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java b/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java index 9d7ba2009c..1e326b90da 100644 --- a/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java +++ b/org.restlet/src/test/java/org/restlet/resource/MyServerResource20.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java b/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java index d002d8c5dc..7da675cfeb 100644 --- a/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java b/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java index 0df72a60c3..39e661bfc9 100644 --- a/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/FilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/MockFilter.java b/org.restlet/src/test/java/org/restlet/routing/MockFilter.java index 2e45ae8dbd..c0291f4856 100644 --- a/org.restlet/src/test/java/org/restlet/routing/MockFilter.java +++ b/org.restlet/src/test/java/org/restlet/routing/MockFilter.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java b/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java index 1c9f7c97df..f15d4cc068 100644 --- a/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java +++ b/org.restlet/src/test/java/org/restlet/routing/MockRestlet.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java b/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java index 3c14af40f4..50e315fcfc 100644 --- a/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java b/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java index 60d2b6ebbd..f1fcb25c68 100644 --- a/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java b/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java index 90878787ea..c9d03d2143 100644 --- a/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java b/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java index 784b4c1c36..f27137b792 100644 --- a/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java +++ b/org.restlet/src/test/java/org/restlet/routing/TraceRestlet.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java b/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java index a45596dbcb..879779a932 100644 --- a/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java b/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java index 88e92fb14b..bff810c43d 100644 --- a/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java +++ b/org.restlet/src/test/java/org/restlet/security/HelloWorldRestlet.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java index a8579c0bc6..d9cc0225c4 100644 --- a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java b/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java index e17d3c1c94..1c67b33722 100644 --- a/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java +++ b/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java b/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java index 59d19378db..d2083ec02c 100644 --- a/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/security/SaasApplication.java b/org.restlet/src/test/java/org/restlet/security/SaasApplication.java index 7e2c825d57..057f2974fd 100644 --- a/org.restlet/src/test/java/org/restlet/security/SaasApplication.java +++ b/org.restlet/src/test/java/org/restlet/security/SaasApplication.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/security/SaasComponent.java b/org.restlet/src/test/java/org/restlet/security/SaasComponent.java index 2f6ecb663d..a408c8f83b 100644 --- a/org.restlet/src/test/java/org/restlet/security/SaasComponent.java +++ b/org.restlet/src/test/java/org/restlet/security/SaasComponent.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java b/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java index 9ce338f400..ff2b5177e5 100644 --- a/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java index df4ffab69a..7f37986970 100644 --- a/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java index 6ba57a4631..71b43ab08f 100644 --- a/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java index cad9901605..1eae214823 100644 --- a/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java b/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java index ec2a738b01..0d8beb9087 100644 --- a/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java +++ b/org.restlet/src/test/java/org/restlet/service/UserAgentTestResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java b/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java index 158eeb0cf5..3f307908f5 100644 --- a/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2024 Qlik + * Copyright 2005-2026 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 diff --git a/org.restlet/src/test/java/org/restlet/util/SeriesTest.java b/org.restlet/src/test/java/org/restlet/util/SeriesTest.java new file mode 100644 index 0000000000..a17fdf81eb --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/util/SeriesTest.java @@ -0,0 +1,172 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.restlet.data.Form; +import org.restlet.data.Parameter; + +class SeriesTest { + + /** A parameter whose name is not a key in the map is silently skipped. */ + @Test + void copyTo_unknownNameIsIgnored() { + Form form = form("foo", "bar"); + Map params = map("other"); + + form.copyTo(params); + + assertNull(params.get("other")); + } + + /** An empty Series leaves the map untouched. */ + @Test + void copyTo_emptySeriesLeavesMapUnchanged() { + Form form = new Form(); + Map params = map("key"); + + form.copyTo(params); + + assertNull(params.get("key")); + } + + /** A single parameter whose name matches a key stores its value directly (not in a list). */ + @Test + void copyTo_singleValueStoredDirectly() { + Form form = form("color", "red"); + Map params = map("color"); + + form.copyTo(params); + + assertEquals("red", params.get("color")); + } + + /** A parameter with a null value stores {@link Series#EMPTY_VALUE} as a sentinel. */ + @Test + void copyTo_nullValueStoredAsEmptyValue() { + Form form = new Form(); + form.add(new Parameter("flag", null)); + Map params = map("flag"); + + form.copyTo(params); + + assertSame(Series.EMPTY_VALUE, params.get("flag")); + } + + /** Two parameters with the same name produce a List containing both values. */ + @Test + void copyTo_duplicateNamesProduceList() { + Form form = form("accept", "text/html", "accept", "application/json"); + Map params = map("accept"); + + form.copyTo(params); + + Object value = params.get("accept"); + assertInstanceOf(List.class, value, "Expected a List for duplicate entries"); + @SuppressWarnings("unchecked") + List list = (List) value; + assertEquals(2, list.size()); + assertEquals("text/html", list.get(0)); + assertEquals("application/json", list.get(1)); + } + + /** Three parameters with the same name all end up in a single growing List. */ + @Test + void copyTo_triplicateNamesProduceListOfThree() { + Form form = form("x", "1", "x", "2", "x", "3"); + Map params = map("x"); + + form.copyTo(params); + + @SuppressWarnings("unchecked") + List list = (List) params.get("x"); + assertEquals(3, list.size()); + assertEquals("1", list.get(0)); + assertEquals("2", list.get(1)); + assertEquals("3", list.get(2)); + } + + /** Null values in a multi-value entry are stored as {@link Series#EMPTY_VALUE}. */ + @Test + void copyTo_nullValuesInListStoredAsEmptyValue() { + Form form = new Form(); + form.add(new Parameter("h", null)); + form.add(new Parameter("h", null)); + Map params = map("h"); + + form.copyTo(params); + + @SuppressWarnings("unchecked") + List list = (List) params.get("h"); + assertEquals(2, list.size()); + assertSame(Series.EMPTY_VALUE, list.get(0)); + assertSame(Series.EMPTY_VALUE, list.get(1)); + } + + /** Mixed null and non-null values in a multi-value entry are handled correctly. */ + @Test + void copyTo_mixedNullAndNonNullValuesInList() { + Form form = new Form(); + form.add(new Parameter("h", "value1")); + form.add(new Parameter("h", null)); + form.add(new Parameter("h", "value2")); + Map params = map("h"); + + form.copyTo(params); + + @SuppressWarnings("unchecked") + List list = (List) params.get("h"); + assertEquals(3, list.size()); + assertEquals("value1", list.get(0)); + assertSame(Series.EMPTY_VALUE, list.get(1)); + assertEquals("value2", list.get(2)); + } + + /** + * Only the keys initially present in the map receive values; unrelated entries are untouched. + */ + @Test + void copyTo_onlyMapKeysArePopulated() { + Form form = form("a", "1", "b", "2", "c", "3"); + Map params = map("a", "c"); + + form.copyTo(params); + + assertEquals("1", params.get("a")); + assertEquals("3", params.get("c")); + assertFalse(params.containsKey("b"), "Key 'b' should not have been inserted"); + } + + // -- helpers -- + + private static Form form(String... pairs) { + Form form = new Form(); + for (int i = 0; i < pairs.length; i += 2) { + form.add(pairs[i], pairs[i + 1]); + } + return form; + } + + private static Map map(String... keys) { + Map m = new HashMap<>(); + for (String key : keys) { + m.put(key, null); + } + return m; + } +} From 20161a7d9cb6832f19aa70b7e80afa796ad854c8 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 14:35:51 +0200 Subject: [PATCH 19/30] Take into account Sonar comments --- .../HttpAzureSharedKeyLiteHelper.java | 2 +- .../crypto/CookieAuthenticatorTestCase.java | 4 +- .../internal/HttpDigestHelperTestCase.java | 2 +- .../org/restlet/engine/header/DateWriter.java | 2 +- .../security/CertificateAuthenticator.java | 6 +- .../org/restlet/service/ConverterService.java | 2 +- .../org/restlet/data/ClientInfoTestCase.java | 10 +-- .../restlet/data/ProductTokenTestCase.java | 44 ++++++------- .../restlet/data/RecipientInfoTestCase.java | 4 +- .../java/org/restlet/data/TagTestCase.java | 14 ++-- .../org/restlet/data/ZipClientTestCase.java | 37 ++++++----- .../MultiPartRepresentationTestCase.java | 10 +-- .../engine/header/CookieReaderTestCase.java | 10 +-- .../restlet/engine/header/HeaderTestCase.java | 66 +++++++++---------- .../engine/util/DateUtilsTestCase.java | 56 +++++++--------- ...eaderTest.java => FormReaderTestCase.java} | 2 +- .../org/restlet/routing/TemplateTestCase.java | 10 +-- .../{SeriesTest.java => SeriesTestCase.java} | 2 +- 18 files changed, 138 insertions(+), 145 deletions(-) rename org.restlet/src/test/java/org/restlet/engine/util/{FormReaderTest.java => FormReaderTestCase.java} (99%) rename org.restlet/src/test/java/org/restlet/util/{SeriesTest.java => SeriesTestCase.java} (99%) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java index ba42b724e5..c80866f9ae 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelper.java @@ -74,7 +74,7 @@ public void formatResponse( if (date == null) { // Add a fresh Date header - date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.get(0)); + date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.getFirst()); httpHeaders.add(HeaderConstants.HEADER_DATE, date); } } else { diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java index f34d6d9d54..dfb97cd502 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/CookieAuthenticatorTestCase.java @@ -31,7 +31,7 @@ * * @author Jerome Louvel */ -public class CookieAuthenticatorTestCase { +class CookieAuthenticatorTestCase { public static class CookieGuardedApplication extends Application { @@ -60,7 +60,7 @@ public void handle(Request request, Response response) { } @Test - public void testCookieAuth1() { + void testCookieAuth1() { CookieGuardedApplication cga = new CookieGuardedApplication(); Component c = new Component(); c.getDefaultHost().attachDefault(cga); diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java index 74cb7c0ee8..bad5b289e5 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java @@ -57,6 +57,6 @@ public void testParsingDigest() { List creq = AuthenticatorUtils.parseRequest(null, authenticate1, null); assertEquals(creq.size(), 1); - assertEquals(authenticate1, AuthenticatorUtils.formatRequest(creq.get(0), null, null)); + assertEquals(authenticate1, AuthenticatorUtils.formatRequest(creq.getFirst(), null, null)); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java b/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java index 4cc0d8a27c..8c7abcf3d8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java +++ b/org.restlet/src/main/java/org/restlet/engine/header/DateWriter.java @@ -37,7 +37,7 @@ public static String write(Date date) { */ public static String write(Date date, boolean cookie) { if (cookie) { - return DateUtils.format(date, DateUtils.FORMAT_RFC_1036.get(0)); + return DateUtils.format(date, DateUtils.FORMAT_RFC_1036.getFirst()); } return DateUtils.format(date); diff --git a/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java b/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java index b40907e599..837308bda6 100644 --- a/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/CertificateAuthenticator.java @@ -52,11 +52,11 @@ protected List getPrincipals(List certificateChain) { ArrayList principals = null; if ((certificateChain != null) && (!certificateChain.isEmpty())) { - Certificate userCert = certificateChain.get(0); + Certificate userCert = certificateChain.getFirst(); - if (userCert instanceof X509Certificate) { + if (userCert instanceof X509Certificate x509Certificate) { principals = new ArrayList<>(); - principals.add(((X509Certificate) userCert).getSubjectX500Principal()); + principals.add(x509Certificate.getSubjectX500Principal()); } return principals; diff --git a/org.restlet/src/main/java/org/restlet/service/ConverterService.java b/org.restlet/src/main/java/org/restlet/service/ConverterService.java index 069d8109b0..0dd71a1709 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConverterService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConverterService.java @@ -281,7 +281,7 @@ public Representation toRepresentation(Object source, Variant target, Resource r resource.getRequest(), resource.getMetadataService()); } else { - target = variants.get(0); + target = variants.getFirst(); } } } diff --git a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java index 5a9f180c01..368887d4a7 100644 --- a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java @@ -57,7 +57,7 @@ public void shouldReturnEnUsAndTextXml() { Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); - assertEquals(ENGLISH_US, pv.getLanguages().get(0)); + assertEquals(ENGLISH_US, pv.getLanguages().getFirst()); } @Test @@ -67,7 +67,7 @@ public void shouldReturnEnAndTextXml() { Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); - assertEquals(ENGLISH, pv.getLanguages().get(0)); + assertEquals(ENGLISH, pv.getLanguages().getFirst()); } // Testing quality priority over parent metadata @@ -78,7 +78,7 @@ public void shouldReturnFrFrAndText() { Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); - assertEquals(FRENCH_FRANCE, pv.getLanguages().get(0)); + assertEquals(FRENCH_FRANCE, pv.getLanguages().getFirst()); } // Testing quality priority over parent metadata @@ -91,7 +91,7 @@ public void shouldReturnFrFrAndXml() { Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(TEXT_XML, pv.getMediaType()); - assertEquals(FRENCH_FRANCE, pv.getLanguages().get(0)); + assertEquals(FRENCH_FRANCE, pv.getLanguages().getFirst()); } // Leveraging parent media types @@ -104,7 +104,7 @@ public void shouldPreferEnUsAndApplicationXml() { Variant pv = connegService.getPreferredVariant(variants, request, ms); assertEquals(APPLICATION_XML, pv.getMediaType()); - assertEquals(ENGLISH_US, pv.getLanguages().get(0)); + assertEquals(ENGLISH_US, pv.getLanguages().getFirst()); } } diff --git a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java index 1ab7df730d..c7e3c044fe 100644 --- a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java @@ -26,11 +26,11 @@ * * @author Thierry Boileau */ -public class ProductTokenTestCase { +class ProductTokenTestCase { @ParameterizedTest(name = "{1} {2}") @MethodSource("mainProductTestCases") - public void testMainProduct( + void testMainProduct( final String userAgent, final String productName, final String productVersion, @@ -93,7 +93,7 @@ private static Stream mainProductTestCases() { } @Test - public void testProductTokens() { + void testProductTokens() { final String userAgent1 = "Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)"; final String userAgent2 = "Advanced Browser (http://www.avantbrowser.com)"; @@ -107,33 +107,33 @@ public void testProductTokens() { List list = ProductReader.read(userAgent1); assertEquals(1, list.size()); - assertEquals("Mozilla", list.get(0).getName()); - assertEquals("4.0", list.get(0).getVersion()); + assertEquals("Mozilla", list.getFirst().getName()); + assertEquals("4.0", list.getFirst().getVersion()); assertEquals( "compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;", - list.get(0).getComment()); + list.getFirst().getComment()); list = ProductReader.read(userAgent2); assertEquals(1, list.size()); - assertEquals(list.get(0).getName(), "Advanced Browser"); - assertNull(list.get(0).getVersion()); - assertEquals(list.get(0).getComment(), "http://www.avantbrowser.com"); + assertEquals("Advanced Browser", list.getFirst().getName()); + assertNull(list.getFirst().getVersion()); + assertEquals("http://www.avantbrowser.com", list.getFirst().getComment()); list = ProductReader.read(userAgent3); assertEquals(1, list.size()); - assertEquals("Mozilla", list.get(0).getName()); - assertEquals("5.0", list.get(0).getVersion()); - assertNull(list.get(0).getComment()); + assertEquals("Mozilla", list.getFirst().getName()); + assertEquals("5.0", list.getFirst().getVersion()); + assertNull(list.getFirst().getComment()); list = ProductReader.read(userAgent4); assertEquals(1, list.size()); - assertEquals("Mozilla", list.get(0).getName()); - assertNull(list.get(0).getVersion()); - assertNull(list.get(0).getComment()); + assertEquals("Mozilla", list.getFirst().getName()); + assertNull(list.getFirst().getVersion()); + assertNull(list.getFirst().getComment()); list = ProductReader.read(userAgent5); assertEquals(3, list.size()); - assertEquals("Mozilla", list.get(0).getName()); + assertEquals("Mozilla", list.getFirst().getName()); assertEquals("5.0", list.get(0).getVersion()); assertEquals("Macintosh; U; PPC Mac OS X; en-US; rv:1.8", list.get(0).getComment()); assertEquals("Gecko", list.get(1).getName()); @@ -145,7 +145,7 @@ public void testProductTokens() { list = ProductReader.read(userAgent6); assertEquals(3, list.size()); - assertEquals("Mozilla", list.get(0).getName()); + assertEquals("Mozilla", list.getFirst().getName()); assertEquals("5.0", list.get(0).getVersion()); assertEquals("X11; U; Linux i686; en-US; rv:1.8.1", list.get(0).getComment()); assertEquals("Gecko", list.get(1).getName()); @@ -157,20 +157,20 @@ public void testProductTokens() { list = ProductReader.read(userAgent7); assertEquals(1, list.size()); - assertEquals("Restlet-Framework", list.get(0).getName()); - assertEquals("2.2-SNAPSHOT", list.get(0).getVersion()); - assertNull(list.get(0).getComment()); + assertEquals("Restlet-Framework", list.getFirst().getName()); + assertEquals("2.2-SNAPSHOT", list.getFirst().getVersion()); + assertNull(list.getFirst().getComment()); } @Test - public void testWriteThenRead() { + void testWriteThenRead() { final List products = new ArrayList<>(); products.add(new Product("Product", "1.2", null)); products.add(new Product("Nre", "1.1m4", "This is a comment")); List list = ProductReader.read(ProductWriter.write(products)); assertEquals(2, list.size()); - assertEquals("Product", list.get(0).getName()); + assertEquals("Product", list.getFirst().getName()); assertEquals("1.2", list.get(0).getVersion()); assertNull(list.get(0).getComment()); assertEquals("Nre", list.get(1).getName()); diff --git a/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java index 9b9b678d3c..02bd56d9f2 100644 --- a/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RecipientInfoTestCase.java @@ -23,10 +23,10 @@ * * @author Jerome Louvel */ -public class RecipientInfoTestCase { +class RecipientInfoTestCase { @Test - public void testVia() { + void testVia() { Header via1a = new Header(HeaderConstants.HEADER_VIA, "1.0 fred, 1.1 nowhere.com (Apache/1.1)"); Header via1b = diff --git a/org.restlet/src/test/java/org/restlet/data/TagTestCase.java b/org.restlet/src/test/java/org/restlet/data/TagTestCase.java index 0976b678ba..c264061ffb 100644 --- a/org.restlet/src/test/java/org/restlet/data/TagTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/TagTestCase.java @@ -23,27 +23,27 @@ * * @author Jerome Louvel */ -public class TagTestCase { +class TagTestCase { @Test - public void testSimpleTag() { + void testSimpleTag() { assertEquals("my-tag", Tag.parse("\"my-tag\"").getName()); assertFalse(Tag.parse("\"my-tag\"").isWeak()); } @Test - public void testInvalidTag() { + void testInvalidTag() { assertNull(Tag.parse("my-tag")); assertNull(Tag.parse("\"my-tag")); } @Test - public void testAllTag() { + void testAllTag() { assertEquals(Tag.ALL.getName(), Tag.parse("*").getName()); } @Test - public void testWeakTag() { + void testWeakTag() { assertEquals(Tag.ALL.getName(), Tag.parse("W/*").getName()); assertTrue(Tag.parse("W/*").isWeak()); @@ -52,7 +52,7 @@ public void testWeakTag() { } @Test - public void testListOfValidTags() { + void testListOfValidTags() { List tags = new ArrayList<>(); new TagReader("\"xyz\", \"r2d2\", \"c3pio\", *").addValues(tags); assertEquals("xyz", tags.get(0).getName()); @@ -63,7 +63,7 @@ public void testListOfValidTags() { } @Test - public void testListOfTagsWithInvalidTag() { + void testListOfTagsWithInvalidTag() { List tags = new ArrayList<>(); new TagReader("\"xyz\", \"r2d2\", c3pio, *").addValues(tags); assertEquals("xyz", tags.get(0).getName()); diff --git a/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java b/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java index 98650970b9..4e51401362 100644 --- a/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ZipClientTestCase.java @@ -30,18 +30,18 @@ * @author Remi Dewitte */ @Disabled("flaky on github") -public class ZipClientTestCase { +class ZipClientTestCase { private File zipFile; @BeforeEach - protected void setUpEach() throws Exception { + void setUpEach() throws Exception { Path testCaseDirectoryPath = Files.createTempDirectory("ZipClientTestCase"); zipFile = testCaseDirectoryPath.resolve("test.zip").toFile(); } @AfterEach - protected void tearDownEach() throws Exception { + void tearDownEach() { zipFile.delete(); } @@ -56,56 +56,57 @@ void testFileClient() throws IOException { String dirEntryReference = zr + "!/dir/"; String test3FileInDirEntryReference = dirEntryReference + "test3.txt"; - // Write test.txt as first entry + // Write test.txt as the first entry ClientResource testFileEntryClientResource = new ClientResource(testFileEntryReference); testFileEntryClientResource.put(new StringRepresentation(text)); - assertEquals(testFileEntryClientResource.getStatus(), Status.SUCCESS_CREATED); + assertEquals(Status.SUCCESS_CREATED, testFileEntryClientResource.getStatus()); // Get the text and compare to the original testFileEntryClientResource.get(); - assertEquals(testFileEntryClientResource.getStatus(), Status.SUCCESS_OK); - assertEquals(testFileEntryClientResource.getResponseEntity().getText(), text); + assertEquals(Status.SUCCESS_OK, testFileEntryClientResource.getStatus()); + assertEquals(text, testFileEntryClientResource.getResponseEntity().getText()); testFileEntryClientResource.release(); - // Write test2.txt as second entry + // Write test2.txt as the second entry ClientResource test2FileEntryClientResource = new ClientResource(test2FileEntryReference); test2FileEntryClientResource.put(new StringRepresentation(text2)); - assertEquals(test2FileEntryClientResource.getStatus(), Status.SUCCESS_OK); + assertEquals(Status.SUCCESS_OK, test2FileEntryClientResource.getStatus()); // Check that the first entry has not been overwritten testFileEntryClientResource.get(); - assertEquals(testFileEntryClientResource.getStatus(), Status.SUCCESS_OK); - assertEquals(testFileEntryClientResource.getResponseEntity().getText(), text); + assertEquals(Status.SUCCESS_OK, testFileEntryClientResource.getStatus()); + assertEquals(text, testFileEntryClientResource.getResponseEntity().getText()); testFileEntryClientResource.release(); // Put a directory ClientResource dirEntryClientResource = new ClientResource(dirEntryReference); dirEntryClientResource.put(new EmptyRepresentation()); - assertEquals(dirEntryClientResource.getStatus(), Status.SUCCESS_OK); + assertEquals(Status.SUCCESS_OK, dirEntryClientResource.getStatus()); dirEntryClientResource.get(); - assertEquals(dirEntryClientResource.getStatus(), Status.SUCCESS_OK); + assertEquals(Status.SUCCESS_OK, dirEntryClientResource.getStatus()); // Add a file inside the directory ClientResource testFileInDirEntryCLientResource = new ClientResource(test3FileInDirEntryReference); testFileInDirEntryCLientResource.put(new StringRepresentation(text)); - assertEquals(testFileInDirEntryCLientResource.getStatus(), Status.SUCCESS_OK); + assertEquals(Status.SUCCESS_OK, testFileInDirEntryCLientResource.getStatus()); // Check that the second entry is still there test2FileEntryClientResource.get(); assertEquals( - test2FileEntryClientResource.getStatus(), Status.SUCCESS_OK, + test2FileEntryClientResource.getStatus(), "Could not get " + test2FileEntryReference); - assertEquals(test2FileEntryClientResource.getResponseEntity().getText(), text2); + assertEquals(text2, test2FileEntryClientResource.getResponseEntity().getText()); // Check that content negotiation does not work ClientResource rTest2 = new ClientResource(zr + "!test2"); assertThrows(ResourceException.class, rTest2::get); - // Try to replace file by directory + // Try to replace a file by directory ClientResource r2d = new ClientResource(test2FileEntryReference + "/"); - assertThrows(ResourceException.class, () -> r2d.put(new EmptyRepresentation())); + final EmptyRepresentation entity = new EmptyRepresentation(); + assertThrows(ResourceException.class, () -> r2d.put(entity)); } } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java index ec7a3b6397..cb6319748c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/MultiPartRepresentationTestCase.java @@ -29,10 +29,10 @@ * * @author Jerome Louvel */ -public class MultiPartRepresentationTestCase { +class MultiPartRepresentationTestCase { @Test - public void testWriteFromParts() throws IOException { + void testWriteFromParts() throws IOException { Path textFilePath = Files.createTempFile("multiPart", ""); Files.write( textFilePath, "this is the content of the file".getBytes(StandardCharsets.UTF_8)); @@ -66,7 +66,7 @@ public void testWriteFromParts() throws IOException { /** Tests the multipart content-type. */ @Test - public void testContentType() { + void testContentType() { MultiPart.ContentSourcePart contentSourcePart = new MultiPart.ContentSourcePart( "field", null, HttpFields.EMPTY, new StringRequestContent("foo")); @@ -78,7 +78,7 @@ public void testContentType() { } @Test - public void testParseIntoParts() throws IOException { + void testParseIntoParts() throws IOException { final String boundary = "-----------------------------1294919323195"; final String multipartEntityContent = """ @@ -100,7 +100,7 @@ public void testParseIntoParts() throws IOException { Path tempDir = Files.createTempDirectory("multipartRepresentationTestCase"); MultiPartRepresentation rep = new MultiPartRepresentation(multipartEntity, tempDir); - Part part1 = rep.getParts().get(0); + Part part1 = rep.getParts().getFirst(); assertEquals("field", part1.getName()); assertNull(part1.getFileName()); assertEquals(3, part1.getLength()); diff --git a/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java index fa8e794fd7..3bfbb107a8 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/CookieReaderTestCase.java @@ -25,7 +25,7 @@ * * @author Jerome Louvel */ -public class CookieReaderTestCase { +class CookieReaderTestCase { /** * Test one cookie header. * @@ -93,7 +93,7 @@ private void testCookieDate(String dateValue) { final Date date = DateUtils.parse(dateValue, DateUtils.FORMAT_RFC_1036); // Rewrite the date - final String newDateValue = DateUtils.format(date, DateUtils.FORMAT_RFC_1036.get(0)); + final String newDateValue = DateUtils.format(date, DateUtils.FORMAT_RFC_1036.getFirst()); // Compare initial and new headers assertEquals(dateValue, newDateValue); @@ -122,7 +122,7 @@ private void testCookieSetting(String headerValue, boolean compare) throws IOExc /** Tests the parsing of cookies. */ @Test - public void testParsing() throws IOException { + void testParsing() throws IOException { // Netscape specification testCookie("CUSTOMER=WILE_E_COYOTE"); testCookie("CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001"); @@ -163,11 +163,11 @@ public void testParsing() throws IOException { } @Test - public void testParsingTooLongMaxAgeShouldBeCapedToIntegerMAX_VALUE() throws IOException { + void testParsingTooLongMaxAgeShouldBeCapedToIntegerMAX_VALUE() throws IOException { CookieSettingReader cr = new CookieSettingReader( "RMS_ADMETA_VISITOR_RMS=27756847%3A240105; max-age=31536000000; path=/; domain=.admeta.com"); CookieSetting cookie = cr.readValue(); - assertEquals(cookie.getMaxAge(), Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, cookie.getMaxAge()); } } diff --git a/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java index 2d90fb9c30..0377c383e5 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/HeaderTestCase.java @@ -30,21 +30,21 @@ * * @author Jerome Louvel */ -public class HeaderTestCase { +class HeaderTestCase { /** Test the {@link HeaderReader#addValues(java.util.Collection)} method. */ @Test - public void testAddValues() { + void testAddValues() { List list = new ArrayList<>(); new EncodingReader("gzip,deflate").addValues(list); - assertEquals(list.size(), 2); - assertEquals(list.get(0), Encoding.GZIP); - assertEquals(list.get(1), Encoding.DEFLATE); + assertEquals(2, list.size()); + assertEquals(Encoding.GZIP, list.get(0)); + assertEquals(Encoding.DEFLATE, list.get(1)); list = new ArrayList<>(); new EncodingReader("gzip,identity, deflate").addValues(list); - assertEquals(list.size(), 2); - assertEquals(list.get(0), Encoding.GZIP); - assertEquals(list.get(1), Encoding.DEFLATE); + assertEquals(2, list.size()); + assertEquals(Encoding.GZIP, list.get(0)); + assertEquals(Encoding.DEFLATE, list.get(1)); list = new ArrayList<>(); new EncodingReader("identity").addValues(list); @@ -69,26 +69,25 @@ public void testAddValues() { tr = new TokenReader("bytes,"); l = tr.readValues(); assertTrue(l.contains("bytes")); - assertEquals(l.size(), 1); + assertEquals(1, l.size()); tr = new TokenReader(""); l = tr.readValues(); - assertEquals(l.size(), 1); + assertEquals(1, l.size()); } @Test - public void testInvalidDate() { + void testInvalidDate() { final String headerValue = "-1"; final Date date = DateUtils.parse(headerValue, DateUtils.FORMAT_RFC_1123); assertNull(date); - final Date unmodifiableDate = DateUtils.unmodifiable(date); - assertNull(unmodifiableDate); + assertNull(DateUtils.unmodifiable(date)); } /** Tests the parsing. */ @Test - public void testParsing() { + void testParsing() { String header1 = "Accept-Encoding,User-Agent"; String header2 = "Accept-Encoding , User-Agent"; final String header3 = "Accept-Encoding,\r\tUser-Agent"; @@ -111,27 +110,27 @@ public void testParsing() { header1 = "gzip;q=1.0, identity;q=0.5 , *;q=0"; ClientInfo clientInfo = new ClientInfo(); PreferenceReader.addEncodings(header1, clientInfo); - assertEquals(clientInfo.getAcceptedEncodings().get(0).getMetadata(), Encoding.GZIP); - assertEquals(clientInfo.getAcceptedEncodings().get(0).getQuality(), 1.0F); - assertEquals(clientInfo.getAcceptedEncodings().get(1).getMetadata(), Encoding.IDENTITY); - assertEquals(clientInfo.getAcceptedEncodings().get(1).getQuality(), 0.5F); - assertEquals(clientInfo.getAcceptedEncodings().get(2).getMetadata(), Encoding.ALL); - assertEquals(clientInfo.getAcceptedEncodings().get(2).getQuality(), 0F); + assertEquals(Encoding.GZIP, clientInfo.getAcceptedEncodings().get(0).getMetadata()); + assertEquals(1.0F, clientInfo.getAcceptedEncodings().get(0).getQuality()); + assertEquals(Encoding.IDENTITY, clientInfo.getAcceptedEncodings().get(1).getMetadata()); + assertEquals(0.5F, clientInfo.getAcceptedEncodings().get(1).getQuality()); + assertEquals(Encoding.ALL, clientInfo.getAcceptedEncodings().get(2).getMetadata()); + assertEquals(0F, clientInfo.getAcceptedEncodings().get(2).getQuality()); // Test the parsing of a "Accept" header header1 = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"; clientInfo = new ClientInfo(); PreferenceReader.addMediaTypes(header1, clientInfo); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getMetadata(), MediaType.TEXT_HTML); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getQuality(), 1.0F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(1).getMetadata(), MediaType.IMAGE_GIF); - assertEquals(clientInfo.getAcceptedMediaTypes().get(1).getQuality(), 1.0F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(2).getMetadata(), MediaType.IMAGE_JPEG); - assertEquals(clientInfo.getAcceptedMediaTypes().get(2).getQuality(), 1.0F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(3).getMetadata(), new MediaType("*")); - assertEquals(clientInfo.getAcceptedMediaTypes().get(3).getQuality(), 0.2F); - assertEquals(clientInfo.getAcceptedMediaTypes().get(4).getMetadata(), MediaType.ALL); - assertEquals(clientInfo.getAcceptedMediaTypes().get(4).getQuality(), 0.2F); + assertEquals(MediaType.TEXT_HTML, clientInfo.getAcceptedMediaTypes().get(0).getMetadata()); + assertEquals(1.0F, clientInfo.getAcceptedMediaTypes().get(0).getQuality()); + assertEquals(MediaType.IMAGE_GIF, clientInfo.getAcceptedMediaTypes().get(1).getMetadata()); + assertEquals(1.0F, clientInfo.getAcceptedMediaTypes().get(1).getQuality()); + assertEquals(MediaType.IMAGE_JPEG, clientInfo.getAcceptedMediaTypes().get(2).getMetadata()); + assertEquals(1.0F, clientInfo.getAcceptedMediaTypes().get(2).getQuality()); + assertEquals(new MediaType("*"), clientInfo.getAcceptedMediaTypes().get(3).getMetadata()); + assertEquals(0.2F, clientInfo.getAcceptedMediaTypes().get(3).getQuality()); + assertEquals(MediaType.ALL, clientInfo.getAcceptedMediaTypes().get(4).getMetadata()); + assertEquals(0.2F, clientInfo.getAcceptedMediaTypes().get(4).getQuality()); // Test a more complex header header1 = @@ -140,8 +139,9 @@ public void testParsing() { + "image/gif, image/jpeg, image/pjpeg, audio/amr, */*"; clientInfo = new ClientInfo(); PreferenceReader.addMediaTypes(header1, clientInfo); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getMetadata(), MediaType.TEXT_HTML); - assertEquals(clientInfo.getAcceptedMediaTypes().get(0).getQuality(), 1.0F); + assertEquals( + MediaType.TEXT_HTML, clientInfo.getAcceptedMediaTypes().getFirst().getMetadata()); + assertEquals(1.0F, clientInfo.getAcceptedMediaTypes().getFirst().getQuality()); } /** @@ -163,7 +163,7 @@ public void testValues(String header, String[] values) { } @Test - public void testEmptyValue() throws IOException { + void testEmptyValue() throws IOException { Header result = HeaderReader.readHeader("My-Header: "); assertNotNull(result); assertEquals("My-Header", result.getName()); diff --git a/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java index 435cc5628a..21ede9e04d 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/DateUtilsTestCase.java @@ -21,29 +21,21 @@ * * @author Jerome Louvel */ -public class DateUtilsTestCase { - - private final String DATE_RFC3339_1 = "1985-04-12T23:20:50.52Z"; - - private final String DATE_RFC3339_2 = "1996-12-19T16:39:57-08:00"; - - private final String DATE_RFC3339_3 = "1990-12-31T23:59:60Z"; - - private final String DATE_RFC3339_4 = "1990-12-31T15:59:60-08:00"; - - private final String DATE_RFC3339_5 = "1937-01-01T12:00:27.87+00:20"; - - private final String DATE_ASC_1 = "Fri Apr 12 23:20:50 1985"; - - private final String DATE_RFC1036_1 = "Friday, 12-Apr-85 23:20:50 GMT"; - - private final String DATE_RFC1123_1 = "Fri, 12 Apr 1985 23:20:50 GMT"; - - private final String DATE_RFC822_1 = "Fri, 12 Apr 85 23:20:50 GMT"; +class DateUtilsTestCase { + + private static final String DATE_RFC3339_1 = "1985-04-12T23:20:50.52Z"; + private static final String DATE_RFC3339_2 = "1996-12-19T16:39:57-08:00"; + private static final String DATE_RFC3339_3 = "1990-12-31T23:59:60Z"; + private static final String DATE_RFC3339_4 = "1990-12-31T15:59:60-08:00"; + private static final String DATE_RFC3339_5 = "1937-01-01T12:00:27.87+00:20"; + private static final String DATE_ASC_1 = "Fri Apr 12 23:20:50 1985"; + private static final String DATE_RFC1036_1 = "Friday, 12-Apr-85 23:20:50 GMT"; + private static final String DATE_RFC1123_1 = "Fri, 12 Apr 1985 23:20:50 GMT"; + private static final String DATE_RFC822_1 = "Fri, 12 Apr 85 23:20:50 GMT"; /** Tests for dates in the RFC 822 format. */ @Test - public void testRfc822() { + void testRfc822() { Date date1 = DateUtils.parse(DATE_RFC822_1, DateUtils.FORMAT_RFC_822); String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_822.get(0)); @@ -53,7 +45,7 @@ public void testRfc822() { /** Tests for dates in the RFC 1123 format. */ @Test - public void testRfc1123() { + void testRfc1123() { Date date1 = DateUtils.parse(DATE_RFC1123_1, DateUtils.FORMAT_RFC_1123); String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_1123.get(0)); @@ -63,38 +55,38 @@ public void testRfc1123() { /** Tests for dates in the RFC 1036 format. */ @Test - public void testRfc1036() { + void testRfc1036() { Date date1 = DateUtils.parse(DATE_RFC1036_1, DateUtils.FORMAT_RFC_1036); - String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_1036.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_1036.getFirst()); assertEquals(DATE_RFC1036_1, dateFormat1); } /** Tests for dates in the RFC 3339 format. */ @Test - public void testAsc() { + void testAsc() { Date date1 = DateUtils.parse(DATE_ASC_1, DateUtils.FORMAT_ASC_TIME); - String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_ASC_TIME.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_ASC_TIME.getFirst()); assertEquals(DATE_ASC_1, dateFormat1); } /** Tests for dates in the RFC 3339 format. */ @Test - public void testRfc3339() { + void testRfc3339() { Date date1 = DateUtils.parse(DATE_RFC3339_1, DateUtils.FORMAT_RFC_3339); Date date2 = DateUtils.parse(DATE_RFC3339_2, DateUtils.FORMAT_RFC_3339); Date date3 = DateUtils.parse(DATE_RFC3339_3, DateUtils.FORMAT_RFC_3339); Date date4 = DateUtils.parse(DATE_RFC3339_4, DateUtils.FORMAT_RFC_3339); Date date5 = DateUtils.parse(DATE_RFC3339_5, DateUtils.FORMAT_RFC_3339); - String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat2 = DateUtils.format(date2, DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat3 = DateUtils.format(date3, DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat4 = DateUtils.format(date4, DateUtils.FORMAT_RFC_3339.get(0)); - String dateFormat5 = DateUtils.format(date5, DateUtils.FORMAT_RFC_3339.get(0)); + String dateFormat1 = DateUtils.format(date1, DateUtils.FORMAT_RFC_3339.getFirst()); + String dateFormat2 = DateUtils.format(date2, DateUtils.FORMAT_RFC_3339.getFirst()); + String dateFormat3 = DateUtils.format(date3, DateUtils.FORMAT_RFC_3339.getFirst()); + String dateFormat4 = DateUtils.format(date4, DateUtils.FORMAT_RFC_3339.getFirst()); + String dateFormat5 = DateUtils.format(date5, DateUtils.FORMAT_RFC_3339.getFirst()); assertEquals(DATE_RFC3339_1, dateFormat1); assertEquals("1996-12-20T00:39:57Z", dateFormat2); @@ -104,7 +96,7 @@ public void testRfc3339() { } @Test - public void unmodifiableDates() { + void unmodifiableDates() { Date now = new Date(); Calendar yesterdayCal = new GregorianCalendar(); yesterdayCal.add(Calendar.DAY_OF_MONTH, -1); diff --git a/org.restlet/src/test/java/org/restlet/engine/util/FormReaderTest.java b/org.restlet/src/test/java/org/restlet/engine/util/FormReaderTestCase.java similarity index 99% rename from org.restlet/src/test/java/org/restlet/engine/util/FormReaderTest.java rename to org.restlet/src/test/java/org/restlet/engine/util/FormReaderTestCase.java index 5a74311b68..db6a51b2e3 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/FormReaderTest.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/FormReaderTestCase.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.restlet.util.Series; -class FormReaderTest { +class FormReaderTestCase { @Nested class ReadParameter { diff --git a/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java b/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java index c9d03d2143..7e1f2e1ec1 100644 --- a/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/TemplateTestCase.java @@ -22,10 +22,10 @@ * * @author Jerome Louvel */ -public class TemplateTestCase { +class TemplateTestCase { @Test - public void testEncodedCharacters() { + void testEncodedCharacters() { Template template = new Template("http://localhost/{token}/bookstore/{bookid}"); String encodedToken = "FtDF91VSX%2F7AN6C39k51ZV510SW%2Fot6SIGstq8XGCcHfOfHbZOZLUD4u%2BGUNK0bBawVZ4GR5TgV7PtRbF%2Bnm9abYJN6AWycdj9J6CLyU4D7Zou36KEjkel%2B0LtlGGhFPVrCvpBuqPy8V8o5IZ9tDys0Py6sXXAtEVbXBYeRYzOvIBzOZkIviIyceVCU%2BlYv%2Fh9k7Fhlb1JGtKUCj3ZDg%2FvJ1Co7dOC1Ho3%2Fe0Fup7k9qgTuCvZRSHcpizaEFPNLp"; @@ -38,7 +38,7 @@ public void testEncodedCharacters() { } @Test - public void testPathMatching() { + void testPathMatching() { Template template = new Template("http://www.mydomain.com/abc/{v1}"); template.setMatchingMode(Template.MODE_STARTS_WITH); template.getDefaultVariable().setType(Variable.TYPE_URI_PATH); @@ -63,7 +63,7 @@ public void testPathMatching() { } @Test - public void testVariableNames() { + void testVariableNames() { Template tpl = new Template("http://{userId}.restlet.com/invoices/{invoiceId}"); tpl.setLogger(Engine.getAnonymousLogger()); List names = tpl.getVariableNames(); @@ -74,7 +74,7 @@ public void testVariableNames() { } @Test - public void testWithPercentChars() { + void testWithPercentChars() { Template template = new Template("abc/{v1}"); template.getDefaultVariable().setType(Variable.TYPE_URI_ALL); Map variables1 = new HashMap<>(); diff --git a/org.restlet/src/test/java/org/restlet/util/SeriesTest.java b/org.restlet/src/test/java/org/restlet/util/SeriesTestCase.java similarity index 99% rename from org.restlet/src/test/java/org/restlet/util/SeriesTest.java rename to org.restlet/src/test/java/org/restlet/util/SeriesTestCase.java index a17fdf81eb..13db334768 100644 --- a/org.restlet/src/test/java/org/restlet/util/SeriesTest.java +++ b/org.restlet/src/test/java/org/restlet/util/SeriesTestCase.java @@ -21,7 +21,7 @@ import org.restlet.data.Form; import org.restlet.data.Parameter; -class SeriesTest { +class SeriesTestCase { /** A parameter whose name is not a key in the map is silently skipped. */ @Test From 9a32fcfaec38eb5df93ee794893818d185ae3a44 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 17:35:30 +0200 Subject: [PATCH 20/30] Take into account Sonar comments --- .../ext/crypto/internal/HttpDigestHelper.java | 4 +- .../org/restlet/ext/gson/GsonTestCase.java | 40 ++-- .../ext/json/JsonpRepresentationTestCase.java | 10 +- .../engine/security/AuthenticatorHelper.java | 4 +- .../engine/security/AuthenticatorUtils.java | 221 ++++++++---------- .../engine/ssl/DefaultSslContextFactory.java | 46 ++-- .../engine/util/DefaultSaxHandler.java | 2 +- .../data/AuthenticationInfoTestCase.java | 4 +- .../connector/ShutdownHookTestCase.java | 18 +- .../util/AlphaNumericComparatorTestCase.java | 4 +- .../resource/AnnotatedResource01TestCase.java | 12 +- .../resource/AnnotatedResource02TestCase.java | 4 +- .../resource/AnnotatedResource03TestCase.java | 4 +- .../resource/AnnotatedResource04TestCase.java | 4 +- .../resource/AnnotatedResource05TestCase.java | 4 +- .../resource/AnnotatedResource06TestCase.java | 4 +- .../resource/AnnotatedResource07TestCase.java | 4 +- .../resource/AnnotatedResource08TestCase.java | 4 +- .../resource/AnnotatedResource12TestCase.java | 4 +- .../resource/AnnotatedResource13TestCase.java | 6 +- .../resource/AnnotatedResource14TestCase.java | 4 +- .../AnnotatedResource15OkTestCase.java | 4 +- .../resource/AnnotatedResource15TestCase.java | 4 +- .../resource/AnnotatedResource16TestCase.java | 4 +- .../resource/AnnotatedResource17TestCase.java | 4 +- .../resource/AnnotatedResource18TestCase.java | 4 +- .../resource/AnnotatedResource20TestCase.java | 6 +- .../restlet/resource/DirectoryTestCase.java | 141 +++++------ .../restlet/security/SecurityTestCase.java | 26 +-- 29 files changed, 274 insertions(+), 326 deletions(-) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java index 086fede101..090d3c7098 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpDigestHelper.java @@ -8,7 +8,6 @@ */ package org.restlet.ext.crypto.internal; -import java.io.IOException; import java.util.Base64; import java.util.logging.Level; import org.restlet.Context; @@ -88,8 +87,7 @@ public void formatRequest( ChallengeWriter cw, ChallengeRequest challenge, Response response, - Series
httpHeaders) - throws IOException { + Series
httpHeaders) { if (challenge.getRealm() != null) { cw.appendQuotedChallengeParameter(REALM, challenge.getRealm()); diff --git a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java index 9a5dcfd898..2f6af0444b 100644 --- a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java +++ b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java @@ -31,7 +31,7 @@ * * @author Neal Mi */ -public class GsonTestCase { +class GsonTestCase { private static class User { private final boolean active; @@ -93,21 +93,21 @@ public boolean isActive() { private User user; @BeforeEach - public void setUpEach() throws Exception { + void setUpEach() { user = new User("hello", "secret", 1, true, new Date(), new Date()); gsonConverter = new GsonConverter(); } @Test - public final void testCreateMediaTypeT() { + final void testCreateMediaTypeT() { Representation rep = new GsonRepresentation<>(user); assertNotNull(rep); - assertEquals(rep.getMediaType(), MediaType.APPLICATION_JSON); + assertEquals(MediaType.APPLICATION_JSON, rep.getMediaType()); } @Test - public final void testCreateRepresentationClassOfT() { + final void testCreateRepresentationClassOfT() { Representation rep = new GsonRepresentation<>(user); Representation rep1 = new GsonRepresentation<>(rep, User.class); @@ -116,23 +116,23 @@ public final void testCreateRepresentationClassOfT() { } @Test - public final void testGsonRepresentationRead() throws IOException { + final void testGsonRepresentationRead() throws IOException { final String userAsJsonString = "{\"loginId\":\"hello\",\"password\":\"secret\",\"rate\":1,\"active\":true,\"createAt\":\"2012-05-20T15:41:01.489+08:00\",\"lastLogin\":\"2012-05-20T15:41:01.489+08:00\"}"; Representation source = new StringRepresentation(userAsJsonString, MediaType.APPLICATION_JSON); GsonRepresentation gsonRep = new GsonRepresentation<>(source, User.class); - User user = gsonRep.getObject(); + User parsedUser = gsonRep.getObject(); - assertNotNull(user); - assertEquals("hello", user.getLoginId()); - assertEquals("secret", user.getPassword()); - assertEquals(1, user.getRate()); - assertTrue(user.isActive()); + assertNotNull(parsedUser); + assertEquals("hello", parsedUser.getLoginId()); + assertEquals("secret", parsedUser.getPassword()); + assertEquals(1, parsedUser.getRate()); + assertTrue(parsedUser.isActive()); DateTime time = new DateTime("2012-05-20T15:41:01.489+08:00"); - assertEquals(time.getMillis(), user.getCreateAt().getTime()); - assertEquals(time.getMillis(), user.getLastLogin().getTime()); + assertEquals(time.getMillis(), parsedUser.getCreateAt().getTime()); + assertEquals(time.getMillis(), parsedUser.getLastLogin().getTime()); GsonRepresentation gsonRep1 = new GsonRepresentation<>(source, User.class); gsonRep1.getBuilder().setVersion(1.0); @@ -147,7 +147,7 @@ public final void testGsonRepresentationRead() throws IOException { } @Test - public final void testGsonRepresentationWrite() throws IOException { + final void testGsonRepresentationWrite() throws IOException { GsonRepresentation source = new GsonRepresentation<>(user); assertEquals(User.class, source.getObjectClass()); @@ -166,7 +166,7 @@ public final void testGsonRepresentationWrite() throws IOException { } @Test - public final void testScoreObjectVariantResource() { + final void testScoreObjectVariantResource() { Variant v = new Variant(MediaType.APPLICATION_JSON); Representation source = new GsonRepresentation<>(user); @@ -178,7 +178,7 @@ public final void testScoreObjectVariantResource() { } @Test - public final void testScoreRepresentationClassOfTResource() { + final void testScoreRepresentationClassOfTResource() { Representation source = new GsonRepresentation(user); float score = gsonConverter.score(source, User.class, null); @@ -191,7 +191,7 @@ public final void testScoreRepresentationClassOfTResource() { } @Test() - public final void testToObjectRepresentationClassOfTResource() throws IOException { + final void testToObjectRepresentationClassOfTResource() throws IOException { Representation source = new GsonRepresentation<>(user); User u = gsonConverter.toObject(source, User.class, null); @@ -209,11 +209,11 @@ public final void testToObjectRepresentationClassOfTResource() throws IOExceptio } @Test - public final void testToRepresentationObjectVariantResource() throws IOException { + final void testToRepresentationObjectVariantResource() throws IOException { Variant v = new Variant(MediaType.APPLICATION_JSON); Representation rep = gsonConverter.toRepresentation(user, v, null); assertNotNull(rep); - assertEquals(rep.getMediaType(), MediaType.APPLICATION_JSON); + assertEquals(MediaType.APPLICATION_JSON, rep.getMediaType()); Variant v1 = new Variant(MediaType.APPLICATION_XML); Representation rep1 = gsonConverter.toRepresentation(user, v1, null); diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java index 1cd2b828d1..6aa0876138 100644 --- a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpRepresentationTestCase.java @@ -22,7 +22,7 @@ * * @author Cyril Lakech */ -public class JsonpRepresentationTestCase { +class JsonpRepresentationTestCase { public static final String CALLBACK = "callback"; @@ -31,7 +31,7 @@ public class JsonpRepresentationTestCase { public static final String JSONP_STATUS_BODY = "({\"status\":,\"body\":});"; @Test - public void testGetSizeJson() { + void testGetSizeJson() { JsonpRepresentation jsonpRepresentation = new JsonpRepresentation(CALLBACK, SUCCESS_OK, new JsonRepresentation(JSON_SAMPLE)); @@ -47,7 +47,7 @@ public void testGetSizeJson() { } @Test - public void testGetSize_with_text_is_UNKNOWN_SIZE() { + void testGetSize_with_text_is_UNKNOWN_SIZE() { JsonpRepresentation jsonpRepresentation = new JsonpRepresentation( CALLBACK, @@ -62,7 +62,7 @@ public void testGetSize_with_text_is_UNKNOWN_SIZE() { } @Test - public void testWrite() throws Exception { + void testWrite() throws Exception { JsonpRepresentation jsonpRepresentation = new JsonpRepresentation(CALLBACK, SUCCESS_OK, new JsonRepresentation(JSON_SAMPLE)); @@ -78,7 +78,7 @@ public void testWrite() throws Exception { // with a text representation, apostrophes are escaped and the text is embedded // between 2 apostrophes @Test - public void testWrite_with_text_then_apostrophe_are_escaped() throws Exception { + void testWrite_with_text_then_apostrophe_are_escaped() throws Exception { JsonpRepresentation jsonpRepresentation = new JsonpRepresentation( CALLBACK, diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java index baea3341f3..ebebdb71db 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorHelper.java @@ -8,7 +8,6 @@ */ package org.restlet.engine.security; -import java.io.IOException; import java.util.logging.Logger; import org.restlet.Context; import org.restlet.Request; @@ -65,8 +64,7 @@ public void formatRequest( ChallengeWriter cw, ChallengeRequest challenge, Response response, - Series
httpHeaders) - throws IOException {} + Series
httpHeaders) {} /** * Formats a challenge response as raw credentials. diff --git a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java index 63a2e6e4d5..ab7db1abe7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/AuthenticatorUtils.java @@ -63,34 +63,35 @@ public static String formatAuthenticationInfo(AuthenticationInfo info) { ChallengeWriter cw = new ChallengeWriter(); boolean firstParameter = true; - if (info != null) { - if (info.getNextServerNonce() != null && !info.getNextServerNonce().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendQuotedChallengeParameter("nextnonce", info.getNextServerNonce()); - firstParameter = false; - } + if (info == null) { + return cw.toString(); + } - if (info.getQuality() != null && !info.getQuality().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendChallengeParameter("qop", info.getQuality()); - firstParameter = false; + if (info.getNextServerNonce() != null && !info.getNextServerNonce().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendQuotedChallengeParameter("nextnonce", info.getNextServerNonce()); + firstParameter = false; + } - if (info.getNonceCount() > 0) { - cw.appendChallengeParameter("nc", formatNonceCount(info.getNonceCount())); - } - } + if (info.getQuality() != null && !info.getQuality().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendChallengeParameter("qop", info.getQuality()); + firstParameter = false; - if (info.getResponseDigest() != null && !info.getResponseDigest().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendQuotedChallengeParameter("rspauth", info.getResponseDigest()); - firstParameter = false; + if (info.getNonceCount() > 0) { + cw.appendChallengeParameter("nc", formatNonceCount(info.getNonceCount())); } + } - if (info.getClientNonce() != null && !info.getClientNonce().isEmpty()) { - cw.setFirstChallengeParameter(firstParameter); - cw.appendChallengeParameter("cnonce", info.getClientNonce()); - firstParameter = false; - } + if (info.getResponseDigest() != null && !info.getResponseDigest().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendQuotedChallengeParameter("rspauth", info.getResponseDigest()); + firstParameter = false; + } + + if (info.getClientNonce() != null && !info.getClientNonce().isEmpty()) { + cw.setFirstChallengeParameter(firstParameter); + cw.appendChallengeParameter("cnonce", info.getClientNonce()); } return cw.toString(); @@ -142,26 +143,10 @@ public static String formatRequest( if (challenge.getRawValue() != null) { cw.append(challenge.getRawValue()); } else { - AuthenticatorHelper helper = - Engine.getInstance().findHelper(challenge.getScheme(), false, true); + AuthenticatorHelper helper = findHelper(challenge.getScheme(), false, true); if (helper != null) { - try { - helper.formatRequest(cw, challenge, response, httpHeaders); - } catch (Exception e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Unable to format the challenge request: " + challenge, - e); - } - } else { - result = "?"; - Context.getCurrentLogger() - .warning( - "Challenge scheme " - + challenge.getScheme() - + " not supported by the Restlet engine."); + helper.formatRequest(cw, challenge, response, httpHeaders); } } @@ -202,25 +187,10 @@ public static String formatResponse( if (challenge.getRawValue() != null) { cw.append(challenge.getRawValue()); } else { - AuthenticatorHelper helper = - Engine.getInstance().findHelper(challenge.getScheme(), true, false); + AuthenticatorHelper helper = findHelper(challenge.getScheme(), true, false); if (helper != null) { - try { - helper.formatResponse(cw, challenge, request, httpHeaders); - } catch (Exception e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Unable to format the challenge response: " + challenge, - e); - } - } else { - Context.getCurrentLogger() - .warning( - "Challenge scheme " - + challenge.getScheme() - + " not supported by the Restlet engine."); + helper.formatResponse(cw, challenge, request, httpHeaders); } } @@ -236,56 +206,73 @@ public static String formatResponse( * @param header The header value to parse. * @return The equivalent {@link AuthenticationInfo} instance. */ - public static AuthenticationInfo parseAuthenticationInfo(String header) { - AuthenticationInfo result = null; + public static AuthenticationInfo parseAuthenticationInfo(final String header) { HeaderReader hr = new HeaderReader<>(header); + Parameter param; try { - String nextNonce = null; - String qop = null; - String responseAuth = null; - String cnonce = null; - int nonceCount = 0; - Parameter param = hr.readParameter(); + param = hr.readParameter(); + } catch (IOException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + e, + () -> "Unable to parse the authentication info header: " + header); + return null; + } + String nextNonce = null; + String qop = null; + String responseAuth = null; + String cnonce = null; + String nonceCountAsString = null; + + try { while (param != null) { - try { - if ("nextnonce".equals(param.getName())) { - nextNonce = param.getValue(); - } else if ("qop".equals(param.getName())) { - qop = param.getValue(); - } else if ("rspauth".equals(param.getName())) { - responseAuth = param.getValue(); - } else if ("cnonce".equals(param.getName())) { - cnonce = param.getValue(); - } else if ("nc".equals(param.getName())) { - nonceCount = Integer.parseInt(param.getValue(), 16); - } - - if (hr.skipValueSeparator()) { - param = hr.readParameter(); - } else { - param = null; - } - } catch (Exception e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Unable to parse the authentication info header parameter", - e); + if ("nextnonce".equals(param.getName())) { + nextNonce = param.getValue(); + } else if ("qop".equals(param.getName())) { + qop = param.getValue(); + } else if ("rspauth".equals(param.getName())) { + responseAuth = param.getValue(); + } else if ("cnonce".equals(param.getName())) { + cnonce = param.getValue(); + } else if ("nc".equals(param.getName())) { + nonceCountAsString = param.getValue(); } - } - result = new AuthenticationInfo(nextNonce, nonceCount, cnonce, qop, responseAuth); + param = + hr.skipValueSeparator() + ? hr.readParameter() // next parameter + : null; // end of header + } } catch (IOException e) { Context.getCurrentLogger() .log( Level.WARNING, - "Unable to parse the authentication info header: " + header, + "Unable to parse the authentication info header parameter", e); } - return result; + int nonceCount = getNonceCount(nonceCountAsString); + + return new AuthenticationInfo(nextNonce, nonceCount, cnonce, qop, responseAuth); + } + + private static int getNonceCount(final String nonceCountAsString) { + if (nonceCountAsString == null) { + return 0; + } + try { + return Integer.parseInt(nonceCountAsString, 16); + } catch (NumberFormatException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + e, + () -> "Unable to parse the nonce count value: " + nonceCountAsString); + } + return 0; } /** @@ -303,19 +290,11 @@ public static List parseRequest( if (header != null) { result = new ChallengeRequestReader(header).readValues(); for (ChallengeRequest cr : result) { - // Give a chance to the authenticator helper to do further - // parsing - AuthenticatorHelper helper = - Engine.getInstance().findHelper(cr.getScheme(), true, false); + // Give a chance to the authenticator helper to do further parsing + AuthenticatorHelper helper = findHelper(cr.getScheme(), true, false); if (helper != null) { helper.parseRequest(cr, response, httpHeaders); - } else { - Context.getCurrentLogger() - .warning( - "Couldn't find any helper support the " - + cr.getScheme() - + " challenge scheme."); } } } @@ -350,17 +329,10 @@ public static ChallengeResponse parseResponse( if (result != null) { // Give a chance to the authenticator helper to do further parsing - AuthenticatorHelper helper = - Engine.getInstance().findHelper(result.getScheme(), true, false); + AuthenticatorHelper helper = findHelper(result.getScheme(), true, false); if (helper != null) { helper.parseResponse(result, request, httpHeaders); - } else { - Context.getCurrentLogger() - .warning( - "Couldn't find any helper support the " - + result.getScheme() - + " challenge scheme."); } } @@ -414,23 +386,32 @@ public static void update( public static Reference updateReference( Reference resourceRef, ChallengeResponse challengeResponse, Request request) { if (challengeResponse != null && challengeResponse.getRawValue() == null) { - AuthenticatorHelper helper = - Engine.getInstance().findHelper(challengeResponse.getScheme(), true, false); + AuthenticatorHelper helper = findHelper(challengeResponse.getScheme(), true, false); if (helper != null) { resourceRef = helper.updateReference(resourceRef, challengeResponse, request); - } else { - Context.getCurrentLogger() - .warning( - "Challenge scheme " - + challengeResponse.getScheme() - + " not supported by the Restlet engine."); } } return resourceRef; } + private static AuthenticatorHelper findHelper( + final ChallengeScheme challengeScheme, + final boolean clientSide, + final boolean serverSide) { + final AuthenticatorHelper helper = + Engine.getInstance().findHelper(challengeScheme, clientSide, serverSide); + if (helper == null) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Challenge scheme {0} not supported by the Restlet engine.", + challengeScheme); + } + return helper; + } + /** * Private constructor to ensure that the class acts as a true utility class i.e., it isn't * instantiable and extensible. diff --git a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java index 77b58ccbbb..02f1b6fff6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/ssl/DefaultSslContextFactory.java @@ -166,6 +166,12 @@ */ public class DefaultSslContextFactory extends SslContextFactory { + private static final String JAVAX_NET_SSL_KEY_PASSWORD = "javax.net.ssl.keyPassword"; + private static final String JAVAX_NET_SSL_KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword"; + private static final String USER_HOME = "user.home"; + private static final String JAVAX_NET_SSL_TRUST_STORE_PASSWORD = + "javax.net.ssl.trustStorePassword"; + /** The whitespace-separated list of disabled cipher suites. */ private volatile String[] disabledCipherSuites = null; @@ -178,36 +184,38 @@ public class DefaultSslContextFactory extends SslContextFactory { /** The whitespace-separated list of enabled SSL protocols. */ private volatile String[] enabledProtocols = null; + private static final String SUN_X509 = "SunX509"; + /** The name of the KeyManager algorithm. */ private volatile String keyManagerAlgorithm = - System.getProperty("ssl.KeyManagerFactory.algorithm", "SunX509"); + System.getProperty("ssl.KeyManagerFactory.algorithm", SUN_X509); /** The password for the key in the keystore (as a String). */ private volatile char[] keyStoreKeyPassword = (System.getProperty( - "javax.net.ssl.keyPassword", - System.getProperty("javax.net.ssl.keyStorePassword")) + JAVAX_NET_SSL_KEY_PASSWORD, + System.getProperty(JAVAX_NET_SSL_KEY_STORE_PASSWORD)) != null) ? System.getProperty( - "javax.net.ssl.keyPassword", - System.getProperty("javax.net.ssl.keyStorePassword")) + JAVAX_NET_SSL_KEY_PASSWORD, + System.getProperty(JAVAX_NET_SSL_KEY_STORE_PASSWORD)) .toCharArray() : null; /** The password for the keystore (as a String). */ private volatile char[] keyStorePassword = - (System.getProperty("javax.net.ssl.keyStorePassword") != null) - ? System.getProperty("javax.net.ssl.keyStorePassword").toCharArray() + (System.getProperty(JAVAX_NET_SSL_KEY_STORE_PASSWORD) != null) + ? System.getProperty(JAVAX_NET_SSL_KEY_STORE_PASSWORD).toCharArray() : null; /** The path to the KeyStore file. */ private volatile String keyStorePath = System.getProperty( "javax.net.ssl.keyStore", - (System.getProperty("user.home") != null) - ? ((System.getProperty("user.home").endsWith("/")) - ? System.getProperty("user.home") + ".keystore" - : System.getProperty("user.home") + "/.keystore") + (System.getProperty(USER_HOME) != null) + ? ((System.getProperty(USER_HOME).endsWith("/")) + ? System.getProperty(USER_HOME) + ".keystore" + : System.getProperty(USER_HOME) + "/.keystore") : null); /** The name of the keystore provider. */ @@ -227,12 +235,12 @@ public class DefaultSslContextFactory extends SslContextFactory { /** The name of the TrustManager algorithm. */ private volatile String trustManagerAlgorithm = - System.getProperty("ssl.TrustManagerFactory.algorithm", "SunX509"); + System.getProperty("ssl.TrustManagerFactory.algorithm", SUN_X509); /** The password for the trust store keystore. */ private volatile char[] trustStorePassword = - (System.getProperty("javax.net.ssl.trustStorePassword") != null) - ? System.getProperty("javax.net.ssl.trustStorePassword").toCharArray() + (System.getProperty(JAVAX_NET_SSL_TRUST_STORE_PASSWORD) != null) + ? System.getProperty(JAVAX_NET_SSL_TRUST_STORE_PASSWORD).toCharArray() : null; /** The path to the trust store (keystore) file. */ @@ -625,15 +633,15 @@ public void init(Series helperParameters) { helperParameters.getFirstValue( "keyManagerAlgorithm", true, - System.getProperty("ssl.KeyManagerFactory.algorithm", "SunX509"))); + System.getProperty("ssl.KeyManagerFactory.algorithm", SUN_X509))); setKeyStorePassword( helperParameters.getFirstValue( "keyStorePassword", true, - System.getProperty("javax.net.ssl.keyStorePassword", ""))); + System.getProperty(JAVAX_NET_SSL_KEY_STORE_PASSWORD, ""))); setKeyStoreKeyPassword( helperParameters.getFirstValue( - "keyPassword", true, System.getProperty("javax.net.ssl.keyPassword"))); + "keyPassword", true, System.getProperty(JAVAX_NET_SSL_KEY_PASSWORD))); if (this.keyStoreKeyPassword == null) { this.keyStoreKeyPassword = this.keyStorePassword; @@ -654,12 +662,12 @@ public void init(Series helperParameters) { helperParameters.getFirstValue( "trustManagerAlgorithm", true, - System.getProperty("ssl.TrustManagerFactory.algorithm", "SunX509"))); + System.getProperty("ssl.TrustManagerFactory.algorithm", SUN_X509))); setTrustStorePassword( helperParameters.getFirstValue( "trustStorePassword", true, - System.getProperty("javax.net.ssl.trustStorePassword"))); + System.getProperty(JAVAX_NET_SSL_TRUST_STORE_PASSWORD))); setTrustStorePath( helperParameters.getFirstValue( "trustStorePath", true, System.getProperty("javax.net.ssl.trustStore"))); diff --git a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java index a786933516..61214d3ef5 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java @@ -163,7 +163,7 @@ public void warning(SAXParseException x) throws SAXException { + ", column #" + x.getColumnNumber(); if (debug) { - Context.getCurrentLogger().log(Level.CONFIG, msg, x); + Context.getCurrentLogger().log(Level.CONFIG, x, () -> msg); } else { logger.config(msg + ": " + x.getLocalizedMessage()); } diff --git a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java index b6e6a4afd6..9da949c4e6 100644 --- a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java @@ -38,11 +38,11 @@ public void testAuthenticationInfoHeaderParse() { public void testCnonce() { AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); - assertEquals(authInfo.getClientNonce(), "testcnonce"); + assertEquals("testcnonce", authInfo.getClientNonce()); String newCnonce = "newcnonce"; authInfo.setClientNonce(newCnonce); - assertEquals(authInfo.getClientNonce(), "newcnonce"); + assertEquals("newcnonce", authInfo.getClientNonce()); } /** Equality tests. */ diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java index 824d18184f..b100942af6 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/ShutdownHookTestCase.java @@ -70,7 +70,7 @@ void whenServerIsNotHandlingRequestThenItStopsImmediately() throws Exception { * didn't wait. */ @Test - public void whenServerIsHandlingBlockingRequestThenItStopsImmediately() throws Exception { + void whenServerIsHandlingBlockingRequestThenItStopsImmediately() throws Exception { // Given a server resource that takes 1 min to send a response final Lock lock = new Lock("Server"); final Restlet hangingRestlet = newHangingAndLockedRestlet(Duration.ofMinutes(1), lock); @@ -109,7 +109,7 @@ public void whenServerIsHandlingBlockingRequestThenItStopsImmediately() throws E * waited the expected amount of time before shutting down. */ @Test - public void whenServerIsHandlingBlockingRequestThenItGracefullyWaitsFor1SecondBeforeStopping() + void whenServerIsHandlingBlockingRequestThenItGracefullyWaitsFor1SecondBeforeStopping() throws Exception { // Given a server resource that takes 1 min to send a response final Lock serverLock = new Lock("Server"); @@ -153,7 +153,7 @@ public void whenServerIsHandlingBlockingRequestThenItGracefullyWaitsFor1SecondBe * request is not taken into account. */ @Test - public void whenServerIsHandlingBlockingRequestThenItRefusesNewRequest() throws Exception { + void whenServerIsHandlingBlockingRequestThenItRefusesNewRequest() throws Exception { // Given a server resource that takes 1 min to send a response final Lock lock = new Lock("Server"); final Restlet hangingRestlet = newHangingAndLockedRestlet(Duration.ofMinutes(1), lock); @@ -202,7 +202,7 @@ public void whenServerIsHandlingBlockingRequestThenItRefusesNewRequest() throws * server, and checking that the request has been handled. */ @Test - public void whenServerIsHandlingLongRequestThenRequestIsHandledCorrectlyBeforeStopping() + void whenServerIsHandlingLongRequestThenRequestIsHandledCorrectlyBeforeStopping() throws Exception { // Given a server resource that takes 1 sec to send a response final Lock lock = new Lock("Server"); @@ -247,7 +247,7 @@ private static void assertIntervalBetweenDatesEquals( assertTrue( isDateDifferenceNearlyEqualToExpectedDuration, String.format( - "Expected delay: %d second(s) versus %d second(s)\n", + "Expected delay: %d second(s) versus %d second(s)%n", expectedDuration.toMillis(), dateDifference.toMillis())); } @@ -263,7 +263,7 @@ private static void assertIntervalBetweenDatesIsLessThan( assertTrue( dateDifferenceIsLessThanExpectedDuration, String.format( - "difference between dates [%d second(s)] is higher than expected: %d second(s)\n", + "difference between dates [%d second(s)] is higher than expected: %d second(s)%n", dateDifference.getSeconds(), expectedDuration.getSeconds())); } @@ -282,7 +282,7 @@ private Server startServer( Engine.getInstance() .getRegisteredServers() - .add(0, new HttpServerHelper(null)); // Creates a Jetty server helper manually + .addFirst(new HttpServerHelper(null)); // Creates a Jetty server helper manually // 0 port means it will be computed when the server starts Server server = new Server( @@ -380,7 +380,7 @@ public void run() { } finally { stoppedAt = Instant.now(); lock.unlock(); - LOGGER.log(Level.FINE, "Client state: " + cr.getStatus()); + LOGGER.log(Level.FINE, "Client state: {0}", cr.getStatus()); } } } @@ -414,6 +414,6 @@ private static void log(final String message) { } private static void log(final String message, final Exception exception) { - LOGGER.log(Level.INFO, Instant.now().toString() + " " + message, exception); + LOGGER.log(Level.INFO, exception, () -> Instant.now().toString() + " " + message); } } diff --git a/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java index cf35a3bac5..7c563a418d 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/AlphaNumericComparatorTestCase.java @@ -25,7 +25,7 @@ * * @author Davide Angelocola */ -public class AlphaNumericComparatorTestCase { +class AlphaNumericComparatorTestCase { private static List refs(String... uris) { List result = new LinkedList<>(); @@ -44,7 +44,7 @@ private static List refs(String... uris) { refs("1", "1.0", "1.1", "1.1.1", "2", "2.0", "2.2", "2.2.2", "3", "3.0", "3.3"); @Test - public void testBug() { + void testBug() { List result = new ArrayList<>(unsorted); result.sort(new AlphaNumericComparator()); assertEquals(expected, result); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java index ad64771e5d..f865e653cc 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource01TestCase.java @@ -24,7 +24,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource01TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource01TestCase extends AbstractAnnotatedResourceWithFinderTestCase { private MyResource01 myResource; @@ -40,12 +40,12 @@ void configureFinder(Finder finder) { } @Test - public void testDelete() { + void testDelete() { assertEquals("Done", myResource.remove()); } @Test - public void testGet() throws IOException, ResourceException { + void testGet() throws IOException, ResourceException { MyBean myBean = myResource.represent(); assertNotNull(myBean); assertEquals("myName", myBean.getName()); @@ -60,18 +60,18 @@ public void testGet() throws IOException, ResourceException { } @Test - public void testOptions() { + void testOptions() { assertEquals("MyDescription", myResource.describe()); } @Test - public void testPost() { + void testPost() { MyBean myBean = new MyBean("myName", "myDescription"); assertTrue(myResource.accept(myBean)); } @Test - public void testPut() throws ResourceException { + void testPut() throws ResourceException { // Get current representation MyBean myBean = myResource.represent(); assertNotNull(myBean); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java index 8e1be40824..eb898ed846 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource02TestCase.java @@ -21,7 +21,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource02TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource02TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -29,7 +29,7 @@ void configureFinder(Finder finder) { } @Test - public void testGet() throws IOException, ResourceException { + void testGet() throws IOException, ResourceException { Representation result = clientResource.get(MediaType.APPLICATION_ATOM); assertNotNull(result); assertEquals("", result.getText()); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java index 6cb1ab7f04..8ee49d19c9 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource03TestCase.java @@ -18,7 +18,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource03TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource03TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -26,7 +26,7 @@ void configureFinder(Finder finder) { } @Test - public void testGet() throws ResourceException { + void testGet() throws ResourceException { Status status = null; try { clientResource.get(); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java index d6402875e3..655ed4b61d 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource04TestCase.java @@ -24,7 +24,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource04TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource04TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -33,7 +33,7 @@ void configureFinder(Finder finder) { @ParameterizedTest @MethodSource("argumentsProvider") - public void testGet(final MediaType responseMediaType, final String responseBody) + void testGet(final MediaType responseMediaType, final String responseBody) throws IOException, ResourceException { Representation result = clientResource.get(responseMediaType); assertNotNull(result); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java index 2aeeb625c2..08c1164b3b 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource05TestCase.java @@ -21,7 +21,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource05TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource05TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -29,7 +29,7 @@ void configureFinder(Finder finder) { } @Test - public void testPost() throws IOException, ResourceException { + void testPost() throws IOException, ResourceException { Representation result = clientResource.post("[\"root\"]", MediaType.APPLICATION_JSON); assertNotNull(result); assertEquals("[\"root\"]", result.getText()); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java index 404264948b..34d7d055ca 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource06TestCase.java @@ -21,7 +21,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource06TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource06TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -29,7 +29,7 @@ void configureFinder(Finder finder) { } @Test - public void testPost() throws IOException, ResourceException { + void testPost() throws IOException, ResourceException { Representation result = clientResource.post("[\"root\"]", MediaType.APPLICATION_JSON); assertNotNull(result); assertEquals("[\"root\"]2", result.getText()); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java index b6119fc516..997f28a97a 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource07TestCase.java @@ -22,7 +22,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource07TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource07TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -30,7 +30,7 @@ void configureFinder(Finder finder) { } @Test - public void testPost() throws IOException, ResourceException { + void testPost() throws IOException, ResourceException { Representation input = new StringRepresentation("[\"root\"]", MediaType.APPLICATION_JSON); Representation result = clientResource.post(input); assertNotNull(result); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java index 7c6fdc2892..5b6d69daae 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource08TestCase.java @@ -29,7 +29,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource08TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource08TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -38,7 +38,7 @@ void configureFinder(Finder finder) { @ParameterizedTest @MethodSource("argumentsProvider") - public void testPost( + void testPost( final MediaType requestBodyMediaType, final String requestBody, final MediaType responseBodyMediaType, diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java index 34bce8a1a4..525e63f8a6 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource12TestCase.java @@ -20,7 +20,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource12TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource12TestCase extends AbstractAnnotatedResourceWithFinderTestCase { private MyResource12 myResource; @@ -36,7 +36,7 @@ void configureFinder(Finder finder) { } @Test - public void testPutGet() { + void testPutGet() { Form myForm = myResource.represent(); assertNull(myForm); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java index b263e99230..ca0babcd20 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource13TestCase.java @@ -20,7 +20,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource13TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource13TestCase extends AbstractAnnotatedResourceWithFinderTestCase { private MyResource13 myResource; @@ -41,12 +41,12 @@ protected void tearDownEach() { } @Test - public void testQuery() { + void testQuery() { Contact contact = myResource.retrieve(); assertNotNull(contact); LightContact lightContact = myResource.retrieveLight(); - assertNotEquals(lightContact.getClass(), Contact.class); + assertNotEquals(Contact.class, lightContact.getClass()); assertNotNull(lightContact); FullContact fullContact = myResource.retrieveFull(); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java index 665f9b84df..c86a3daffa 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource14TestCase.java @@ -28,7 +28,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource14TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource14TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -46,7 +46,7 @@ static Stream argumentsProvider() { @ParameterizedTest @MethodSource("argumentsProvider") - public void testQuery( + void testQuery( final MediaType requestEntityMediaType, final MediaType responseMediaTypePreference, final String responseEntityAsText) diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java index 5d18a91a62..5c428e6e1c 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15OkTestCase.java @@ -20,7 +20,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource15OkTestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource15OkTestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -28,7 +28,7 @@ void configureFinder(Finder finder) { } @Test - public void shouldFailBecauseServerResourceAnnotationResidesOnGenericServerResource() { + void shouldFailBecauseServerResourceAnnotationResidesOnGenericServerResource() { MyBean myBean = new MyBean("test", "description"); Representation rep = clientResource.post(myBean, MediaType.APPLICATION_JAVA_OBJECT); assertNotNull(rep); diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java index 00afa564dc..afdedb02fd 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource15TestCase.java @@ -20,7 +20,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource15TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource15TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -34,7 +34,7 @@ void configureFinder(Finder finder) { * AbstractGenericAnnotatedServerResource class). */ @Test - public void shouldFailBecauseServerResourceAnnotationResidesOnGenericServerResource() { + void shouldFailBecauseServerResourceAnnotationResidesOnGenericServerResource() { MyBean myBean = new MyBean("test", "description"); ResourceException resourceException = assertThrows( diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java index bd58e73909..067786542d 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource16TestCase.java @@ -21,7 +21,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource16TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource16TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -29,7 +29,7 @@ void configureFinder(Finder finder) { } @Test - public void testQuery() { + void testQuery() { final MyBean myBean = new MyBean("test", "description"); final Representation rep = clientResource.post( diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java index bd909a0d00..c10b819af6 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource17TestCase.java @@ -23,7 +23,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource17TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource17TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -31,7 +31,7 @@ void configureFinder(Finder finder) { } @Test - public void testQuery() throws IOException, ClassNotFoundException { + void testQuery() throws IOException, ClassNotFoundException { MyBean myBean = new MyBean("test", "description"); Representation rep = clientResource.post( diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java index 31136dffdd..1707ea07d8 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource18TestCase.java @@ -23,7 +23,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource18TestCase extends AbstractAnnotatedResourceWithFinderTestCase { +class AnnotatedResource18TestCase extends AbstractAnnotatedResourceWithFinderTestCase { @Override void configureFinder(Finder finder) { @@ -31,7 +31,7 @@ void configureFinder(Finder finder) { } @Test - public void testQuery() throws IOException, ClassNotFoundException { + void testQuery() throws IOException, ClassNotFoundException { MyBean myBean = new MyBean("test", "description"); Representation rep = clientResource.post( diff --git a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java index 3a9f44198a..015ee774a0 100644 --- a/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/AnnotatedResource20TestCase.java @@ -20,7 +20,7 @@ * * @author Jerome Louvel */ -public class AnnotatedResource20TestCase extends AbstractAnnotatedResourceTestCase { +class AnnotatedResource20TestCase extends AbstractAnnotatedResourceTestCase { private MyResource20 myResource; @@ -37,13 +37,13 @@ void configureClientResource(ClientResource clientResource) { } @Test - public void testGet() { + void testGet() { assertThrows(MyException01.class, () -> myResource.represent()); assertEquals(400, clientResource.getStatus().getCode()); } @Test - public void testGetAndSerializeException() { + void testGetAndSerializeException() { MyException02 e = assertThrows( MyException02.class, () -> myResource.representAndSerializeException()); diff --git a/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java b/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java index fa08185d37..7b6a26caa8 100644 --- a/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java +++ b/org.restlet/src/test/java/org/restlet/resource/DirectoryTestCase.java @@ -35,6 +35,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.restlet.Application; import org.restlet.Component; import org.restlet.Request; @@ -59,7 +61,7 @@ * * @author Thierry Boileau */ -public class DirectoryTestCase { +class DirectoryTestCase { String webSiteURL = "http://myapplication/"; @@ -89,7 +91,7 @@ public class DirectoryTestCase { File testDir; @BeforeAll - public static void setUp() throws Exception { + static void setUp() throws Exception { Engine.getInstance().getRegisteredConverters().clear(); Engine.getInstance().registerDefaultConverters(); Engine.getInstance().getRegisteredClients().add(new FileClientHelper(null)); @@ -112,65 +114,34 @@ public static void setUp() throws Exception { } @AfterAll - public static void tearDown() throws Exception { + static void tearDown() throws Exception { // Now, let's stop the component! clientComponent.stop(); } @AfterEach - public void afterEach() { + void afterEach() { IoUtils.delete(this.testDir, true); } - @Test - public void testDirectoryWithExtensionTunnelAndIndexName() throws IOException { - application.getTunnelService().setExtensionsTunnel(true); - this.testDir = - Files.createTempDirectory("testDirectoryWithExtensionTunnelAndIndexName").toFile(); - application.setTestDirectory(testDir); - - // Test the directory Restlet with an index name - testDirectory(application, application.getDirectory(), "index"); - } - - @Test - public void testDirectoryWithExtensionTunnelAndWithoutIndexName() throws IOException { - application.getTunnelService().setExtensionsTunnel(true); - this.testDir = - Files.createTempDirectory("testDirectoryWithExtensionTunnelAndWithoutIndexName") - .toFile(); - application.setTestDirectory(testDir); - - // Test the directory Restlet with no index name - testDirectory(application, application.getDirectory(), ""); - } - - @Test - public void testDirectoryWithoutExtensionTunnelAndIndexName() throws IOException { - application.getTunnelService().setExtensionsTunnel(false); - this.testDir = - Files.createTempDirectory("testDirectoryWithoutExtensionTunnelAndIndexName") - .toFile(); - application.setTestDirectory(testDir); - - // Test the directory Restlet with an index name - testDirectory(application, application.getDirectory(), "index"); - } - - @Test - public void testDirectoryWithoutExtensionTunnelAndWithoutIndexName() throws IOException { - application.getTunnelService().setExtensionsTunnel(false); - this.testDir = - Files.createTempDirectory("testDirectoryWithoutExtensionTunnelAndWithoutIndexName") - .toFile(); + @ParameterizedTest + @CsvSource({ + "true,index,testDirectoryWithExtensionTunnelAndIndexName", + "true,'',testDirectoryWithExtensionTunnelAndWithoutIndexName", + "false,index,testDirectoryWithoutExtensionTunnelAndIndexName", + "false,'',testDirectoryWithoutExtensionTunnelAndWithoutIndexName" + }) + void testDirectoryWithOrWithoutExtensionTunnel( + boolean extensionsTunnel, String indexName, String testDirectoryName) + throws IOException { + application.getTunnelService().setExtensionsTunnel(extensionsTunnel); + this.testDir = Files.createTempDirectory(testDirectoryName).toFile(); application.setTestDirectory(testDir); - - // Test the directory Restlet with no index name - testDirectory(application, application.getDirectory(), ""); + testDirectory(application, application.getDirectory(), indexName); } @Test - public void testDirectoryDeeplyAccessible() throws IOException { + void testDirectoryDeeplyAccessible() throws IOException { this.testDir = Files.createTempDirectory("testDirectoryDeeplyAccessible").toFile(); application.setTestDirectory(testDir); @@ -207,7 +178,7 @@ public void testDirectoryDeeplyAccessible() throws IOException { } @Test - public void testParentDirectoryInaccessible() throws IOException { + void testParentDirectoryInaccessible() throws IOException { application.getTunnelService().setExtensionsTunnel(false); this.testDir = Files.createTempDirectory("testParentDirectoryInaccessible").toFile(); @@ -295,7 +266,7 @@ public void testParentDirectoryInaccessible() throws IOException { /** Test content negotiation based on client preferences. */ @Test - public void testUserPreferences() throws IOException { + void testUserPreferences() throws IOException { application.getTunnelService().setExtensionsTunnel(true); // Allow extensions tunneling application .getMetadataService() @@ -445,13 +416,6 @@ protected Response handle(Method method) { } } - /** - * Helper - * - * @param directory - * @param indexName - * @throws IOException - */ private void testDirectory(MyApplication application, Directory directory, String indexName) throws IOException { // Create a temporary file for the tests (the tests directory is not empty) @@ -464,31 +428,31 @@ private void testDirectory(MyApplication application, Directory directory, Strin final String testDirectoryUrl = this.webSiteURL.concat(testDirectory.getName()); directory.setIndexName(indexName); - // Test 1a : directory does not allow to GET its content + // Test 1a: directory does not allow to GET its content directory.setListingAllowed(false); Response response = new TestRequest(this.webSiteURL).baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - // Test 1b : directory allows to GET its content + // Test 1b: directory allows to GET its content directory.setListingAllowed(true); response = new TestRequest(this.webSiteURL).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); // should list all files in the directory (at least the temporary file generated before) assertTrue(response.getEntityAsText().contains(testFile.getName())); - // Test 2a : tests the HEAD method + // Test 2a: tests the HEAD method response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - // Test 2b : try to GET a file that does not exist + // Test 2b: try to GET a file that does not exist response = new TestRequest(this.webSiteURL, "123456.txt").baseRef(this.webSiteURL).handle(GET); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - // Test 3a : try to put a new representation, but the directory is read only + // Test 3a: try to put a new representation, but the directory is read only directory.setModifiable(false); response = new TestRequest(this.baseFileUrl) @@ -497,8 +461,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin .handle(PUT); assertEquals(CLIENT_ERROR_METHOD_NOT_ALLOWED, response.getStatus()); - // Test 3b : try to put a new representation, the directory is no more - // read only + // Test 3b: try to put a new representation, the directory is no more read-only directory.setModifiable(true); response = new TestRequest(this.baseFileUrlFr) @@ -516,12 +479,12 @@ private void testDirectory(MyApplication application, Directory directory, Strin assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 3b", response.getEntityAsText()); - // Test 4 : Try to get the representation of the new file + // Test 4: Try to get the representation of the new file response = new TestRequest(this.baseFileUrl).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 3b", response.getEntityAsText()); - // Test 5 : add a new representation of the same base file + // Test 5: add a new representation of the same base file response = new TestRequest(this.baseFileUrlEn) .baseRef(this.webSiteURL) @@ -533,18 +496,18 @@ private void testDirectory(MyApplication application, Directory directory, Strin response = new TestRequest(this.baseFileUrlEn).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); - // Test 6a : delete a file + // Test 6a: delete a file response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(HEAD); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - // Test 6b : delete a file that does not exist + // Test 6b: delete a file that does not exist response = new TestRequest(testFileUrl).baseRef(this.webSiteURL).handle(DELETE); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - // Test 6c : delete a directory (without and with trailing slash) + // Test 6c: delete a directory (without and with trailing slash) // Distinct behaviors if an index has been defined or not if (indexName.isEmpty()) { response = new TestRequest(testDirectoryUrl).baseRef(this.webSiteURL).handle(DELETE); @@ -576,8 +539,8 @@ private void testDirectory(MyApplication application, Directory directory, Strin .handle(PUT); assertTrue(response.getStatus().isSuccess()); - // Test 7b : put another representation of the base file (in French - // language) but the extensions are mixed + // Test 7b: put another representation of the base file (in French + // language), but the extensions are mixed // and there is no content negotiation directory.setNegotiatingContent(false); response = @@ -592,7 +555,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(HEAD); assertEquals(Status.SUCCESS_OK, response.getStatus()); - // Test 7c : delete the file representation of the resources with no content negotiation + // Test 7c: delete the file representation of the resources with no content negotiation // The 2 French resources are deleted (there were only one) response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); @@ -606,8 +569,8 @@ private void testDirectory(MyApplication application, Directory directory, Strin response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(DELETE); assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); - // Test 7d : put another representation of the base file (in French - // language) but the extensions are mixed + // Test 7d: put another representation of the base file (in French + // language), but the extensions are mixed // and there is content negotiation directory.setNegotiatingContent(true); response = @@ -628,13 +591,13 @@ private void testDirectory(MyApplication application, Directory directory, Strin response = new TestRequest(this.baseFileUrlFrBis).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); - // TBOI : not sure this test is correct + // TBOI: not sure this test is correct // Check if only one resource has been created directory.setNegotiatingContent(false); response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(HEAD); assertEquals(SUCCESS_OK, response.getStatus()); - // Test 7e : delete the file representation of the resources with content negotiation + // Test 7e: delete the file representation of the resources with content negotiation directory.setNegotiatingContent(true); response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); @@ -653,8 +616,8 @@ private void testDirectory(MyApplication application, Directory directory, Strin assertEquals(CLIENT_ERROR_NOT_FOUND, response.getStatus()); } - // Test 8 : must delete the English representation - response = new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(DELETE); + // Test 8: must delete the English representation + new TestRequest(this.baseFileUrlFr).baseRef(this.webSiteURL).handle(DELETE); response = new TestRequest(this.baseFileUrlEn).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); @@ -670,24 +633,24 @@ private void testDirectory(MyApplication application, Directory directory, Strin .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - // Test 9b : Try to get the representation of the new file + // Test 9b: Try to get the representation of the new file response = new TestRequest(this.percentEncodedFileUrl).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 9a", response.getEntityAsText()); - // Test 9c : Try to get the representation of the new file with an + // Test 9c: Try to get the representation of the new file with an // equivalent URI response = new TestRequest(this.percentEncodedFileUrlBis).baseRef(this.webSiteURL).handle(GET); assertEquals(SUCCESS_OK, response.getStatus()); assertEquals("this is test 9a", response.getEntityAsText()); - // Test 9d : Try to delete the file + // Test 9d: Try to delete the file response = new TestRequest(this.percentEncodedFileUrl).baseRef(this.webSiteURL).handle(DELETE); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - // Test 10a : Try to create a directory with an unknown hierarchy of + // Test 10a: Try to create a directory with an unknown hierarchy of // parent directories. response = new TestRequest(this.testCreationDirectory) @@ -696,7 +659,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - // Test 10b : Try to create a directory (with the trailing "/") with an + // Test 10b: Try to create a directory (with the trailing "/") with an // unknown hierarchy of parent directories. response = new TestRequest(this.testCreationDirectory, "/") @@ -705,7 +668,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin .handle(PUT); assertEquals(SUCCESS_NO_CONTENT, response.getStatus()); - // Test 10c : Try to create a file with an unknown hierarchy of + // Test 10c: Try to create a file with an unknown hierarchy of // parent directories. The name and the metadata of the provided entity // don't match response = @@ -715,7 +678,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin .handle(PUT); assertEquals(CLIENT_ERROR_BAD_REQUEST, response.getStatus()); - // Test 10d : Try to create a file with an unknown hierarchy of + // Test 10d: Try to create a file with an unknown hierarchy of // parent directories. The name and the metadata of the provided entity // match response = @@ -725,7 +688,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin .handle(PUT); assertEquals(SUCCESS_CREATED, response.getStatus()); - // Test 11 : redirection for Directory + // Test 11: redirection for Directory response = new TestRequest(testDirectoryUrl) .baseRef(this.webSiteURL) @@ -734,7 +697,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin assertEquals(REDIRECTION_SEE_OTHER, response.getStatus()); assertEquals("http://myapplication/try/", response.getLocationRef().toString()); - // Test 12 : redirection for Directory with proxy forwarding + // Test 12: redirection for Directory with proxy forwarding response = new TestRequest(testDirectoryUrl) .baseRef(this.webSiteURL) @@ -744,7 +707,7 @@ private void testDirectory(MyApplication application, Directory directory, Strin assertEquals(REDIRECTION_SEE_OTHER, response.getStatus()); assertEquals("https://myapplication:123/try/", response.getLocationRef().toString()); - // Test 13 : creation of resource with proxy forwarding + // Test 13: creation of resource with proxy forwarding response = new TestRequest(this.percentEncodedFileUrl) .baseRef(this.webSiteURL) diff --git a/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java b/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java index ff2b5177e5..531063c764 100644 --- a/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/SecurityTestCase.java @@ -25,7 +25,7 @@ * * @author Jerome Louvel */ -public class SecurityTestCase { +class SecurityTestCase { private final ChallengeResponse lambdaUserCR = new ChallengeResponse(HTTP_BASIC, "stiger", "pwd"); @@ -36,7 +36,7 @@ public class SecurityTestCase { private int testPort; @BeforeEach - public void startComponent() throws Exception { + void startComponent() throws Exception { Engine.register(); Engine.clearThreadLocalVariables(); this.component = new SaasComponent(); @@ -45,7 +45,7 @@ public void startComponent() throws Exception { } @AfterEach - public void stopServer() throws Exception { + void stopServer() throws Exception { Engine.clearThreadLocalVariables(); if (this.component.isStarted()) { this.component.stop(); @@ -55,7 +55,7 @@ public void stopServer() throws Exception { } @Test - public void withoutAuthenticationHttpBasicAuthenticatorShouldReturnUnauthorizedResponse() { + void withoutAuthenticationHttpBasicAuthenticatorShouldReturnUnauthorizedResponse() { ClientResource resource = new ClientResource("http://localhost:" + testPort + "/httpBasicAuthenticator"); runClientResource(resource); @@ -63,7 +63,7 @@ public void withoutAuthenticationHttpBasicAuthenticatorShouldReturnUnauthorizedR } @Test - public void withAuthenticationHttpBasicAuthenticatorShouldReturnOkResponse() { + void withAuthenticationHttpBasicAuthenticatorShouldReturnOkResponse() { ClientResource resource = new ClientResource("http://localhost:" + testPort + "/httpBasicAuthenticator"); resource.setChallengeResponse(lambdaUserCR); @@ -72,7 +72,7 @@ public void withAuthenticationHttpBasicAuthenticatorShouldReturnOkResponse() { } @Test - public void withoutAuthenticationAlwaysAuthenticatorShouldReturnOkResponse() { + void withoutAuthenticationAlwaysAuthenticatorShouldReturnOkResponse() { ClientResource resource = new ClientResource("http://localhost:" + testPort + "/alwaysAuthenticator"); runClientResource(resource); @@ -80,7 +80,7 @@ public void withoutAuthenticationAlwaysAuthenticatorShouldReturnOkResponse() { } @Test - public void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() { + void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() { ClientResource resource = new ClientResource("http://localhost:" + testPort + "/neverAuthenticator"); runClientResource(resource); @@ -88,7 +88,7 @@ public void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() } @Test - public void + void withLambdaUserAuthenticationAdminRoleAuthorizerAuthenticatorShouldReturnForbiddenResponse() { ClientResource resource = new ClientResource("http://localhost:" + testPort + "/adminRoleAuthorizer"); @@ -98,8 +98,7 @@ public void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() } @Test - public void - withAdminUserAuthenticationAdminRoleAuthorizerAuthenticatorShouldReturnOkResponse() { + void withAdminUserAuthenticationAdminRoleAuthorizerAuthenticatorShouldReturnOkResponse() { ClientResource resource = new ClientResource("http://localhost:" + testPort + "/adminRoleAuthorizer"); resource.setChallengeResponse(adminUserCR); @@ -108,7 +107,7 @@ public void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() } @Test - public void + void withAdminUserAuthenticationAdminRoleForbiddenAuthorizerAuthenticatorShouldReturnForbiddenResponse() { ClientResource resource = new ClientResource( @@ -119,7 +118,7 @@ public void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() } @Test - public void + void withLambdaUserAuthenticationAdminRoleForbiddenAuthorizerAuthenticatorShouldReturnOkResponse() { ClientResource resource = new ClientResource( @@ -132,7 +131,8 @@ public void withAuthenticationNeverAuthenticatorShouldReturnForbiddenResponse() private static void runClientResource(ClientResource resource) { try { resource.get(); - } catch (ResourceException e) { + } catch (ResourceException ignored) { + // Ignored exceptions are expected } resource.release(); } From ef671f71e60dc9cf43a11ffbebdcd60a21ddc399 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 19:26:26 +0200 Subject: [PATCH 21/30] Take into account Sonar comments --- .../main/java/org/restlet/data/Reference.java | 93 ++++++------- .../engine/local/DirectoryServerResource.java | 120 ++++++++-------- .../engine/resource/MethodAnnotationInfo.java | 10 +- .../engine/security/HttpBasicHelper.java | 128 +++++++++--------- .../engine/util/DefaultSaxHandler.java | 80 ++++------- .../org/restlet/resource/ClientResource.java | 53 ++++---- .../java/org/restlet/routing/Redirector.java | 26 ++-- .../java/org/restlet/routing/Template.java | 119 ++++++++-------- .../org/restlet/service/ConverterService.java | 25 ++-- 9 files changed, 305 insertions(+), 349 deletions(-) diff --git a/org.restlet/src/main/java/org/restlet/data/Reference.java b/org.restlet/src/main/java/org/restlet/data/Reference.java index 6355870f37..6b96c4ee46 100644 --- a/org.restlet/src/main/java/org/restlet/data/Reference.java +++ b/org.restlet/src/main/java/org/restlet/data/Reference.java @@ -676,56 +676,50 @@ public Reference copy() { * @return The original reference, eventually with invalid URI characters encoded. */ private String encodeInvalidCharacters(String uriRef) throws IllegalArgumentException { - String result = uriRef; + if (uriRef == null) { + return null; + } - if (uriRef != null) { - boolean valid = true; + boolean valid = true; - // Ensure that all characters are valid, otherwise encode them - for (int i = 0; valid && (i < uriRef.length()); i++) { - if (!isValid(uriRef.charAt(i))) { - valid = false; - Context.getCurrentLogger() - .fine( - "Invalid character detected in URI reference at index '" - + i - + "': \"" - + uriRef.charAt(i) - + "\". It will be automatically encoded."); - } else if ((uriRef.charAt(i) == '%') && (i > uriRef.length() - 2)) { - // A percent encoding character has been detected but - // without the necessary two hexadecimal digits following - valid = false; - Context.getCurrentLogger() - .fine( - "Invalid percent encoding detected in URI reference at index '" - + i - + "': \"" - + uriRef.charAt(i) - + "\". It will be automatically encoded."); - } + // Ensure that all characters are valid, otherwise encode them + for (int i = 0; valid && (i < uriRef.length()); i++) { + final char character = uriRef.charAt(i); + + if (!isValid(character) + || ((character == '%') + && (i + > uriRef.length() + - 2))) { // missing the 2 necessary trailing characters + valid = false; + Context.getCurrentLogger() + .log( + Level.FINE, + "Invalid character \"{0}\" detected in URI at index {1} will be encoded.", + new Object[] {character, i}); } + } - if (!valid) { - StringBuilder sb = new StringBuilder(); + if (valid) { + return uriRef; + } - for (int i = 0; (i < uriRef.length()); i++) { - if (isValid(uriRef.charAt(i))) { - if ((uriRef.charAt(i) == '%') && (i > uriRef.length() - 2)) { - sb.append("%25"); - } else { - sb.append(uriRef.charAt(i)); - } - } else { - sb.append(encode(String.valueOf(uriRef.charAt(i)))); - } - } + StringBuilder sb = new StringBuilder(); - result = sb.toString(); + for (int i = 0; i < uriRef.length(); i++) { + final char character = uriRef.charAt(i); + if (isValid(character)) { + if ((character == '%') && (i > uriRef.length() - 2)) { + sb.append("%25"); + } else { + sb.append(character); + } + } else { + sb.append(encode(String.valueOf(character))); } } - return result; + return sb.toString(); } /** {@inheritDoc} */ @@ -1017,11 +1011,8 @@ public int getHostPort() { Context.getCurrentLogger() .log( Level.WARNING, - "Can't parse hostPort : [hostRef,requestUri]=[" - + getBaseRef() - + "," - + this.internalRef - + "]"); + "Can''t parse hostPort : [hostRef,requestUri]=[{0},{1}]", + new Object[] {getBaseRef(), this.internalRef}); } } } @@ -1371,9 +1362,9 @@ public Reference getRelativeRef() { * invoked for absolute references, otherwise an IllegalArgumentException will be raised. * * @param base The base reference to use. + * @return The current reference relatively to a base reference. * @throws IllegalArgumentException If the relative reference is computed, although the * reference or the base reference are not absolute or not hierarchical. - * @return The current reference relatively to a base reference. */ public Reference getRelativeRef(Reference base) { Reference result = null; @@ -1733,17 +1724,17 @@ public List getSegments(boolean decode) { * Returns the target reference. This method resolves relative references against the base * reference, then normalizes them. * + * @return The target reference. * @throws IllegalArgumentException If the base reference (after resolution) is not absolute. * @throws IllegalArgumentException If the reference is relative and not base reference has been * provided. - * @return The target reference. */ public Reference getTargetRef() { Reference result = null; // Step 1 - Resolve relative reference against their base reference if (isRelative() && (this.baseRef != null)) { - Reference baseReference = null; + final Reference baseReference; if (this.baseRef.isAbsolute()) { baseReference = this.baseRef; @@ -1786,7 +1777,7 @@ public Reference getTargetRef() { result.setPath(path); } else { final String basePath = baseReference.getPath(); - String mergedPath = null; + final String mergedPath; if ((baseReference.getAuthority() != null) && ((basePath == null) || (basePath.isEmpty()))) { @@ -2779,7 +2770,7 @@ public java.net.URL toUrl() { java.net.URL result = null; try { - result = new java.net.URL(getTargetRef().toString()); + result = toUri().toURL(); } catch (java.net.MalformedURLException e) { throw new IllegalArgumentException("Malformed URL exception", e); } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java index f632273a3a..fcc08200f6 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/DirectoryServerResource.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.SortedSet; @@ -36,10 +37,10 @@ * webapp context). A content negotiation mechanism (similar to Apache HTTP Server) is available. It * is based on path extensions to detect variants (languages, media types, or character sets). * - * @see Apache - * mod_negotiation module * @author Jerome Louvel * @author Thierry Boileau + * @see Apache + * mod_negotiation module */ public class DirectoryServerResource extends ServerResource { @@ -100,38 +101,35 @@ public class DirectoryServerResource extends ServerResource { public Representation delete() throws ResourceException { if (!this.directory.isModifiable()) { setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED, "The directory is not modifiable."); - return null; - } - - Request contextRequest = new Request(Method.DELETE, this.targetUri); - Response contextResponse = new Response(contextRequest); - - if (this.directoryTarget && !this.indexTarget) { - // let the client handle the directory's deletion - contextRequest.setResourceRef(this.targetUri); - dispatchRequest(contextRequest, contextResponse); - - setStatus(contextResponse.getStatus()); - return null; - } + } else { + Request contextRequest = new Request(Method.DELETE, this.targetUri); + Response contextResponse = new Response(contextRequest); - ReferenceList references = getVariantsReferences(); + if (this.directoryTarget && !this.indexTarget) { + // let the client handle the directory's deletion + contextRequest.setResourceRef(this.targetUri); + dispatchRequest(contextRequest, contextResponse); - if (references.isEmpty()) { - // no representation found - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - } else if (this.uniqueReference != null) { - // only one representation - contextRequest.setResourceRef(this.uniqueReference); - dispatchRequest(contextRequest, contextResponse); - setStatus(contextResponse.getStatus()); - } else { - // several variants found, but not the right one - setStatus( - Status.CLIENT_ERROR_NOT_ACCEPTABLE, - "Unable to process properly the request. Several variants exist but none of them suits precisely. "); + setStatus(contextResponse.getStatus()); + } else { + ReferenceList references = getVariantsReferences(); + + if (references.isEmpty()) { + // no representation found + setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } else if (this.uniqueReference != null) { + // only one representation + contextRequest.setResourceRef(this.uniqueReference); + dispatchRequest(contextRequest, contextResponse); + setStatus(contextResponse.getStatus()); + } else { + // several variants found, but not the right one + setStatus( + Status.CLIENT_ERROR_NOT_ACCEPTABLE, + "Unable to process properly the request. Several variants exist but none of them suits precisely. "); + } + } } - return null; } @@ -166,9 +164,10 @@ public void doInit() throws ResourceException { : null; if (getClientDispatcher() == null) { getLogger() - .warning( - "No client dispatcher is available. Can't get the target URI: " - + this.targetUri); + .log( + Level.WARNING, + "No client dispatcher is available. Can''t get the target URI: {0}", + this.targetUri); throw new ResourceException( Status.SERVER_ERROR_INTERNAL, "No client dispatcher is available."); @@ -178,15 +177,14 @@ public void doInit() throws ResourceException { setNegotiated(this.directory.isNegotiatingContent()); this.relativePart = getReference().getRemainingPart(false, false); this.originalRef = getOriginalRef(); - if (this.originalRef != null) { + if (this.originalRef != null + && (getApplication() != null) + && getApplication().getTunnelService().isExtensionsTunnel()) { // Restore the original URI in case the call has been tunneled. - if ((getApplication() != null) - && getApplication().getTunnelService().isExtensionsTunnel()) { - Reference originalBaseRef = new Reference(this.originalRef); - originalBaseRef.setPath(getReference().getBaseRef().getPath()); - this.originalRef.setBaseRef(originalBaseRef); - this.relativePart = this.originalRef.getRemainingPart(false, false); - } + Reference originalBaseRef = new Reference(this.originalRef); + originalBaseRef.setPath(getReference().getBaseRef().getPath()); + this.originalRef.setBaseRef(originalBaseRef); + this.relativePart = this.originalRef.getRemainingPart(false, false); } if (this.relativePart.startsWith("/")) { @@ -356,8 +354,8 @@ && getApplication().getTunnelService().isExtensionsTunnel()) { } // Log results - getLogger().fine("Converted target URI: " + this.targetUri); - getLogger().fine("Converted base name : " + this.baseName); + getLogger().log(Level.FINE, "Converted target URI: {0}", this.targetUri); + getLogger().log(Level.FINE, "Converted base name: {0}", this.baseName); } private ReferenceList tryToConvertAsReferenceList(Representation entity) @@ -559,8 +557,7 @@ protected List getVariants(Method method) { // filter the directory listing // Allow sorting the list of representations - SortedSet resultSet = - new TreeSet(getRepresentationsComparator()); + SortedSet resultSet = new TreeSet<>(getRepresentationsComparator()); // Compute the base reference (from a call's client point of view) String baseReference = getVariantsBaseReference(); @@ -661,7 +658,8 @@ private ReferenceList getVariantsReferences() { if (!MediaType.TEXT_URI_LIST.equals(contextResponse.getEntity().getMediaType())) { // The unique reference has been found. this.uniqueReference = contextResponse.getEntity().getLocationRef(); - return new ReferenceList(Arrays.asList(contextResponse.getEntity().getLocationRef())); + return new ReferenceList( + Collections.singletonList(contextResponse.getEntity().getLocationRef())); } ReferenceList listVariants; @@ -687,25 +685,19 @@ private ReferenceList getVariantsReferences() { ? fullEntryName.substring(0, firstDotIndex) : fullEntryName; - if (!baseEntryName.equals(this.baseName)) { - // Not a valid variant - continue; - } - - // Test if the variant is included in the base prototype variant - Variant variant = new Variant(); - Entity.updateMetadata(fullEntryName, variant, true, getMetadataService()); + if (baseEntryName.equals(this.baseName)) { // Valid variant + // Test if the variant is included in the base prototype variant + Variant variant = new Variant(); + Entity.updateMetadata(fullEntryName, variant, true, getMetadataService()); - if (!this.protoVariant.includes(variant)) { - // Not a valid variant - continue; - } + if (this.protoVariant.includes(variant)) { // Valid variant + variantsReferences.add(variantReference); - variantsReferences.add(variantReference); - - if (variant.equals(this.baseVariant)) { - // The unique reference has been found. - this.uniqueReference = variantReference; + if (variant.equals(this.baseVariant)) { + // The unique reference has been found. + this.uniqueReference = variantReference; + } + } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java index 7321e4959c..731f676a9e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java @@ -174,12 +174,11 @@ public String getQuery() { * * @param metadataService The metadata service to use. * @return A list of request variants. - * @throws IOException */ @SuppressWarnings("unchecked") public List getRequestVariants( - MetadataService metadataService, org.restlet.service.ConverterService converterService) - throws IOException { + MetadataService metadataService, + org.restlet.service.ConverterService converterService) { List result = null; Class[] classes = getJavaInputTypes(); @@ -204,12 +203,11 @@ public List getRequestVariants( * @param metadataService The metadata service to use. * @param converterService The converter service to use. * @return A list of response variants. - * @throws IOException */ @SuppressWarnings("unchecked") public List getResponseVariants( - MetadataService metadataService, org.restlet.service.ConverterService converterService) - throws IOException { + MetadataService metadataService, + org.restlet.service.ConverterService converterService) { List result = null; if ((getJavaOutputType() != null) diff --git a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java index a13c39c7a3..1081514606 100644 --- a/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/security/HttpBasicHelper.java @@ -33,6 +33,11 @@ */ public class HttpBasicHelper extends AuthenticatorHelper { + private static final String CHARSET = "charset"; + private static final String REALM = "realm"; + private static final String UTF_8 = "UTF-8"; + private static final String ISO_8859_1 = "ISO-8859-1"; + /** Constructor. */ public HttpBasicHelper() { super(ChallengeScheme.HTTP_BASIC, true, true); @@ -45,10 +50,10 @@ public void formatRequest( Response response, Series
httpHeaders) { String realm = challenge.getRealm(); - String charset = challenge.getParameters().getFirstValue("charset"); + String charset = challenge.getParameters().getFirstValue(CHARSET); if (realm != null) { - cw.appendQuotedChallengeParameter("realm", realm); + cw.appendQuotedChallengeParameter(REALM, realm); } else { getLogger() .warning( @@ -56,8 +61,8 @@ public void formatRequest( } if (charset != null) { - if ("UTF-8".equalsIgnoreCase(charset)) { - cw.appendQuotedChallengeParameter("charset", "UTF-8"); + if (UTF_8.equalsIgnoreCase(charset)) { + cw.appendQuotedChallengeParameter(CHARSET, UTF_8); } else { getLogger().warning("The \"charset\" parameter must be \"UTF-8\" per RFC 7617."); } @@ -74,20 +79,7 @@ public void formatResponse( if (challenge == null) { throw new RuntimeException("No challenge provided, unable to encode credentials"); } else { - String charset = challenge.getParameters().getFirstValue("charset"); - - if (charset != null) { - if ("UTF-8".equalsIgnoreCase(charset)) { - charset = "UTF-8"; - } else { - getLogger() - .warning( - "The \"charset\" parameter must be \"UTF-8\" per RFC 7617. Using \"ISO-8859-1\" instead."); - charset = "ISO-8859-1"; - } - } else { - charset = "ISO-8859-1"; - } + String charset = getCharset(challenge); CharArrayWriter credentials = new CharArrayWriter(); credentials.write(challenge.getIdentifier()); @@ -108,34 +100,37 @@ public void formatResponse( @Override public void parseRequest( ChallengeRequest challenge, Response response, Series
httpHeaders) { - if (challenge.getRawValue() != null) { - HeaderReader hr = new HeaderReader<>(challenge.getRawValue()); + if (challenge.getRawValue() == null) { + return; + } + + HeaderReader hr = new HeaderReader<>(challenge.getRawValue()); + Parameter param = null; + try { + param = hr.readParameter(); + } catch (IOException e) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to parse the challenge request header parameter", + e); + } + + while (param != null) { try { - Parameter param = hr.readParameter(); - - while (param != null) { - try { - if ("realm".equals(param.getName())) { - challenge.setRealm(param.getValue()); - } else { - challenge.getParameters().add(param); - } - - if (hr.skipValueSeparator()) { - param = hr.readParameter(); - } else { - param = null; - } - } catch (Exception e) { - Context.getCurrentLogger() - .log( - Level.WARNING, - "Unable to parse the challenge request header parameter", - e); - } + if (REALM.equals(param.getName())) { + challenge.setRealm(param.getValue()); + } else { + challenge.getParameters().add(param); } - } catch (Exception e) { + + if (hr.skipValueSeparator()) { + param = hr.readParameter(); + } else { + param = null; // end of header + } + } catch (IOException e) { Context.getCurrentLogger() .log( Level.WARNING, @@ -154,20 +149,7 @@ public void parseResponse( } try { - String charset = challenge.getParameters().getFirstValue("charset"); - - if (charset != null) { - if ("UTF-8".equalsIgnoreCase(charset)) { - charset = "UTF-8"; - } else { - getLogger() - .warning( - "The \"charset\" parameter must be \"UTF-8\" per RFC 7617. Using \"ISO-8859-1\" instead."); - charset = "ISO-8859-1"; - } - } else { - charset = "ISO-8859-1"; - } + String charset = getCharset(challenge); byte[] credentialsEncoded = Base64.getDecoder().decode(challenge.getRawValue()); @@ -177,11 +159,10 @@ public void parseResponse( if (separator == -1) { // Log the blocking getLogger() - .info( - "Invalid credentials given by client with IP: " - + ((request != null) - ? request.getClientInfo().getAddress() - : "?")); + .log( + Level.INFO, + "Invalid credentials given by client with IP: {0}", + request != null ? request.getClientInfo().getAddress() : "?"); } else { challenge.setIdentifier(credentials.substring(0, separator)); challenge.setSecret(credentials.substring(separator + 1)); @@ -192,4 +173,27 @@ public void parseResponse( getLogger().log(Level.INFO, "Unable to decode the HTTP Basic credential", e); } } + + /** + * Returns the charset from the given ChallengeResponse. If the charset is not specified, or if + * it is not UTF-8, then ISO-8859-1 is returned as per RFC 7617. + * + * @param challenge The challenge response. + * @return The charset. + */ + private String getCharset(final ChallengeResponse challenge) { + String charset = challenge.getParameters().getFirstValue(CHARSET); + + if (charset == null) { + charset = ISO_8859_1; + } else if (UTF_8.equalsIgnoreCase(charset)) { + charset = UTF_8; + } else { + getLogger() + .warning( + "The \"charset\" parameter must be \"UTF-8\" per RFC 7617. Using \"ISO-8859-1\" instead."); + charset = ISO_8859_1; + } + return charset; + } } diff --git a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java index 61214d3ef5..aba10cb42d 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/DefaultSaxHandler.java @@ -60,44 +60,12 @@ public DefaultSaxHandler() { @Override public void error(SAXParseException x) throws SAXException { - if (loggable) { - final String msg = - "[ERROR] - Unexpected exception while parsing " - + "an instance of PUBLIC [" - + x.getPublicId() - + "], SYSTEM [" - + x.getSystemId() - + "] - line #" - + x.getLineNumber() - + ", column #" - + x.getColumnNumber(); - if (debug) { - Context.getCurrentLogger().log(Level.CONFIG, msg, x); - } else { - logger.config(msg + ": " + x.getLocalizedMessage()); - } - } + log("ERROR", x); } @Override public void fatalError(SAXParseException x) { - if (loggable) { - final String msg = - "[FATAL] - Unexpected exception while parsing " - + "an instance of PUBLIC [" - + x.getPublicId() - + "], SYSTEM [" - + x.getSystemId() - + "] - line #" - + x.getLineNumber() - + ", column #" - + x.getColumnNumber(); - if (debug) { - Context.getCurrentLogger().log(Level.CONFIG, msg, x); - } else { - logger.config(msg + ": " + x.getLocalizedMessage()); - } - } + log("FATAL", x); } @Override @@ -105,7 +73,12 @@ public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { if (loggable) { logger.config( - "Resolve entity with PUBLIC [" + publicId + "], and SYSTEM [" + systemId + "]"); + () -> + "Resolve entity with PUBLIC [" + + publicId + + "], and SYSTEM [" + + systemId + + "]"); } return super.resolveEntity(publicId, systemId); } @@ -126,17 +99,18 @@ public org.w3c.dom.ls.LSInput resolveResource( String type, String namespaceUri, String publicId, String systemId, String baseUri) { if (loggable) { logger.config( - "Resolve resource with type [" - + type - + "], namespace URI [" - + namespaceUri - + "], PUBLIC [" - + publicId - + "], SYSTEM [" - + systemId - + "], and base URI [" - + baseUri - + "]"); + () -> + "Resolve resource with type [" + + type + + "], namespace URI [" + + namespaceUri + + "], PUBLIC [" + + publicId + + "], SYSTEM [" + + systemId + + "], and base URI [" + + baseUri + + "]"); } return null; } @@ -145,15 +119,21 @@ public org.w3c.dom.ls.LSInput resolveResource( public void skippedEntity(String name) throws SAXException { super.skippedEntity(name); if (loggable) { - logger.config("Skipped entity named [" + name + "]"); + logger.config(() -> "Skipped entity named [" + name + "]"); } } @Override public void warning(SAXParseException x) throws SAXException { + log("WARN", x); + } + + private void log(String level, SAXParseException x) { if (loggable) { final String msg = - "[WARN] - Unexpected exception while parsing " + "[" + + level + + "] - Unexpected exception while parsing " + "an instance of PUBLIC [" + x.getPublicId() + "], SYSTEM [" @@ -163,9 +143,9 @@ public void warning(SAXParseException x) throws SAXException { + ", column #" + x.getColumnNumber(); if (debug) { - Context.getCurrentLogger().log(Level.CONFIG, x, () -> msg); + Context.getCurrentLogger().log(Level.CONFIG, msg, x); } else { - logger.config(msg + ": " + x.getLocalizedMessage()); + logger.config(() -> msg + ": " + x.getLocalizedMessage()); } } } diff --git a/org.restlet/src/main/java/org/restlet/resource/ClientResource.java b/org.restlet/src/main/java/org/restlet/resource/ClientResource.java index c0d7e3cca6..1d6f84c848 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ClientResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ClientResource.java @@ -1,5 +1,5 @@ /** - * Copyright 2005-2026 Qlik + * Copyright 2005-2024 Qlik *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 @@ -546,9 +546,9 @@ public void doError(Status errorStatus, Request request, Response response) { @Override protected void doRelease() throws ResourceException { if ((getNext() != null) && this.nextCreated) { - if (getNext() instanceof Restlet) { + if (getNext() instanceof Restlet restlet) { try { - ((Restlet) getNext()).stop(); + restlet.stop(); } catch (Exception e) { throw new ResourceException(e); } @@ -1009,7 +1009,10 @@ protected void handle( redirect(request, response, references, retryAttempt, next); } else { getLogger() - .fine("Unable to redirect the client call after a response" + response); + .fine( + () -> + "Unable to redirect the client call after a response" + + response); } } @@ -1053,12 +1056,12 @@ public Representation handleInbound(Response response) { * @see #getNext() */ public Response handleOutbound(Request request) { - Response response = createResponse(request); - Uniform next = getNext(); + final Response response = createResponse(request); + final Uniform nextUniform = getNext(); - if (next != null) { + if (nextUniform != null) { // Effectively handle the call - handle(request, response, null, 0, next); + handle(request, response, null, 0, nextUniform); // Update the last received response. setResponse(response); @@ -1202,8 +1205,8 @@ public Representation options(MediaType mediaType) throws ResourceException { } /** - * Patches a resource with the given object as delta state. Automatically serializes the object - * using the {@link org.restlet.service.ConverterService}. + * Patches a resource with the given object as a delta state. Automatically serializes the + * object using the {@link org.restlet.service.ConverterService}. * * @param entity The object entity containing the patch. * @return The optional result entity. @@ -1219,8 +1222,8 @@ public Representation patch(Object entity) throws ResourceException { } /** - * Patches a resource with the given object as delta state. Automatically serializes the object - * using the {@link org.restlet.service.ConverterService}. + * Patches a resource with the given object as a delta state. Automatically serializes the + * object using the {@link org.restlet.service.ConverterService}. * * @param The expected type for the response entity. * @param entity The object entity containing the patch. @@ -1418,14 +1421,15 @@ protected void redirect( Reference newTargetRef = response.getLocationRef(); if ((references != null) && references.contains(newTargetRef)) { - getLogger().warning("Infinite redirection loop detected with URI: " + newTargetRef); + getLogger() + .warning(() -> "Infinite redirection loop detected with URI: " + newTargetRef); } else if (request.getEntity() != null && !request.isEntityAvailable()) { getLogger() .warning( "Unable to follow the redirection because the request entity isn't available anymore."); } else { if (references == null) { - references = new ArrayList(); + references = new ArrayList<>(); } if (references.size() >= getMaxRedirects()) { @@ -1459,13 +1463,13 @@ protected void retry( int retryAttempt, Uniform next) { getLogger() - .log( - Level.INFO, - "A recoverable error was detected (" - + response.getStatus().getCode() - + "), attempting again in " - + getRetryDelay() - + " ms."); + .info( + () -> + "A recoverable error was detected (" + + response.getStatus().getCode() + + "), attempting again in " + + getRetryDelay() + + " ms."); // Wait before attempting again if (getRetryDelay() > 0) { @@ -1624,11 +1628,8 @@ public void setMethod(Method method) { * @param next The next handler. */ public void setNext(org.restlet.Uniform next) { - if (next instanceof final Restlet nextRestlet) { - - if (nextRestlet.getContext() == null) { - nextRestlet.setContext(getContext()); - } + if (next instanceof final Restlet nextRestlet && nextRestlet.getContext() == null) { + nextRestlet.setContext(getContext()); } this.next = next; diff --git a/org.restlet/src/main/java/org/restlet/routing/Redirector.java b/org.restlet/src/main/java/org/restlet/routing/Redirector.java index ca8285e83d..b9d9319f22 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Redirector.java +++ b/org.restlet/src/main/java/org/restlet/routing/Redirector.java @@ -8,7 +8,6 @@ */ package org.restlet.routing; -import java.util.logging.Level; import org.restlet.Application; import org.restlet.Component; import org.restlet.Context; @@ -243,7 +242,7 @@ public void handle(Request request, Response response) { switch (this.mode) { case MODE_CLIENT_PERMANENT: if (request.isLoggable()) { - getLogger().log(Level.FINE, "Permanently redirecting client to: " + targetRef); + getLogger().fine(() -> "Permanently redirecting client to: " + targetRef); } response.redirectPermanent(targetRef); @@ -251,8 +250,7 @@ public void handle(Request request, Response response) { case MODE_CLIENT_FOUND: if (request.isLoggable()) { - getLogger() - .log(Level.FINE, "Redirecting client to found location: " + targetRef); + getLogger().fine(() -> "Redirecting client to found location: " + targetRef); } response.setLocationRef(targetRef); @@ -261,10 +259,7 @@ public void handle(Request request, Response response) { case MODE_CLIENT_SEE_OTHER: if (request.isLoggable()) { - getLogger() - .log( - Level.FINE, - "Redirecting client to another location: " + targetRef); + getLogger().fine(() -> "Redirecting client to another location: " + targetRef); } response.redirectSeeOther(targetRef); @@ -272,7 +267,7 @@ public void handle(Request request, Response response) { case MODE_CLIENT_TEMPORARY: if (request.isLoggable()) { - getLogger().log(Level.FINE, "Temporarily redirecting client to: " + targetRef); + getLogger().fine(() -> "Temporarily redirecting client to: " + targetRef); } response.redirectTemporary(targetRef); @@ -280,8 +275,7 @@ public void handle(Request request, Response response) { case MODE_SERVER_OUTBOUND: if (request.isLoggable()) { - getLogger() - .log(Level.FINE, "Redirecting via client dispatcher to: " + targetRef); + getLogger().fine(() -> "Redirecting via client dispatcher to: " + targetRef); } outboundServerRedirect(targetRef, request, response); @@ -289,12 +283,12 @@ public void handle(Request request, Response response) { case MODE_SERVER_INBOUND: if (request.isLoggable()) { - getLogger() - .log(Level.FINE, "Redirecting via server dispatcher to: " + targetRef); + getLogger().fine(() -> "Redirecting via server dispatcher to: " + targetRef); } inboundServerRedirect(targetRef, request, response); break; + default: } } @@ -429,7 +423,11 @@ public void rewriteLocation(Request request, Response response) { protected void serverRedirect( Restlet next, Reference targetRef, Request request, Response response) { if (next == null) { - getLogger().warning("No next Restlet provided for server redirection to " + targetRef); + getLogger() + .warning( + () -> + "No next Restlet provided for server redirection to " + + targetRef); } else { // Save the base URI if it exists as we might need it for // redirections diff --git a/org.restlet/src/main/java/org/restlet/routing/Template.java b/org.restlet/src/main/java/org/restlet/routing/Template.java index 3120f9bdd2..1d0698081e 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Template.java +++ b/org.restlet/src/main/java/org/restlet/routing/Template.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -100,7 +101,7 @@ private static void appendGroup(StringBuilder pattern, String content, boolean r * @return The Regex pattern string corresponding to a variable. */ private static String getVariableRegex(Variable variable) { - String result = null; + final String result; if (variable.isFixed()) { result = "(" + Pattern.quote(variable.getDefaultValue()) + ")"; @@ -192,6 +193,7 @@ private static String getVariableRegex(Variable variable) { case Variable.TYPE_COMMENT_ATTRIBUTE: appendClass(coreRegex, COMMENT_ATTRIBUTE, variable.isRequired()); break; + default: } result = coreRegex.toString(); @@ -346,24 +348,21 @@ public String format(Resolver resolver) { } else if (next == '}') { // End of variable detected if (varBuffer.isEmpty()) { - getLogger() - .warning( - "Empty pattern variables are not allowed : " - + this.regexPattern); + logEmptyVariablesMessage(); } else { final String varName = varBuffer.toString(); Object varValue = resolver.resolve(varName); - Variable var = getVariables().get(varName); + Variable variable = getVariables().get(varName); // Use the default values instead if (varValue == null) { - if (var == null) { - var = getDefaultVariable(); + if (variable == null) { + variable = getDefaultVariable(); } - if (var != null) { - varValue = var.getDefaultValue(); + if (variable != null) { + varValue = variable.getDefaultValue(); } } @@ -371,13 +370,13 @@ public String format(Resolver resolver) { if (this.encodingVariables) { // In case the values must be encoded. - if (var != null) { - result.append(var.encode(varValueString)); + if (variable != null) { + result.append(variable.encode(varValueString)); } else { result.append(Reference.encode(varValueString)); } } else { - if ((var != null) && var.isEncodingOnFormat()) { + if ((variable != null) && variable.isEncodingOnFormat()) { result.append(Reference.encode(varValueString)); } else { result.append(varValueString); @@ -389,20 +388,14 @@ public String format(Resolver resolver) { } inVariable = false; } else { - getLogger() - .warning( - "An invalid character was detected inside a pattern variable : " - + this.regexPattern); + logInvalidCharacter(this.regexPattern); } } else { if (next == '{') { inVariable = true; varBuffer = new StringBuilder(); } else if (next == '}') { - getLogger() - .warning( - "An invalid character was detected inside a pattern variable : " - + this.regexPattern); + logInvalidCharacter(this.regexPattern); } else { result.append(next); } @@ -411,6 +404,18 @@ public String format(Resolver resolver) { return result.toString(); } + private void logInvalidCharacter(Object pattern) { + getLogger() + .warning( + () -> + "An invalid character was detected inside a pattern variable : " + + pattern); + } + + private void logEmptyVariablesMessage() { + getLogger().warning(() -> "Empty pattern variables are not allowed : " + this.regexPattern); + } + /** * Returns the default variable. * @@ -471,10 +476,7 @@ private Pattern getRegexPattern() { } else if (next == '}') { // End of variable detected if (varBuffer.isEmpty()) { - getLogger() - .warning( - "Empty pattern variables are not allowed : " - + this.regexPattern); + logEmptyVariablesMessage(); } else { final String varName = varBuffer.toString(); final int varIndex = getRegexVariables().indexOf(varName); @@ -488,11 +490,11 @@ private Pattern getRegexPattern() { // New variable detected. Insert a // capturing group. getRegexVariables().add(varName); - Variable var = getVariables().get(varName); - if (var == null) { - var = getDefaultVariable(); + Variable variable = getVariables().get(varName); + if (variable == null) { + variable = getDefaultVariable(); } - patternBuffer.append(getVariableRegex(var)); + patternBuffer.append(getVariableRegex(variable)); } // Reset the variable name buffer @@ -501,20 +503,14 @@ private Pattern getRegexPattern() { inVariable = false; } else { - getLogger() - .warning( - "An invalid character was detected inside a pattern variable : " - + this.regexPattern); + logInvalidCharacter(this.regexPattern); } } else { if (next == '{') { inVariable = true; varBuffer = new StringBuilder(); } else if (next == '}') { - getLogger() - .warning( - "An invalid character was detected inside a pattern variable : " - + this.regexPattern); + logInvalidCharacter(this.regexPattern); } else { patternBuffer.append(quote(next)); } @@ -558,10 +554,10 @@ public List getVariableNames() { StringBuilder varBuffer = null; char next; boolean inVariable = false; - final String pattern = getPattern(); + final String currentPattern = getPattern(); - for (int i = 0; i < pattern.length(); i++) { - next = pattern.charAt(i); + for (int i = 0; i < currentPattern.length(); i++) { + next = currentPattern.charAt(i); if (inVariable) { if (Reference.isUnreserved(next)) { @@ -572,8 +568,9 @@ public List getVariableNames() { if (varBuffer.isEmpty()) { getLogger() .warning( - "Empty pattern variables are not allowed : " - + this.pattern); + () -> + "Empty pattern variables are not allowed : " + + this.pattern); } else { result.add(varBuffer.toString()); @@ -583,20 +580,14 @@ public List getVariableNames() { inVariable = false; } else { - getLogger() - .warning( - "An invalid character was detected inside a pattern variable : " - + this.pattern); + logInvalidCharacter(this.pattern); } } else { if (next == '{') { inVariable = true; varBuffer = new StringBuilder(); } else if (next == '}') { - getLogger() - .warning( - "An invalid character was detected inside a pattern variable : " - + this.pattern); + logInvalidCharacter(this.pattern); } } } @@ -637,9 +628,8 @@ public int match(String formattedString) { if (formattedString != null) { final Matcher matcher = getRegexPattern().matcher(formattedString); - if ((getMatchingMode() == MODE_EQUALS) && matcher.matches()) { - result = matcher.end(); - } else if ((getMatchingMode() == MODE_STARTS_WITH) && matcher.lookingAt()) { + if (((getMatchingMode() == MODE_EQUALS) && matcher.matches()) + || ((getMatchingMode() == MODE_STARTS_WITH) && matcher.lookingAt())) { result = matcher.end(); } } @@ -695,26 +685,24 @@ public int parse(String formattedString, Map variables, boolean result = matcher.end(); // Update the attributes with the variables value - String attributeName = null; - String attributeValue = null; + String attributeName; + String attributeValue; for (int i = 0; i < getRegexVariables().size(); i++) { attributeName = getRegexVariables().get(i); attributeValue = matcher.group(i + 1); - Variable var = getVariables().get(attributeName); + Variable variable = getVariables().get(attributeName); - if ((var != null) && var.isDecodingOnParse()) { + if ((variable != null) && variable.isDecodingOnParse()) { attributeValue = Reference.decode(attributeValue); } if (loggable) { getLogger() - .fine( - "Template variable \"" - + attributeName - + "\" matched with value \"" - + attributeValue - + "\""); + .log( + Level.FINE, + "Template variable \"{0}\" matched with value \"{1}\"", + new Object[] {attributeName, attributeValue}); } variables.put(attributeName, attributeValue); @@ -723,8 +711,9 @@ public int parse(String formattedString, Map variables, boolean } catch (StackOverflowError soe) { getLogger() .warning( - "StackOverflowError exception encountered while matching this string : " - + formattedString); + () -> + "StackOverflowError exception encountered while matching this string : " + + formattedString); } } diff --git a/org.restlet/src/main/java/org/restlet/service/ConverterService.java b/org.restlet/src/main/java/org/restlet/service/ConverterService.java index 0dd71a1709..3ecd952855 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConverterService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConverterService.java @@ -124,9 +124,8 @@ public List getPatchTypes(MediaType representationType) { * @param source The source class. * @param target The expected representation metadata. * @return The list of variants that can be converted. - * @throws IOException */ - public List getVariants(Class source, Variant target) throws IOException { + public List getVariants(Class source, Variant target) { return ConverterUtils.getVariants(source, target); } @@ -175,7 +174,15 @@ public T toObject(Representation source, Class target, Resource resource) if ((source != null) && source.isAvailable() && (source.getSize() != 0)) { ConverterHelper ch = ConverterUtils.getBestHelper(source, target, resource); - if (ch != null) { + if (ch == null) { + if (loggable) { + Context.getCurrentLogger() + .log( + Level.WARNING, + "Unable to find a converter for this representation : {0}", + source); + } + } else { if (loggable && Context.getCurrentLogger().isLoggable(Level.FINE)) { Context.getCurrentLogger() .fine( @@ -195,13 +202,6 @@ public T toObject(Representation source, Class target, Resource resource) resultRepresentation.getEncodings().addAll(source.getEncodings()); resultRepresentation.getLanguages().addAll(source.getLanguages()); } - } else { - if (loggable) { - Context.getCurrentLogger() - .warning( - "Unable to find a converter for this representation : " - + source); - } } } @@ -318,7 +318,10 @@ public Representation toRepresentation(Object source, Variant target, Resource r } else { if (loggable) { Context.getCurrentLogger() - .warning("Unable to find a converter for this object : " + source); + .log( + Level.WARNING, + "Unable to find a converter for this object : {0}", + source); } } From d9728548d1b964ed6c09606c5996f2f2c16cf0be Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 20:59:36 +0200 Subject: [PATCH 22/30] Take into account Sonar comments --- .../internal/RestletOpenApiReader.java | 31 +-- .../restlet/ext/velocity/TemplateFilter.java | 3 +- .../ext/velocity/TemplateRepresentation.java | 31 ++- .../restlet/ext/xml/SaxRepresentation.java | 3 +- .../ext/xml/TransformRepresentation.java | 23 +- .../restlet/engine/adapter/ClientAdapter.java | 45 ++-- .../engine/resource/AnnotationUtils.java | 5 +- .../engine/resource/MethodAnnotationInfo.java | 14 +- .../BufferingRepresentation.java | 15 +- .../representation/StringRepresentation.java | 4 +- .../org/restlet/representation/Variant.java | 197 +++++++--------- .../org/restlet/resource/ServerResource.java | 210 ++++++++---------- .../java/org/restlet/service/TaskService.java | 54 +++-- .../java/org/restlet/util/ClientList.java | 2 +- .../java/org/restlet/util/WrapperList.java | 10 + .../engine/jetty/resource/MyResource11.java | 1 - .../resource/GenericServerResource16.java | 1 - .../resource/GenericServerResource17.java | 1 - 18 files changed, 277 insertions(+), 373 deletions(-) diff --git a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java index cb47a73eb7..5b0fdc875e 100644 --- a/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java +++ b/org.restlet.ext.openapi/src/main/java/org/restlet/ext/openapi/internal/RestletOpenApiReader.java @@ -26,7 +26,6 @@ import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.responses.ApiResponse; -import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; @@ -39,7 +38,6 @@ import org.restlet.engine.resource.MethodAnnotationInfo; import org.restlet.representation.Variant; import org.restlet.resource.Finder; -import org.restlet.resource.ResourceException; import org.restlet.resource.ServerResource; import org.restlet.routing.Route; import org.restlet.routing.Router; @@ -211,29 +209,23 @@ private void completeOperation( final ServerResource serverResource, final Operation operation, MethodAnnotationInfo methodAnnotationInfo) { - try { - var methodOperationAnnotation = - ReflectionUtils.getAnnotation( - methodAnnotationInfo.getJavaMethod(), - io.swagger.v3.oas.annotations.Operation.class); - - if (methodOperationAnnotation != null) { - OpenApiAnnotationProcessor.documentOperation(operation, methodOperationAnnotation); - } - - completeOperationInput(serverResource, operation, methodAnnotationInfo); - completeOperationSuccessfulOutput(serverResource, operation, methodAnnotationInfo); + var methodOperationAnnotation = + ReflectionUtils.getAnnotation( + methodAnnotationInfo.getJavaMethod(), + io.swagger.v3.oas.annotations.Operation.class); - } catch (IOException e) { - throw new ResourceException(e); + if (methodOperationAnnotation != null) { + OpenApiAnnotationProcessor.documentOperation(operation, methodOperationAnnotation); } + + completeOperationInput(serverResource, operation, methodAnnotationInfo); + completeOperationSuccessfulOutput(serverResource, operation, methodAnnotationInfo); } private void completeOperationInput( ServerResource serverResource, Operation operation, - MethodAnnotationInfo methodAnnotationInfo) - throws IOException { + MethodAnnotationInfo methodAnnotationInfo) { Type[] parameterTypes = methodAnnotationInfo.getJavaMethod().getGenericParameterTypes(); if (parameterTypes.length == 0) { return; @@ -262,8 +254,7 @@ private void completeOperationInput( private void completeOperationSuccessfulOutput( ServerResource serverResource, Operation operation, - MethodAnnotationInfo methodAnnotationInfo) - throws IOException { + MethodAnnotationInfo methodAnnotationInfo) { List responseVariants = methodAnnotationInfo.getResponseVariants( metadataService, serverResource.getConverterService()); diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java index 7047860c74..f570f01e21 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateFilter.java @@ -8,7 +8,6 @@ */ package org.restlet.ext.velocity; -import java.io.IOException; import java.util.Map; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; @@ -115,7 +114,7 @@ protected void afterHandle(Request request, Response response) { response.setEntity(representation); } catch (ResourceNotFoundException e) { response.setStatus(Status.CLIENT_ERROR_NOT_FOUND, e); - } catch (ParseErrorException | IOException e) { + } catch (ParseErrorException e) { response.setStatus(Status.SERVER_ERROR_INTERNAL, e); } } diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java index 8ecf647104..e45689547f 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java @@ -151,12 +151,11 @@ public TemplateRepresentation( * * @param templateRepresentation The representation to 'decode'. * @param mediaType The representation's media-type. - * @throws IOException * @throws ParseErrorException * @throws ResourceNotFoundException */ public TemplateRepresentation(Representation templateRepresentation, MediaType mediaType) - throws ResourceNotFoundException, ParseErrorException, IOException { + throws ResourceNotFoundException, ParseErrorException { super(mediaType); this.engine = null; this.template = new Template(); @@ -263,17 +262,15 @@ public VelocityEngine getEngine() { * @return The Velocity template. */ public Template getTemplate() { - if (this.template == null) { - if (this.templateName != null) { - try { - getEngine().init(); - this.template = getEngine().getTemplate(this.templateName); - } catch (Exception e) { - final Context context = Context.getCurrent(); - - if (context != null) { - context.getLogger().log(Level.WARNING, "Unable to get template", e); - } + if (this.template == null && this.templateName != null) { + try { + getEngine().init(); + this.template = getEngine().getTemplate(this.templateName); + } catch (Exception e) { + final Context currentContext = Context.getCurrent(); + + if (currentContext != null) { + currentContext.getLogger().log(Level.WARNING, "Unable to get template", e); } } } @@ -333,14 +330,12 @@ public void write(Writer writer) throws IOException { // Process the template getTemplate().merge(getContext(), writer); } catch (Exception e) { - final Context context = Context.getCurrent(); + final Context currentContext = Context.getCurrent(); - if (context != null) { - context.getLogger().log(Level.WARNING, "Unable to process the template", e); + if (currentContext != null) { + currentContext.getLogger().log(Level.WARNING, "Unable to process the template", e); } - e.printStackTrace(); - throw new IOException("Template processing error. " + e.getMessage()); } } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java index bb81fe078b..5d09b68bac 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/SaxRepresentation.java @@ -48,8 +48,7 @@ public class SaxRepresentation extends XmlRepresentation { */ public static final boolean XML_SECURE_PROCESSING = (System.getProperty("org.restlet.ext.xml.secureProcessing") == null) - ? true - : Boolean.getBoolean("org.restlet.ext.xml.secureProcessing"); + || Boolean.getBoolean("org.restlet.ext.xml.secureProcessing"); /** Limits potential XML overflow attacks. */ private boolean secureProcessing; diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java index 89c7b23bee..c7043fcd76 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/TransformRepresentation.java @@ -239,8 +239,7 @@ public SAXSource getSaxSource() throws IOException { * @return The default SAX transformer factory. */ private SAXTransformerFactory getSaxTransformerFactory() { - SAXTransformerFactory result = (SAXTransformerFactory) TransformerFactory.newInstance(); - return result; + return (SAXTransformerFactory) TransformerFactory.newInstance(); } /** @@ -298,10 +297,10 @@ public Transformer getTransformer() throws IOException { Transformer result = null; try { - Templates templates = getTemplates(); + Templates currentTemplates = getTemplates(); - if (templates != null) { - result = templates.newTransformer(); + if (currentTemplates != null) { + result = currentTemplates.newTransformer(); if (getErrorListener() != null) { result.setErrorListener(getErrorListener()); @@ -337,11 +336,11 @@ public Transformer getTransformer() throws IOException { */ public TransformerHandler getTransformerHandler() throws IOException { TransformerHandler result = null; - Templates templates = getTemplates(); + Templates currentTemplates = getTemplates(); - if (templates != null) { + if (currentTemplates != null) { try { - result = getSaxTransformerFactory().newTransformerHandler(templates); + result = getSaxTransformerFactory().newTransformerHandler(currentTemplates); } catch (TransformerConfigurationException tce) { throw new IOException("Transformer configuration exception. " + tce.getMessage()); } @@ -376,11 +375,11 @@ public URIResolver getUriResolver() { */ public XMLFilter getXmlFilter() throws IOException { XMLFilter result = null; - final Templates templates = getTemplates(); + final Templates currentTemplates = getTemplates(); - if (templates != null) { + if (currentTemplates != null) { try { - result = getSaxTransformerFactory().newXMLFilter(templates); + result = getSaxTransformerFactory().newXMLFilter(currentTemplates); } catch (TransformerConfigurationException tce) { throw new IOException("Transformer configuration exception. " + tce.getMessage()); } @@ -390,7 +389,7 @@ public XMLFilter getXmlFilter() throws IOException { } /** - * Releases the source and transform sheet representations, the transformer and the URI + * Releases the source and transform sheet representations, the transformer, and the URI * resolver. */ @Override diff --git a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java index cee7ea2a59..e15ea6b292 100644 --- a/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java +++ b/org.restlet/src/main/java/org/restlet/engine/adapter/ClientAdapter.java @@ -44,10 +44,8 @@ public ClientAdapter(Context context) { * @param httpCall The original HTTP call. * @param request The high-level request. * @param response The high-level response. - * @throws Exception */ - public void commit(final ClientCall httpCall, Request request, Response response) - throws Exception { + public void commit(final ClientCall httpCall, Request request, Response response) { if (httpCall != null) { // Check if the call is asynchronous if (request.isAsynchronous()) { @@ -57,27 +55,26 @@ public void commit(final ClientCall httpCall, Request request, Response response httpCall.sendRequest( request, response, - (Uniform) - (request1, response1) -> { - try { - updateResponse( - response1, - new Status( - httpCall.getStatusCode(), - httpCall.getReasonPhrase()), - httpCall); - - if (userCallback != null) { - userCallback.handle(request1, response1); - } - } catch (Exception exception) { - getLogger() - .log( - Level.WARNING, - "Unexpected error or exception inside the user call back", - exception); - } - }); + (request1, response1) -> { + try { + updateResponse( + response1, + new Status( + httpCall.getStatusCode(), + httpCall.getReasonPhrase()), + httpCall); + + if (userCallback != null) { + userCallback.handle(request1, response1); + } + } catch (Exception exception) { + getLogger() + .log( + Level.WARNING, + "Unexpected error or exception inside the user call back", + exception); + } + }); } else { updateResponse(response, httpCall.sendRequest(request), httpCall); } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java index 83c08ee7a4..69f3df0ac7 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationUtils.java @@ -8,7 +8,6 @@ */ package org.restlet.engine.resource; -import java.io.IOException; import java.lang.annotation.Annotation; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -249,7 +248,6 @@ public MethodAnnotationInfo getMethodAnnotation( * @param metadataService The metadata service to use. * @param converterService The converter service to use. * @return The annotation descriptor. - * @throws IOException */ public MethodAnnotationInfo getMethodAnnotation( List annotations, @@ -257,8 +255,7 @@ public MethodAnnotationInfo getMethodAnnotation( Form query, Representation entity, MetadataService metadataService, - org.restlet.service.ConverterService converterService) - throws IOException { + org.restlet.service.ConverterService converterService) { if (annotations != null) { for (AnnotationInfo annotationInfo : annotations) { if (annotationInfo instanceof MethodAnnotationInfo methodAnnotationInfo diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java index 731f676a9e..ce0064ffd8 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/MethodAnnotationInfo.java @@ -8,7 +8,6 @@ */ package org.restlet.engine.resource; -import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -331,22 +330,19 @@ public int hashCode() { * @param metadataService The metadata service to use. * @param converterService The converter service to use. * @return True if the annotated method is compatible. - * @throws IOException */ public boolean isCompatible( Method restletMethod, Form queryParams, Representation requestEntity, MetadataService metadataService, - org.restlet.service.ConverterService converterService) - throws IOException { + org.restlet.service.ConverterService converterService) { boolean result = true; // Verify query parameters if (getQuery() != null) { - Form requiredParams = new Form(getQuery()); - - for (Iterator iter = requiredParams.iterator(); iter.hasNext() && result; ) { + for (Iterator iter = new Form(getQuery()).iterator(); + iter.hasNext() && result; ) { result = queryParams.contains(iter.next()); } } @@ -371,13 +367,11 @@ public boolean isCompatible( * @param metadataService The metadata service to use. * @param converterService The converter service to use. * @return True if the given request entity is compatible with the annotated method described. - * @throws IOException */ public boolean isCompatibleRequestEntity( Representation requestEntity, MetadataService metadataService, - org.restlet.service.ConverterService converterService) - throws IOException { + org.restlet.service.ConverterService converterService) { boolean result = true; if ((requestEntity != null) && requestEntity.isAvailable()) { diff --git a/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java index 43c806130b..5ed4a8e7d9 100644 --- a/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/BufferingRepresentation.java @@ -54,14 +54,12 @@ public BufferingRepresentation(Representation bufferedRepresentation) { * @throws IOException */ private void buffer() throws IOException { - if (!isBuffered()) { - if (getWrappedRepresentation().isAvailable()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - getWrappedRepresentation().write(baos); - baos.flush(); - setBuffer(baos.toByteArray()); - setBuffered(true); - } + if (!isBuffered() && getWrappedRepresentation().isAvailable()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + getWrappedRepresentation().write(baos); + baos.flush(); + setBuffer(baos.toByteArray()); + setBuffered(true); } } @@ -102,7 +100,6 @@ public InputStream getStream() throws IOException { buffer(); return (getBuffer() != null) ? new ByteArrayInputStream(getBuffer()) : null; } - ; @Override public String getText() throws IOException { diff --git a/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java index e474cfe515..75011e5010 100644 --- a/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/StringRepresentation.java @@ -117,9 +117,7 @@ public Reader getReader() throws IOException { public InputStream getStream() throws IOException { CharacterSet charset = getCharacterSet() == null ? CharacterSet.ISO_8859_1 : getCharacterSet(); - ByteArrayInputStream result = - new ByteArrayInputStream(getText().getBytes(charset.getName())); - return result; + return new ByteArrayInputStream(getText().getBytes(charset.getName())); } @Override diff --git a/org.restlet/src/main/java/org/restlet/representation/Variant.java b/org.restlet/src/main/java/org/restlet/representation/Variant.java index 6fa4613c84..d6b462b11d 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Variant.java +++ b/org.restlet/src/main/java/org/restlet/representation/Variant.java @@ -10,7 +10,6 @@ import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Objects; import org.restlet.data.CharacterSet; @@ -152,61 +151,7 @@ public CharacterSet getCharacterSet() { */ public List getEncodings() { if (this.encodings == null) { - this.encodings = - new WrapperList<>() { - - @Override - public boolean add(Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.add(element); - } - - @Override - public void add(int index, Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - super.add(index, element); - } - - @Override - public boolean addAll(Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = - elements.iterator(); - !addNull && iterator.hasNext(); ) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(elements); - } - - @Override - public boolean addAll(int index, Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = - elements.iterator(); - !addNull && iterator.hasNext(); ) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(index, elements); - } - }; + this.encodings = new MetadataList<>("encoding"); } return this.encodings; @@ -223,61 +168,7 @@ public boolean addAll(int index, Collection elements) { */ public List getLanguages() { if (this.languages == null) { - this.languages = - new WrapperList<>() { - - @Override - public void add(int index, Language element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - super.add(index, element); - } - - @Override - public boolean add(Language element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - return super.add(element); - } - - @Override - public boolean addAll(Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = - elements.iterator(); - !addNull && iterator.hasNext(); ) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - return super.addAll(elements); - } - - @Override - public boolean addAll(int index, Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = - elements.iterator(); - !addNull && iterator.hasNext(); ) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null language."); - } - - return super.addAll(index, elements); - } - }; + this.languages = new MetadataList<>("language"); } return this.languages; } @@ -468,8 +359,6 @@ public String toString() { if (!getEncodings().isEmpty()) { if (!first) { sb.append(","); - } else { - first = false; } sb.append(getEncodings()); @@ -478,4 +367,86 @@ public String toString() { sb.append("]"); return sb.toString(); } + + /** + * A list that prevents adding null elements. + * + * @param + */ + private static class MetadataList extends WrapperList { + + private final String exceptionMessage; + + private MetadataList(final String label) { + this.exceptionMessage = "Cannot add a null " + label; + } + + @Override + public boolean add(final T element) { + validate(element); + return super.add(element); + } + + @Override + public void add(final int index, final T element) { + validate(element); + super.add(index, element); + } + + @Override + public void addFirst(final T element) { + validate(element); + super.addFirst(element); + } + + @Override + public void addLast(final T element) { + validate(element); + super.addLast(element); + } + + @Override + public boolean addAll(final Collection elements) { + validate(elements); + return super.addAll(elements); + } + + @Override + public boolean addAll(final int index, final Collection elements) { + validate(elements); + return super.addAll(index, elements); + } + + @Override + public boolean equals(final Object obj) { + return super.equals(obj) + && Objects.equals(exceptionMessage, ((MetadataList) obj).exceptionMessage); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + private void validate(final T element) { + if (element == null) { + throw new IllegalArgumentException(this.exceptionMessage); + } + } + + private void validate(final Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (T element : elements) { + if (element == null) { + addNull = true; + break; + } + } + } + if (addNull) { + throw new IllegalArgumentException(exceptionMessage); + } + } + } } diff --git a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java index d414b50abe..fa753904f3 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java @@ -143,18 +143,12 @@ public void commit() { */ protected Representation delete() throws ResourceException { Representation result = null; - MethodAnnotationInfo annotationInfo; + MethodAnnotationInfo annotationInfo = getAnnotation(Method.DELETE); - try { - annotationInfo = getAnnotation(Method.DELETE); - - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); } return result; @@ -298,8 +292,8 @@ protected Representation doConditionalHandle() throws ResourceException { } if ((Method.GET.equals(getMethod()) || Method.HEAD.equals(getMethod())) - && resultInfo instanceof Representation) { - result = (Representation) resultInfo; + && resultInfo instanceof Representation representation) { + result = representation; } else if ((getStatus() != null) && getStatus().isSuccess()) { // Conditions were passed successfully, continue the normal // processing. @@ -335,19 +329,14 @@ protected void doError(Status errorStatus) { * @throws ResourceException */ private RepresentationInfo doGetInfo() throws ResourceException { - RepresentationInfo result = null; - MethodAnnotationInfo annotationInfo; + final RepresentationInfo result; - try { - annotationInfo = getAnnotation(Method.GET); + MethodAnnotationInfo annotationInfo = getAnnotation(Method.GET); - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - result = getInfo(); - } - } catch (IOException e) { - throw new ResourceException(e); + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + result = getInfo(); } return result; @@ -498,28 +487,23 @@ && getRequestEntity().getSize() != 0) { * @param query The query parameters. * @param entity The request entity (can be null, or unavailable). * @return The response entity. - * @throws IOException */ private Representation doHandle(Method method, Form query, Representation entity) throws ResourceException { Representation result = null; - try { - if (getAnnotation(method) != null) { - // We know the method is supported, let's check the entity. - MethodAnnotationInfo annotationInfo = getAnnotation(method, query, entity); + if (getAnnotation(method) != null) { + // We know the method is supported, let's check the entity. + MethodAnnotationInfo annotationInfo = getAnnotation(method, query, entity); - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - // The request entity is not supported. - doError(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE); - } + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); + // The request entity is not supported. + doError(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE); } - } catch (IOException e) { - throw new ResourceException(e); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); } return result; @@ -528,7 +512,7 @@ private Representation doHandle(Method method, Form query, Representation entity /** * Effectively handles a call with content negotiation of the response entity. The default * behavior is to dispatch the call to one of the {@link #get(Variant)}, {@link - * #post(Representation,Variant)}, {@link #put(Representation,Variant)}, {@link + * #post(Representation, Variant)}, {@link #put(Representation, Variant)}, {@link * #delete(Variant)}, {@link #head(Variant)} or {@link #options(Variant)} methods. * * @param variant The response variant expected. @@ -585,8 +569,9 @@ protected Representation doHandle(Variant variant) throws ResourceException { /** * Effectively handles a call with content negotiation of the response entity. The default * behavior is to dispatch the call to call a matching annotated method or one of the {@link - * #get(Variant)}, {@link #post(Representation,Variant)}, {@link #put(Representation,Variant)}, - * {@link #delete(Variant)}, {@link #head(Variant)} or {@link #options(Variant)} methods.
+ * #get(Variant)}, {@link #post(Representation, Variant)}, {@link #put(Representation, + * Variant)}, {@link #delete(Variant)}, {@link #head(Variant)} or {@link #options(Variant)} + * methods.
*
* If no acceptable variant is found, the {@link * org.restlet.data.Status#CLIENT_ERROR_NOT_ACCEPTABLE} status is set. @@ -633,18 +618,12 @@ protected Representation doNegotiatedHandle() throws ResourceException { */ protected Representation get() throws ResourceException { Representation result = null; - MethodAnnotationInfo annotationInfo; + MethodAnnotationInfo annotationInfo = getAnnotation(Method.GET); - try { - annotationInfo = getAnnotation(Method.GET); - - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); } return result; @@ -662,8 +641,8 @@ protected Representation get() throws ResourceException { * * @param variant The variant whose full representation must be returned. * @return The resource's representation. - * @see #get(Variant) * @throws ResourceException + * @see #get(Variant) */ protected Representation get(Variant variant) throws ResourceException { Representation result = null; @@ -682,9 +661,8 @@ protected Representation get(Variant variant) throws ResourceException { * * @param method The method to match. * @return The annotation descriptor. - * @throws IOException */ - protected MethodAnnotationInfo getAnnotation(Method method) throws IOException { + protected MethodAnnotationInfo getAnnotation(Method method) { return getAnnotation(method, getQuery(), null); } @@ -695,10 +673,8 @@ protected MethodAnnotationInfo getAnnotation(Method method) throws IOException { * @param query The query parameters. * @param entity The request entity or null. * @return The annotation descriptor. - * @throws IOException */ - protected MethodAnnotationInfo getAnnotation(Method method, Form query, Representation entity) - throws IOException { + protected MethodAnnotationInfo getAnnotation(Method method, Form query, Representation entity) { if (isAnnotated()) { return AnnotationUtils.getInstance() .getMethodAnnotation( @@ -870,64 +846,58 @@ private List getVariantsFromAnnotations(Method method) { continue; } - try { - final boolean methodAnnotationIsCompatible = - methodAnnotationInfo.isCompatible( - method, - getQuery(), - getRequestEntity(), - getMetadataService(), - getConverterService()); - - if (!methodAnnotationIsCompatible) { - continue; - } + final boolean methodAnnotationIsCompatible = + methodAnnotationInfo.isCompatible( + method, + getQuery(), + getRequestEntity(), + getMetadataService(), + getConverterService()); - final List responseVariants = - methodAnnotationInfo.getResponseVariants( - getMetadataService(), getConverterService()); + if (!methodAnnotationIsCompatible) { + continue; + } - if (responseVariants == null) { - continue; - } + final List responseVariants = + methodAnnotationInfo.getResponseVariants( + getMetadataService(), getConverterService()); - // Compute an affinity score between this annotation and the input entity. - float score = 0.5f; - if ((getRequest().getEntity() != null) && getRequest().getEntity().isAvailable()) { - final MediaType requestEntityMediaType = - getRequest().getEntity().getMediaType(); - final List amts = - getMetadataService().getAllMediaTypes(methodAnnotationInfo.getInput()); - if (amts != null) { - for (MediaType amt : amts) { - if (amt.equals(requestEntityMediaType)) { - score = 1.0f; - } else if (amt.includes(requestEntityMediaType)) { - score = Math.max(0.8f, score); - } else if (amt.isCompatible(requestEntityMediaType)) { - score = Math.max(0.6f, score); - } + if (responseVariants == null) { + continue; + } + + // Compute an affinity score between this annotation and the input entity. + float score = 0.5f; + if ((getRequest().getEntity() != null) && getRequest().getEntity().isAvailable()) { + final MediaType requestEntityMediaType = getRequest().getEntity().getMediaType(); + final List amts = + getMetadataService().getAllMediaTypes(methodAnnotationInfo.getInput()); + if (amts != null) { + for (MediaType amt : amts) { + if (amt.equals(requestEntityMediaType)) { + score = 1.0f; + } else if (amt.includes(requestEntityMediaType)) { + score = Math.max(0.8f, score); + } else if (amt.isCompatible(requestEntityMediaType)) { + score = Math.max(0.6f, score); } } - } else if (methodAnnotationInfo.getInput() == null - || methodAnnotationInfo - .getInput() - .isEmpty()) { // the annotation does not declare input media type - score = 1.0f; - } else if (methodAnnotationInfo.getJavaInputTypes() == null - || methodAnnotationInfo.getJavaInputTypes().length - == 0) { // the annotated method does not require an entity - score = 0.9f; - } - - for (Variant variant : responseVariants) { - final VariantInfo variantInfo = new VariantInfo(variant, methodAnnotationInfo); - variantInfo.setInputScore(score); - result.add(variantInfo); } + } else if (methodAnnotationInfo.getInput() == null + || methodAnnotationInfo + .getInput() + .isEmpty()) { // the annotation does not declare input media type + score = 1.0f; + } else if (methodAnnotationInfo.getJavaInputTypes() == null + || methodAnnotationInfo.getJavaInputTypes().length + == 0) { // the annotated method does not require an entity + score = 0.9f; + } - } catch (IOException e) { - getLogger().log(Level.FINE, "Unable to get variants from annotation", e); + for (Variant variant : responseVariants) { + final VariantInfo variantInfo = new VariantInfo(variant, methodAnnotationInfo); + variantInfo.setInputScore(score); + result.add(variantInfo); } } @@ -1030,8 +1000,8 @@ protected Representation head() throws ResourceException { * * @param variant The variant whose full representation must be returned. * @return The resource's representation. - * @see #get(Variant) * @throws ResourceException + * @see #get(Variant) */ protected Representation head(Variant variant) throws ResourceException { return get(variant); @@ -1118,21 +1088,15 @@ public boolean isNegotiated() { */ protected Representation options() throws ResourceException { Representation result = null; - MethodAnnotationInfo annotationInfo; - - try { - annotationInfo = getAnnotation(Method.OPTIONS); + MethodAnnotationInfo annotationInfo = getAnnotation(Method.OPTIONS); - // Updates the list of allowed methods - updateAllowedMethods(); + // Updates the list of allowed methods + updateAllowedMethods(); - if (annotationInfo != null) { - result = doHandle(annotationInfo, null); - } else { - doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - } catch (IOException e) { - throw new ResourceException(e); + if (annotationInfo != null) { + result = doHandle(annotationInfo, null); + } else { + doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); } return result; diff --git a/org.restlet/src/main/java/org/restlet/service/TaskService.java b/org.restlet/src/main/java/org/restlet/service/TaskService.java index 2ef6e8b87d..6849c85d13 100644 --- a/org.restlet/src/main/java/org/restlet/service/TaskService.java +++ b/org.restlet/src/main/java/org/restlet/service/TaskService.java @@ -102,35 +102,31 @@ public void execute(final Runnable runnable) { final Response currentResponse = Response.getCurrent(); executorService.execute( - new Runnable() { - public void run() { - // Copy the thread local variables - Response.setCurrent(currentResponse); - Context.setCurrent(currentContext); - VirtualHost.setCurrent(currentVirtualHost); - Application.setCurrent(currentApplication); - - if (runnable instanceof ContextualRunnable) { - ClassLoader tccl = - Thread.currentThread().getContextClassLoader(); - try { - // Run the user task - Thread.currentThread() - .setContextClassLoader( - ((ContextualRunnable) runnable) - .getContextClassLoader()); - runnable.run(); - } finally { - Engine.clearThreadLocalVariables(); - Thread.currentThread().setContextClassLoader(tccl); - } - } else { - try { - // Run the user task - runnable.run(); - } finally { - Engine.clearThreadLocalVariables(); - } + () -> { + // Copy the thread local variables + Response.setCurrent(currentResponse); + Context.setCurrent(currentContext); + VirtualHost.setCurrent(currentVirtualHost); + Application.setCurrent(currentApplication); + + if (runnable instanceof ContextualRunnable contextualRunnable) { + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + try { + // Run the user task + Thread.currentThread() + .setContextClassLoader( + contextualRunnable.getContextClassLoader()); + runnable.run(); + } finally { + Engine.clearThreadLocalVariables(); + Thread.currentThread().setContextClassLoader(tccl); + } + } else { + try { + // Run the user task + runnable.run(); + } finally { + Engine.clearThreadLocalVariables(); } } }); diff --git a/org.restlet/src/main/java/org/restlet/util/ClientList.java b/org.restlet/src/main/java/org/restlet/util/ClientList.java index df4cba5d39..58209f767d 100644 --- a/org.restlet/src/main/java/org/restlet/util/ClientList.java +++ b/org.restlet/src/main/java/org/restlet/util/ClientList.java @@ -29,7 +29,7 @@ public final class ClientList extends WrapperList { * @param context The context. */ public ClientList(Context context) { - super(new CopyOnWriteArrayList()); + super(new CopyOnWriteArrayList<>()); this.context = context; } diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperList.java b/org.restlet/src/main/java/org/restlet/util/WrapperList.java index 587870ffc1..ba624cfcb7 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperList.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperList.java @@ -89,6 +89,16 @@ public boolean addAll(int index, Collection elements) { return getDelegate().addAll(index, elements); } + @Override + public void addFirst(final E e) { + getDelegate().addFirst(e); + } + + @Override + public void addLast(final E e) { + getDelegate().addLast(e); + } + /** Removes the elements from this list. */ public void clear() { getDelegate().clear(); diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java index 2996357469..a225f9983b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/MyResource11.java @@ -22,5 +22,4 @@ public class MyResource11 extends AbstractAnnotatedServerResource03 { public String asText() { return "asText-txt"; } - ; } diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java index bcea0f70c7..2564e40ec8 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource16.java @@ -14,5 +14,4 @@ public class GenericServerResource16 extends AbstractGenericAnnotatedServerRe public E addResponse(E representation) { return representation; } - ; } diff --git a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java index cb987bee25..a2d0fbfc5f 100644 --- a/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java +++ b/org.restlet/src/test/java/org/restlet/resource/GenericServerResource17.java @@ -13,5 +13,4 @@ public class GenericServerResource17 extends ServerResource implements MyReso public E add(E rep) { return rep; } - ; } From 93ff8167022eea761c6b909203cfa4013e5bdece Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 21:26:04 +0200 Subject: [PATCH 23/30] Take into account Sonar comments --- .../application/EncodeRepresentation.java | 64 +------------ .../org/restlet/representation/Variant.java | 89 +----------------- .../org/restlet/util/NonNullItemsList.java | 94 +++++++++++++++++++ 3 files changed, 100 insertions(+), 147 deletions(-) create mode 100644 org.restlet/src/main/java/org/restlet/util/NonNullItemsList.java diff --git a/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java b/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java index 8177f12faa..c8ecd54c3e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/EncodeRepresentation.java @@ -13,8 +13,6 @@ import java.io.OutputStream; import java.io.Reader; import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; import java.util.List; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; @@ -25,7 +23,7 @@ import org.restlet.data.Encoding; import org.restlet.engine.io.IoUtils; import org.restlet.representation.Representation; -import org.restlet.util.WrapperList; +import org.restlet.util.NonNullItemsList; import org.restlet.util.WrapperRepresentation; /** @@ -49,7 +47,7 @@ public static List getSupportedEncodings() { } /** Indicates if the encoding can happen. */ - private volatile boolean canEncode; + private final boolean canEncode; /** The encoding to apply. */ private volatile Encoding encoding; @@ -108,61 +106,7 @@ public long getAvailableSize() { @Override public List getEncodings() { if (this.encodings == null) { - this.encodings = - new WrapperList<>() { - - @Override - public boolean add(Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.add(element); - } - - @Override - public void add(int index, Encoding element) { - if (element == null) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - super.add(index, element); - } - - @Override - public boolean addAll(Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = - elements.iterator(); - !addNull && iterator.hasNext(); ) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(elements); - } - - @Override - public boolean addAll(int index, Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (final Iterator iterator = - elements.iterator(); - !addNull && iterator.hasNext(); ) { - addNull = (iterator.next() == null); - } - } - if (addNull) { - throw new IllegalArgumentException("Cannot add a null encoding."); - } - - return super.addAll(index, elements); - } - }; + this.encodings = new NonNullItemsList<>("Cannot add a null encoding."); this.encodings.addAll(getWrappedRepresentation().getEncodings()); if (canEncode()) { this.encodings.add(this.encoding); @@ -246,8 +190,6 @@ public void write(OutputStream outputStream) throws IOException { stream.putNextEntry(new ZipEntry(name)); encoderOutputStream = stream; - } else if (this.encoding.equals(Encoding.IDENTITY)) { - // Encoder unnecessary for identity encoding } if (encoderOutputStream != null) { diff --git a/org.restlet/src/main/java/org/restlet/representation/Variant.java b/org.restlet/src/main/java/org/restlet/representation/Variant.java index d6b462b11d..d6d14a780f 100644 --- a/org.restlet/src/main/java/org/restlet/representation/Variant.java +++ b/org.restlet/src/main/java/org/restlet/representation/Variant.java @@ -8,7 +8,6 @@ */ package org.restlet.representation; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -20,7 +19,7 @@ import org.restlet.data.Preference; import org.restlet.data.Reference; import org.restlet.engine.util.SystemUtils; -import org.restlet.util.WrapperList; +import org.restlet.util.NonNullItemsList; /** * Descriptor for available representations of a resource. It contains all the important metadata @@ -151,7 +150,7 @@ public CharacterSet getCharacterSet() { */ public List getEncodings() { if (this.encodings == null) { - this.encodings = new MetadataList<>("encoding"); + this.encodings = new NonNullItemsList<>("Cannot add a null encoding"); } return this.encodings; @@ -168,7 +167,7 @@ public List getEncodings() { */ public List getLanguages() { if (this.languages == null) { - this.languages = new MetadataList<>("language"); + this.languages = new NonNullItemsList<>("Cannot add a null language"); } return this.languages; } @@ -367,86 +366,4 @@ public String toString() { sb.append("]"); return sb.toString(); } - - /** - * A list that prevents adding null elements. - * - * @param - */ - private static class MetadataList extends WrapperList { - - private final String exceptionMessage; - - private MetadataList(final String label) { - this.exceptionMessage = "Cannot add a null " + label; - } - - @Override - public boolean add(final T element) { - validate(element); - return super.add(element); - } - - @Override - public void add(final int index, final T element) { - validate(element); - super.add(index, element); - } - - @Override - public void addFirst(final T element) { - validate(element); - super.addFirst(element); - } - - @Override - public void addLast(final T element) { - validate(element); - super.addLast(element); - } - - @Override - public boolean addAll(final Collection elements) { - validate(elements); - return super.addAll(elements); - } - - @Override - public boolean addAll(final int index, final Collection elements) { - validate(elements); - return super.addAll(index, elements); - } - - @Override - public boolean equals(final Object obj) { - return super.equals(obj) - && Objects.equals(exceptionMessage, ((MetadataList) obj).exceptionMessage); - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - private void validate(final T element) { - if (element == null) { - throw new IllegalArgumentException(this.exceptionMessage); - } - } - - private void validate(final Collection elements) { - boolean addNull = (elements == null); - if (!addNull) { - for (T element : elements) { - if (element == null) { - addNull = true; - break; - } - } - } - if (addNull) { - throw new IllegalArgumentException(exceptionMessage); - } - } - } } diff --git a/org.restlet/src/main/java/org/restlet/util/NonNullItemsList.java b/org.restlet/src/main/java/org/restlet/util/NonNullItemsList.java new file mode 100644 index 0000000000..34b300cc21 --- /dev/null +++ b/org.restlet/src/main/java/org/restlet/util/NonNullItemsList.java @@ -0,0 +1,94 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.util; + +import java.util.Collection; +import java.util.Objects; + +/** + * A list that prevents adding null elements. + * + * @param + */ +public class NonNullItemsList extends WrapperList { + + private final String exceptionMessage; + + public NonNullItemsList(final String exceptionMessage) { + this.exceptionMessage = exceptionMessage; + } + + @Override + public boolean add(final T element) { + validate(element); + return super.add(element); + } + + @Override + public void add(final int index, final T element) { + validate(element); + super.add(index, element); + } + + @Override + public void addFirst(final T element) { + validate(element); + super.addFirst(element); + } + + @Override + public void addLast(final T element) { + validate(element); + super.addLast(element); + } + + @Override + public boolean addAll(final Collection elements) { + validate(elements); + return super.addAll(elements); + } + + @Override + public boolean addAll(final int index, final Collection elements) { + validate(elements); + return super.addAll(index, elements); + } + + @Override + public boolean equals(final Object obj) { + return super.equals(obj) + && Objects.equals(exceptionMessage, ((NonNullItemsList) obj).exceptionMessage); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + private void validate(final T element) { + if (element == null) { + throw new IllegalArgumentException(this.exceptionMessage); + } + } + + private void validate(final Collection elements) { + boolean addNull = (elements == null); + if (!addNull) { + for (T element : elements) { + if (element == null) { + addNull = true; + break; + } + } + } + if (addNull) { + throw new IllegalArgumentException(exceptionMessage); + } + } +} From 1f539f6e35cb52139ce27d7051f7147c6275536d Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 22:32:15 +0200 Subject: [PATCH 24/30] Take into account Sonar comments --- .../internal/HttpAwsS3SigningTestCase.java | 73 ++++++----- .../ext/jaas/ChallengeCallbackHandler.java | 24 ++-- .../restlet/ext/spring/SpringComponent.java | 38 +++--- .../ext/thymeleaf/TemplateRepresentation.java | 10 +- .../java/org/restlet/ext/xml/XmlWriter.java | 116 +++++++++--------- .../main/java/org/restlet/Application.java | 5 +- .../src/main/java/org/restlet/Request.java | 2 +- .../org/restlet/engine/CompositeHelper.java | 5 +- .../main/java/org/restlet/engine/Engine.java | 41 ++++--- .../engine/log/LoggingThreadFactory.java | 8 +- .../engine/resource/AnnotationInfo.java | 6 +- .../org/restlet/engine/util/StringUtils.java | 2 +- .../org/restlet/resource/ServerResource.java | 31 +++-- .../java/org/restlet/routing/VirtualHost.java | 5 +- .../org/restlet/security/Authenticator.java | 28 ++--- .../security/ChallengeAuthenticator.java | 5 +- .../org/restlet/service/StatusService.java | 11 +- .../org/restlet/data/LanguageTestCase.java | 12 +- .../HttpTransportProtocolsTestCase.java | 5 +- .../engine/header/HeaderUtilsTestCase.java | 23 ++-- 20 files changed, 229 insertions(+), 221 deletions(-) diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java index 9e398533df..c8c564493f 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3SigningTestCase.java @@ -26,7 +26,7 @@ * * @author Jean-Philippe Steinmetz */ -public class HttpAwsS3SigningTestCase { +class HttpAwsS3SigningTestCase { private static final String ACCESS_KEY = "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"; private Request getRequest; @@ -36,16 +36,16 @@ public class HttpAwsS3SigningTestCase { private Request uploadRequest; @BeforeEach - public void setUpEach() throws Exception { + void setUpEach() { getRequest = new Request(); - Series

headers = new Series
(Header.class); + Series
headers = new Series<>(Header.class); getRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers); headers.add(HeaderConstants.HEADER_DATE, "Tue, 27 Mar 2007 19:36:42 +0000"); getRequest.setMethod(Method.GET); getRequest.setResourceRef("http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); putRequest = new Request(); - headers = new Series
(Header.class); + headers = new Series<>(Header.class); putRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers); headers.add(HeaderConstants.HEADER_CONTENT_LENGTH, "94328"); headers.add(HeaderConstants.HEADER_CONTENT_TYPE, "image/jpeg"); @@ -54,7 +54,7 @@ public void setUpEach() throws Exception { putRequest.setResourceRef("http://johnsmith.s3.amazonaws.com/photos/puppy.jpg"); uploadRequest = new Request(); - headers = new Series
(Header.class); + headers = new Series<>(Header.class); uploadRequest.getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers); headers.add(HeaderConstants.HEADER_CONTENT_LENGTH, "5913339"); headers.add(HeaderConstants.HEADER_CONTENT_MD5, "4gJE4saaMU4BqNR0kLY+lw=="); @@ -70,14 +70,14 @@ public void setUpEach() throws Exception { } @AfterEach - public void tearDownEach() throws Exception { + void tearDownEach() { getRequest = null; putRequest = null; uploadRequest = null; } @Test - public void testGetCanonicalizedAmzHeaders() { + void testGetCanonicalizedAmzHeaders() { Series
headers = getRequest.getHeaders(); String expected = ""; String actual = AwsUtils.getCanonicalizedAmzHeaders(headers); @@ -85,22 +85,24 @@ public void testGetCanonicalizedAmzHeaders() { headers = uploadRequest.getHeaders(); expected = - "x-amz-acl:public-read\n" - + "x-amz-meta-checksumalgorithm:crc32\n" - + "x-amz-meta-filechecksum:0x02661779\n" - + "x-amz-meta-reviewedby:joe@johnsmith.net,jane@johnsmith.net\n"; + """ + x-amz-acl:public-read + x-amz-meta-checksumalgorithm:crc32 + x-amz-meta-filechecksum:0x02661779 + x-amz-meta-reviewedby:joe@johnsmith.net,jane@johnsmith.net + """; actual = AwsUtils.getCanonicalizedAmzHeaders(headers); assertEquals(expected, actual); } @Test - public void testGetCanonicalizedResourceName() { + void testGetCanonicalizedResourceName() { String result = AwsUtils.getCanonicalizedResourceName(getRequest.getResourceRef()); assertEquals("/johnsmith/photos/puppy.jpg", result); } @Test - public void testGetSignature() { + void testGetSignature() { String result = AwsUtils.getS3Signature(getRequest, ACCESS_KEY.toCharArray()); assertEquals("xXjDGYUmKxnwqr5KXNPGldn5LbA=", result); @@ -112,36 +114,39 @@ public void testGetSignature() { } @Test - public void testGetStringToSign() { + void testGetStringToSign() { String expected = - "GET\n" - + "\n" - + "\n" - + "Tue, 27 Mar 2007 19:36:42 +0000\n" - + "/johnsmith/photos/puppy.jpg"; + """ + GET + + + Tue, 27 Mar 2007 19:36:42 +0000 + /johnsmith/photos/puppy.jpg"""; String actual = AwsUtils.getS3StringToSign(getRequest); assertEquals(expected, actual); expected = - "PUT\n" - + "\n" - + "image/jpeg\n" - + "Tue, 27 Mar 2007 21:15:45 +0000\n" - + "/johnsmith/photos/puppy.jpg"; + """ + PUT + + image/jpeg + Tue, 27 Mar 2007 21:15:45 +0000 + /johnsmith/photos/puppy.jpg"""; actual = AwsUtils.getS3StringToSign(putRequest); assertEquals(expected, actual); expected = - "PUT\n" - + "4gJE4saaMU4BqNR0kLY+lw==\n" - + "application/x-download\n" - + "Tue, 27 Mar 2007 21:06:08 +0000\n" - + "x-amz-acl:public-read\n" - + "x-amz-meta-checksumalgorithm:crc32\n" - + "x-amz-meta-filechecksum:0x02661779\n" - + "x-amz-meta-reviewedby:" - + "joe@johnsmith.net,jane@johnsmith.net\n" - + "/static.johnsmith.net/db-backup.dat.gz"; + """ + PUT + 4gJE4saaMU4BqNR0kLY+lw== + application/x-download + Tue, 27 Mar 2007 21:06:08 +0000 + x-amz-acl:public-read + x-amz-meta-checksumalgorithm:crc32 + x-amz-meta-filechecksum:0x02661779 + x-amz-meta-reviewedby:\ + joe@johnsmith.net,jane@johnsmith.net + /static.johnsmith.net/db-backup.dat.gz"""; actual = AwsUtils.getS3StringToSign(uploadRequest); assertEquals(expected, actual); } diff --git a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java index 7cfe1bbdc7..06d662429c 100644 --- a/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java +++ b/org.restlet.ext.jaas/src/main/java/org/restlet/ext/jaas/ChallengeCallbackHandler.java @@ -8,9 +8,9 @@ */ package org.restlet.ext.jaas; -import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import org.restlet.Request; @@ -61,23 +61,19 @@ public Response getResponse() { /** * Handles a callback. The default implementation automatically sets the identifier on {@link - * javax.security.auth.callback.NameCallback} instances and the secret on {@link - * PasswordCallback}. + * NameCallback} instances and the secret on {@link PasswordCallback}. * * @param callback The callback to handle. * @throws UnsupportedCallbackException */ protected void handle(Callback callback) throws UnsupportedCallbackException { - if (callback instanceof javax.security.auth.callback.NameCallback nameCallback) { - if (getRequest().getChallengeResponse() != null) { - nameCallback.setName(getRequest().getChallengeResponse().getIdentifier()); - } - } else if (callback instanceof PasswordCallback passwordCallback) { - if (getRequest().getChallengeResponse() != null) { - passwordCallback.setPassword(getRequest().getChallengeResponse().getSecret()); - } - } else { - throw new UnsupportedCallbackException(callback, "Unrecognized Callback"); + switch (callback) { + case NameCallback nameCallback when getRequest().getChallengeResponse() != null -> + nameCallback.setName(getRequest().getChallengeResponse().getIdentifier()); + case PasswordCallback passwordCallback + when getRequest().getChallengeResponse() != null -> + passwordCallback.setPassword(getRequest().getChallengeResponse().getSecret()); + default -> throw new UnsupportedCallbackException(callback, "Unrecognized Callback"); } } @@ -87,7 +83,7 @@ protected void handle(Callback callback) throws UnsupportedCallbackException { * * @param callbacks The callbacks to handle. */ - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + public void handle(Callback[] callbacks) throws UnsupportedCallbackException { if (callbacks != null) { for (Callback callback : callbacks) { diff --git a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java index 43ade660ce..8aa58eb18e 100644 --- a/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java +++ b/org.restlet.ext.spring/src/main/java/org/restlet/ext/spring/SpringComponent.java @@ -78,16 +78,14 @@ public void setClient(Object clientInfo) { */ public synchronized void setClientsList(List clients) { for (final Object client : clients) { - if (client instanceof String string) { - getClients().add(Protocol.valueOf(string)); - } else if (client instanceof Protocol protocol) { - getClients().add(protocol); - } else if (client instanceof Client client1) { - getClients().add(client1); - } else { - getLogger() - .warning( - "Unknown object found in the clients list. Only instances of String, org.restlet.data.Protocol and org.restlet.Client are allowed."); + switch (client) { + case String string -> getClients().add(Protocol.valueOf(string)); + case Protocol protocol -> getClients().add(protocol); + case Client client1 -> getClients().add(client1); + case null, default -> + getLogger() + .warning( + "Unknown object found in the clients list. Only instances of String, org.restlet.data.Protocol and org.restlet.Client are allowed."); } } } @@ -108,7 +106,7 @@ public void setDefaultTarget(Restlet target) { * @param serverInfo The server info. */ public void setServer(Object serverInfo) { - final List servers = new ArrayList(); + final List servers = new ArrayList<>(); servers.add(serverInfo); setServersList(servers); } @@ -120,16 +118,14 @@ public void setServer(Object serverInfo) { */ public void setServersList(List serversInfo) { for (final Object serverInfo : serversInfo) { - if (serverInfo instanceof String string) { - getServers().add(Protocol.valueOf(string)); - } else if (serverInfo instanceof Protocol protocol) { - getServers().add(protocol); - } else if (serverInfo instanceof Server server) { - getServers().add(server); - } else { - getLogger() - .warning( - "Unknown object found in the servers list. Only instances of String, org.restlet.data.Protocol and org.restlet.Server are allowed."); + switch (serverInfo) { + case String string -> getServers().add(Protocol.valueOf(string)); + case Protocol protocol -> getServers().add(protocol); + case Server server -> getServers().add(server); + case null, default -> + getLogger() + .warning( + "Unknown object found in the servers list. Only instances of String, org.restlet.data.Protocol and org.restlet.Server are allowed."); } } } diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java index 32c2c6a9fd..7bb7eaf52f 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/TemplateRepresentation.java @@ -8,6 +8,8 @@ */ package org.restlet.ext.thymeleaf; +import static org.restlet.Context.getCurrent; + import java.io.IOException; import java.io.Writer; import java.util.Collections; @@ -319,14 +321,12 @@ public void write(Writer writer) throws IOException { engine.process(templateName, context, writer); } catch (Exception e) { - final org.restlet.Context context = org.restlet.Context.getCurrent(); + final var currentContext = getCurrent(); - if (context != null) { - context.getLogger().log(Level.WARNING, "Unable to process the template", e); + if (currentContext != null) { + currentContext.getLogger().log(Level.WARNING, "Unable to process the template", e); } - e.printStackTrace(); - throw new IOException("Template processing error. " + e.getMessage()); } } diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java index bf30f98992..13eba38005 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlWriter.java @@ -15,10 +15,11 @@ import java.io.Writer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; +import java.util.ArrayDeque; import java.util.Arrays; +import java.util.Deque; import java.util.Enumeration; import java.util.Map; -import java.util.Stack; import java.util.concurrent.ConcurrentHashMap; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -265,9 +266,9 @@ * </Person> * * + * @author David Megginson, Jerome Louvel (contact@restlet.com) * @see org.xml.sax.XMLFilter * @see org.xml.sax.ContentHandler - * @author David Megginson, Jerome Louvel (contact@restlet.com) */ public final class XmlWriter extends XMLFilterImpl { private static final Object SEEN_DATA = new Object(); @@ -287,7 +288,7 @@ public final class XmlWriter extends XMLFilterImpl { private volatile int elementLevel = 0; /** Constant representing empty attributes. */ - private final Attributes EMPTY_ATTS = new AttributesImpl(); + private final Attributes emptyAttributes = new AttributesImpl(); /** The forced declarations table. */ private volatile Map forcedDeclTable; @@ -308,7 +309,7 @@ public final class XmlWriter extends XMLFilterImpl { private volatile Object state = SEEN_NOTHING; - private volatile Stack stateStack = new Stack<>(); + private volatile Deque stateStack = new ArrayDeque<>(); /** * Create a new XML writer. @@ -397,8 +398,8 @@ public XmlWriter(XMLReader xmlreader, Writer writer) { * @param ch The array of characters to write. * @param start The starting position in the array. * @param len The number of characters to write. - * @exception org.xml.sax.SAXException If there is an error writing the characters, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the characters, or if a restlet + * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#characters */ private void characters(boolean dataFormat, char[] ch, int start, int len) throws SAXException { @@ -421,7 +422,7 @@ private void characters(boolean dataFormat, char[] ch, int start, int len) throw * then invokes {@link #characters(char[], int, int)}. * * @param data The character data. - * @exception org.xml.sax.SAXException If there is an error writing the string, or if a restlet + * @throws org.xml.sax.SAXException If there is an error writing the string, or if a restlet * further down the filter chain raises an exception. * @see #characters(char[], int, int) */ @@ -436,8 +437,8 @@ private void characters(boolean dataFormat, String data) throws SAXException { * @param ch The array of characters to write. * @param start The starting position in the array. * @param len The number of characters to write. - * @exception org.xml.sax.SAXException If there is an error writing the characters, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the characters, or if a restlet + * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#characters */ @Override @@ -452,7 +453,7 @@ public void characters(char[] ch, int start, int len) throws SAXException { * then invokes {@link #characters(char[], int, int)}. * * @param data The character data. - * @exception org.xml.sax.SAXException If there is an error writing the string, or if a restlet + * @throws org.xml.sax.SAXException If there is an error writing the string, or if a restlet * further down the filter chain raises an exception. * @see #characters(char[], int, int) */ @@ -472,14 +473,14 @@ public void characters(String data) throws SAXException { * * @param localName The element's local name. * @param content The character data content. - * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the empty tag, or if a restlet + * further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) * @see #characters(String) * @see #endElement(String, String, String) */ public void dataElement(String localName, String content) throws SAXException { - dataElement("", localName, "", this.EMPTY_ATTS, content); + dataElement("", localName, "", this.emptyAttributes, content); } /** @@ -495,14 +496,14 @@ public void dataElement(String localName, String content) throws SAXException { * @param uri The element's Namespace URI. * @param localName The element's local name. * @param content The character data content. - * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the empty tag, or if a restlet + * further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) * @see #characters(String) * @see #endElement(String, String, String) */ public void dataElement(String uri, String localName, String content) throws SAXException { - dataElement(uri, localName, "", this.EMPTY_ATTS, content); + dataElement(uri, localName, "", this.emptyAttributes, content); } /** @@ -519,8 +520,8 @@ public void dataElement(String uri, String localName, String content) throws SAX * @param qName The element's default qualified name. * @param atts The element's attributes. * @param content The character data content. - * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the empty tag, or if a restlet + * further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) * @see #characters(String) * @see #endElement(String, String, String) @@ -536,8 +537,8 @@ public void dataElement( /** * Print indentation for the current level. * - * @exception org.xml.sax.SAXException If there is an error writing the indentation characters, - * or if a filter further down the chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the indentation characters, or + * if a filter further down the chain raises an exception. */ private void doIndent() throws SAXException { if ((this.indentStep > 0) && (this.depth > 0)) { @@ -549,8 +550,7 @@ private void doIndent() throws SAXException { } /** - * Determine the prefix for an element or attribute name. TODO: this method probably needs some - * cleanup. + * Determine the prefix for an element or attribute name. * * @param uri The Namespace URI. * @param qName The qualified name (optional); this will be used to indicate the preferred @@ -622,12 +622,12 @@ private String doPrefix(String uri, String qName, boolean isElement) { * Attributes)} directly. * * @param localName The element's local name. - * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the empty tag, or if a restlet + * further down the filter chain raises an exception. * @see #emptyElement(String, String, String, Attributes) */ public void emptyElement(String localName) throws SAXException { - emptyElement("", localName, "", this.EMPTY_ATTS); + emptyElement("", localName, "", this.emptyAttributes); } /** @@ -638,12 +638,12 @@ public void emptyElement(String localName) throws SAXException { * * @param uri The element's Namespace URI. * @param localName The element's local name. - * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the empty tag, or if a restlet + * further down the filter chain raises an exception. * @see #emptyElement(String, String, String, Attributes) */ public void emptyElement(String uri, String localName) throws SAXException { - emptyElement(uri, localName, "", this.EMPTY_ATTS); + emptyElement(uri, localName, "", this.emptyAttributes); } /** @@ -658,8 +658,8 @@ public void emptyElement(String uri, String localName) throws SAXException { * available. This parameter is strictly advisory: the writer may or may not use the prefix * attached. * @param atts The element's attribute list. - * @exception org.xml.sax.SAXException If there is an error writing the empty tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the empty tag, or if a restlet + * further down the filter chain raises an exception. * @see #startElement * @see #endElement */ @@ -690,7 +690,7 @@ public void emptyElement(String uri, String localName, String qName, Attributes * Write a newline at the end of the document. Pass the event on down the filter chain for * further processing. * - * @exception org.xml.sax.SAXException If there is an error writing the newline, or if a restlet + * @throws org.xml.sax.SAXException If there is an error writing the newline, or if a restlet * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#endDocument */ @@ -712,7 +712,7 @@ public void endDocument() throws SAXException { * Namespace URI. It invokes {@link #endElement(String, String, String)} directly. * * @param localName The element's local name. - * @exception org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet + * @throws org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet * further down the filter chain raises an exception. * @see #endElement(String, String, String) */ @@ -728,7 +728,7 @@ public void endElement(String localName) throws SAXException { * * @param uri The element's Namespace URI. * @param localName The element's local name. - * @exception org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet + * @throws org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet * further down the filter chain raises an exception. * @see #endElement(String, String, String) */ @@ -744,7 +744,7 @@ public void endElement(String uri, String localName) throws SAXException { * @param qName The element's qualified (prefixed) name, or the empty string is none is * available. This method will use the qName as a template for generating a prefix if * necessary, but it is not guaranteed to use the same qName. - * @exception org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet + * @throws org.xml.sax.SAXException If there is an error writing the end tag, or if a restlet * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#endElement */ @@ -804,7 +804,7 @@ public void flush() throws IOException { * there and reduces the number of xmlns attributes in the document. * * @param uri The Namespace URI to declare. - * @see #forceNSDecl(java.lang.String,java.lang.String) + * @see #forceNSDecl(java.lang.String, java.lang.String) * @see #setPrefix */ public void forceNSDecl(String uri) { @@ -879,8 +879,8 @@ public Writer getWriter() { * @param ch The array of characters to write. * @param start The starting position in the array. * @param length The number of characters to write. - * @exception org.xml.sax.SAXException If there is an error writing the whitespace, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the whitespace, or if a restlet + * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#ignorableWhitespace */ @Override @@ -914,8 +914,8 @@ public boolean isDataFormat() { * * @param target The PI target. * @param data The PI data. - * @exception org.xml.sax.SAXException If there is an error writing the PI, or if a restlet - * further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the PI, or if a restlet further + * down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#processingInstruction */ @Override @@ -950,7 +950,7 @@ public void reset() { if (isDataFormat()) { this.depth = 0; this.state = SEEN_NOTHING; - this.stateStack = new Stack<>(); + this.stateStack = new ArrayDeque<>(); } this.elementLevel = 0; @@ -999,7 +999,7 @@ public void setOutput(Writer writer) { * @param prefix The preferred prefix, or "" to select the default Namespace. * @see #getPrefix * @see #forceNSDecl(java.lang.String) - * @see #forceNSDecl(java.lang.String,java.lang.String) + * @see #forceNSDecl(java.lang.String, java.lang.String) */ public void setPrefix(String uri, String prefix) { this.prefixTable.put(uri, prefix); @@ -1009,7 +1009,7 @@ public void setPrefix(String uri, String prefix) { * Write the XML declaration at the beginning of the document. Pass the event on down the filter * chain for further processing. * - * @exception org.xml.sax.SAXException If there is an error writing the XML declaration, or if a + * @throws org.xml.sax.SAXException If there is an error writing the XML declaration, or if a * restlet further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#startDocument */ @@ -1028,12 +1028,12 @@ public void startDocument() throws SAXException { * String, Attributes)} directly. * * @param localName The element's local name. - * @exception org.xml.sax.SAXException If there is an error writing the start tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the start tag, or if a restlet + * further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) */ public void startElement(String localName) throws SAXException { - startElement("", localName, "", this.EMPTY_ATTS); + startElement("", localName, "", this.emptyAttributes); } /** @@ -1045,12 +1045,12 @@ public void startElement(String localName) throws SAXException { * * @param uri The element's Namespace URI. * @param localName The element's local name. - * @exception org.xml.sax.SAXException If there is an error writing the start tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the start tag, or if a restlet + * further down the filter chain raises an exception. * @see #startElement(String, String, String, Attributes) */ public void startElement(String uri, String localName) throws SAXException { - startElement(uri, localName, "", this.EMPTY_ATTS); + startElement(uri, localName, "", this.emptyAttributes); } /** @@ -1062,8 +1062,8 @@ public void startElement(String uri, String localName) throws SAXException { * available. This method will use the qName as a template for generating a prefix if * necessary, but it is not guaranteed to use the same qName. * @param atts The element's attribute list (must not be null). - * @exception org.xml.sax.SAXException If there is an error writing the start tag, or if a - * restlet further down the filter chain raises an exception. + * @throws org.xml.sax.SAXException If there is an error writing the start tag, or if a restlet + * further down the filter chain raises an exception. * @see org.xml.sax.ContentHandler#startElement */ @Override @@ -1099,8 +1099,8 @@ public void startElement(String uri, String localName, String qName, Attributes * Write a raw character. * * @param c The character to write. - * @exception org.xml.sax.SAXException If there is an error writing the character, this method - * will throw an IOException wrapped in a SAXException. + * @throws org.xml.sax.SAXException If there is an error writing the character, this method will + * throw an IOException wrapped in a SAXException. */ private void write(char c) throws SAXException { try { @@ -1114,7 +1114,7 @@ private void write(char c) throws SAXException { * Write a raw string. * * @param s - * @exception org.xml.sax.SAXException If there is an error writing the string, this method will + * @throws org.xml.sax.SAXException If there is an error writing the string, this method will * throw an IOException wrapped in a SAXException */ private void write(String s) throws SAXException { @@ -1129,7 +1129,7 @@ private void write(String s) throws SAXException { * Write out an attribute list, escaping values. The names will have prefixes added to them. * * @param attributes The attribute list to write. - * @exception org.xml.SAXException If there is an error writing the attribute list, this method + * @throws org.xml.sax.SAXException If there is an error writing the attribute list, this method * will throw an IOException wrapped in a SAXException. */ private void writeAttributes(Attributes attributes) throws SAXException { @@ -1164,8 +1164,8 @@ private void writeAttributes(Attributes attributes) throws SAXException { * @param start The starting position. * @param length The number of characters to use. * @param isAttVal true if this is an attribute value literal. - * @exception org.xml.SAXException If there is an error writing the characters, this method will - * throw an IOException wrapped in a SAXException. + * @throws org.xml.sax.SAXException If there is an error writing the characters, this method + * will throw an IOException wrapped in a SAXException. */ private void writeEsc(char[] ch, int start, int length, boolean isAttVal) throws SAXException { for (int i = start; i < start + length; i++) { @@ -1205,7 +1205,7 @@ private void writeEsc(char[] ch, int start, int length, boolean isAttVal) throws * @param localName The local name. * @param qName The prefixed name, if available, or the empty string. * @param isElement true if this is an element name, false if it is an attribute name. - * @exception org.xml.sax.SAXException This method will throw an IOException wrapped in a + * @throws org.xml.sax.SAXException This method will throw an IOException wrapped in a * SAXException if there is an error writing the name. */ private void writeName(String uri, String localName, String qName, boolean isElement) @@ -1222,7 +1222,7 @@ private void writeName(String uri, String localName, String qName, boolean isEle /** * Write out the list of Namespace declarations. * - * @exception org.xml.sax.SAXException This method will throw an IOException wrapped in a + * @throws org.xml.sax.SAXException This method will throw an IOException wrapped in a * SAXException if there is an error writing the Namespace declarations. */ private void writeNSDecls() throws SAXException { diff --git a/org.restlet/src/main/java/org/restlet/Application.java b/org.restlet/src/main/java/org/restlet/Application.java index bf4b119219..611a01deef 100644 --- a/org.restlet/src/main/java/org/restlet/Application.java +++ b/org.restlet/src/main/java/org/restlet/Application.java @@ -532,9 +532,10 @@ public synchronized void start() throws Exception { getLogger() .log( Level.INFO, - "Starting " + getClass().getName() + " application in debug mode"); + "Starting {0} application in debug mode", + getClass().getName()); } else { - getLogger().log(Level.INFO, "Starting " + getClass().getName() + " application"); + getLogger().log(Level.INFO, "Starting {0} application", getClass().getName()); } if (getHelper() != null) { diff --git a/org.restlet/src/main/java/org/restlet/Request.java b/org.restlet/src/main/java/org/restlet/Request.java index 488f724934..f30028640f 100644 --- a/org.restlet/src/main/java/org/restlet/Request.java +++ b/org.restlet/src/main/java/org/restlet/Request.java @@ -615,7 +615,7 @@ public boolean isEntityAvailable() { * @return True if an associated response is expected. */ public boolean isExpectingResponse() { - return (getMethod() == null) ? false : getMethod().isReplying(); + return getMethod() != null && getMethod().isReplying(); } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java index 1d185ce5a0..46b35cb642 100644 --- a/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/CompositeHelper.java @@ -187,9 +187,8 @@ public void handle(Request request, Response response) { .getLogger() .log( Level.SEVERE, - "The " - + getHelped().getClass().getName() - + " class has no Restlet defined to process calls. Maybe it wasn't properly started."); + "The {0} class has no Restlet defined to process calls. Maybe it wasn''t properly started.", + getHelped().getClass().getName()); } } } diff --git a/org.restlet/src/main/java/org/restlet/engine/Engine.java b/org.restlet/src/main/java/org/restlet/engine/Engine.java index 2874e73da2..384527271f 100644 --- a/org.restlet/src/main/java/org/restlet/engine/Engine.java +++ b/org.restlet/src/main/java/org/restlet/engine/Engine.java @@ -8,6 +8,8 @@ */ package org.restlet.engine; +import static java.net.URL.setURLStreamHandlerFactory; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -185,8 +187,8 @@ public static void configureLog() { try { LogManager.getLogManager() .readConfiguration(new ByteArrayInputStream(sb.toString().getBytes())); - } catch (Throwable t) { - t.printStackTrace(); + } catch (Exception exception) { + exception.printStackTrace(); } } @@ -511,7 +513,7 @@ public org.restlet.engine.connector.ConnectorHelper createHelper( sb.append(" Then, register this connector helper manually."); } - Context.getCurrentLogger().log(Level.WARNING, sb.toString()); + Context.getCurrentLogger().log(Level.WARNING, sb::toString); } } @@ -570,7 +572,7 @@ public ConnectorHelper createHelper(Server server, String helperClass) { sb.append(" Then, register this connector helper manually."); } - Context.getCurrentLogger().log(Level.WARNING, sb.toString()); + Context.getCurrentLogger().log(Level.WARNING, sb::toString); } } @@ -800,7 +802,10 @@ public void registerHelper( } } catch (Exception exception) { Context.getCurrentLogger() - .log(Level.INFO, "Unable to register the helper " + provider, exception); + .log( + Level.INFO, + exception, + () -> "Unable to register the helper " + provider); } } } @@ -831,7 +836,7 @@ public void registerHelpers( } } catch (IOException e) { Context.getCurrentLogger() - .log(Level.SEVERE, "Unable to read the provider descriptor: " + configUrl); + .log(Level.SEVERE, "Unable to read the provider descriptor: {0}", configUrl); } } @@ -845,12 +850,13 @@ public void registerHelpers( */ public void registerHelpers(String descriptorPath, List helpers, Class constructorClass) throws IOException { - ClassLoader classLoader = getClassLoader(); - Enumeration configUrls = classLoader.getResources(descriptorPath); + ClassLoader currentClassLoader = getClassLoader(); + Enumeration configUrls = currentClassLoader.getResources(descriptorPath); if (configUrls != null) { while (configUrls.hasMoreElements()) { - registerHelpers(classLoader, configUrls.nextElement(), helpers, constructorClass); + registerHelpers( + currentClassLoader, configUrls.nextElement(), helpers, constructorClass); } } } @@ -865,10 +871,9 @@ public void registerHelpers(String descriptorPath, List helpers, Class con */ public void registerUrlFactory() { // Set up an java.net.URLStreamHandlerFactory for proper creation of java.net.URL instances - java.net.URL.setURLStreamHandlerFactory( - new java.net.URLStreamHandlerFactory() { - public java.net.URLStreamHandler createURLStreamHandler(String protocol) { - return new java.net.URLStreamHandler() { + setURLStreamHandlerFactory( + protocol -> + new java.net.URLStreamHandler() { @Override protected java.net.URLConnection openConnection(java.net.URL url) @@ -882,7 +887,7 @@ public void connect() { @Override public InputStream getInputStream() throws IOException { - InputStream result1 = null; + InputStream result = null; // Retrieve the current context final Context context = Context.getCurrent(); @@ -896,17 +901,15 @@ public InputStream getInputStream() throws IOException { this.url.toString())); if (response.getStatus().isSuccess()) { - result1 = response.getEntity().getStream(); + result = response.getEntity().getStream(); } } - return result1; + return result; } }; } - }; - } - }); + }); } /** diff --git a/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java b/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java index 49bc6d605a..738a7a4729 100644 --- a/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java +++ b/org.restlet/src/main/java/org/restlet/engine/log/LoggingThreadFactory.java @@ -25,8 +25,12 @@ private class LoggingExceptionHandler implements Thread.UncaughtExceptionHandler public void uncaughtException(Thread t, Throwable ex) { logger.log( Level.SEVERE, - "Thread: " + t.getName() + " terminated with exception: " + ex.getMessage(), - ex); + ex, + () -> + "Thread: " + + t.getName() + + " terminated with exception: " + + ex.getMessage()); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java index 6e39327431..bc80b6f63a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java +++ b/org.restlet/src/main/java/org/restlet/engine/resource/AnnotationInfo.java @@ -194,12 +194,12 @@ protected Class getJavaActualType(Class initialType, Type genericType) { String genericTypeName = genericTypeVariable.getName(); result = getJavaActualType(getJavaClass(), genericTypeName); } - } catch (Throwable t) { + } catch (Exception exception) { Context.getCurrentLogger() .log( Level.WARNING, - "Cannot get actual type of generic type: " + genericType, - t); + exception, + () -> "Cannot get actual type of generic type: " + genericType); } return result; diff --git a/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java b/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java index 8316c366ec..9e689f6214 100644 --- a/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java +++ b/org.restlet/src/main/java/org/restlet/engine/util/StringUtils.java @@ -507,7 +507,7 @@ public static String htmlUnescape(String str) { int len = str.length(); StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; i++) { - char c = str.charAt(i); + final char c = str.charAt(i); if (c == '&') { int nextIndex = i + 1; int semicolonIndex = -1; diff --git a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java index fa753904f3..a1528c2332 100644 --- a/org.restlet/src/main/java/org/restlet/resource/ServerResource.java +++ b/org.restlet/src/main/java/org/restlet/resource/ServerResource.java @@ -212,7 +212,7 @@ protected Representation describeVariants() { * @param throwable The caught error or exception. */ protected void doCatch(Throwable throwable) { - Level level = Level.INFO; + final Level level; Status status = getStatusService().toStatus(throwable, this); if (status.isServerError()) { @@ -221,6 +221,8 @@ protected void doCatch(Throwable throwable) { level = Level.INFO; } else if (status.isClientError()) { level = Level.FINE; + } else { + level = Level.INFO; } getLogger().log(level, "Exception or error caught in server resource", throwable); @@ -428,7 +430,7 @@ protected Representation doHandle(MethodAnnotationInfo annotationInfo, Variant v try { if (parameterTypes.length > 0) { - List parameters = new ArrayList(); + List parameters = new ArrayList<>(); Object parameter = null; for (Class parameterType : parameterTypes) { @@ -467,8 +469,8 @@ && getRequestEntity().getSize() != 0) { } catch (IllegalArgumentException | IllegalAccessException | IOException e) { throw new ResourceException(e); } catch (InvocationTargetException e) { - if (e.getTargetException() instanceof ResourceException) { - throw (ResourceException) e.getTargetException(); + if (e.getTargetException() instanceof ResourceException resourceException) { + throw resourceException; } throw new ResourceException(e.getTargetException()); @@ -532,8 +534,8 @@ protected Representation doHandle(Variant variant) throws ResourceException { result = patch(getRequestEntity(), variant); } else if (isExisting()) { if (method.equals(Method.GET)) { - if (variant instanceof Representation) { - result = (Representation) variant; + if (variant instanceof Representation representation) { + result = representation; } else { result = get(variant); } @@ -542,19 +544,19 @@ protected Representation doHandle(Variant variant) throws ResourceException { } else if (method.equals(Method.DELETE)) { result = delete(variant); } else if (method.equals(Method.HEAD)) { - if (variant instanceof Representation) { - result = (Representation) variant; + if (variant instanceof Representation representation) { + result = representation; } else { result = head(variant); } } else if (method.equals(Method.OPTIONS)) { - if (variant instanceof Representation) { - result = (Representation) variant; + if (variant instanceof Representation representation) { + result = representation; } else { result = options(variant); } - } else if (variant instanceof VariantInfo) { - result = doHandle(((VariantInfo) variant).getAnnotationInfo(), variant); + } else if (variant instanceof VariantInfo variantInfo) { + result = doHandle(variantInfo.getAnnotationInfo(), variant); } else { doError(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); } @@ -1631,10 +1633,7 @@ public void updateAllowedMethods() { if (annotations != null) { for (AnnotationInfo annotationInfo : annotations) { if (annotationInfo instanceof final MethodAnnotationInfo methodAnnotationInfo) { - - if (!getAllowedMethods().contains(methodAnnotationInfo.getRestletMethod())) { - getAllowedMethods().add(methodAnnotationInfo.getRestletMethod()); - } + getAllowedMethods().add(methodAnnotationInfo.getRestletMethod()); } } } diff --git a/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java b/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java index 8e0b97b992..00504e8ee4 100644 --- a/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java +++ b/org.restlet/src/main/java/org/restlet/routing/VirtualHost.java @@ -49,7 +49,7 @@ * @author Jerome Louvel */ public class VirtualHost extends Router { - private static final ThreadLocal CURRENT = new ThreadLocal(); + private static final ThreadLocal CURRENT = new ThreadLocal<>(); /** * Returns the virtual host code associated with the current thread. @@ -75,6 +75,7 @@ public static String getIpAddress(String domain) { try { result = InetAddress.getByName(domain).getHostAddress(); } catch (UnknownHostException ignored) { + // Ignored exception, the result is null } return result; @@ -91,6 +92,7 @@ public static String getLocalHostAddress() { try { result = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException ignored) { + // Ignored exception, the result is null } return result; @@ -107,6 +109,7 @@ public static String getLocalHostName() { try { result = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException ignored) { + // Ignored exception, the result is null } return result; diff --git a/org.restlet/src/main/java/org/restlet/security/Authenticator.java b/org.restlet/src/main/java/org/restlet/security/Authenticator.java index 669df4fc75..467fee1444 100644 --- a/org.restlet/src/main/java/org/restlet/security/Authenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/Authenticator.java @@ -114,13 +114,13 @@ protected int authenticated(Request request, Response response) { if (loggable && request.getChallengeResponse() != null) { getLogger() - .log( - Level.FINE, - "The authentication succeeded for the identifer \"" - + request.getChallengeResponse().getIdentifier() - + "\" using the " - + request.getChallengeResponse().getScheme() - + " scheme."); + .fine( + () -> + "The authentication succeeded for the identifier \"" + + request.getChallengeResponse().getIdentifier() + + "\" using the " + + request.getChallengeResponse().getScheme() + + " scheme."); } // Update the client info accordingly @@ -244,13 +244,13 @@ protected int unauthenticated(Request request, Response response) { if (request.getChallengeResponse() != null && loggable) { getLogger() - .log( - Level.FINE, - "The authentication failed for the identifier \"" - + request.getChallengeResponse().getIdentifier() - + "\" using the " - + request.getChallengeResponse().getScheme() - + " scheme."); + .fine( + () -> + "The authentication failed for the identifier \"" + + request.getChallengeResponse().getIdentifier() + + "\" using the " + + request.getChallengeResponse().getScheme() + + " scheme."); } // Update the client info accordingly diff --git a/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java b/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java index 395ef36348..769a513aac 100644 --- a/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java +++ b/org.restlet/src/main/java/org/restlet/security/ChallengeAuthenticator.java @@ -186,6 +186,7 @@ protected boolean authenticate(Request request, Response response) { } } break; + default: } } else { getLogger().warning("Authentication failed. No verifier provided."); @@ -239,8 +240,8 @@ public void forbid(Response response) { getLogger() .log( Level.FINE, - "Authentication or authorization failed for this URI: " - + response.getRequest().getResourceRef()); + "Authentication or authorization failed for this URI: {0}", + response.getRequest().getResourceRef()); } response.setStatus(Status.CLIENT_ERROR_FORBIDDEN); diff --git a/org.restlet/src/main/java/org/restlet/service/StatusService.java b/org.restlet/src/main/java/org/restlet/service/StatusService.java index 62a458b782..12923d714c 100644 --- a/org.restlet/src/main/java/org/restlet/service/StatusService.java +++ b/org.restlet/src/main/java/org/restlet/service/StatusService.java @@ -286,9 +286,10 @@ public Representation toRepresentation(Status status, Request request, Response Context.getCurrentLogger() .log( Level.WARNING, - "Could not serialize throwable class " - + ((cause == null) ? null : cause.getClass()), - e); + e, + () -> + "Could not serialize throwable class " + + ((cause == null) ? null : cause.getClass())); } } @@ -327,8 +328,8 @@ public Status toStatus(Throwable throwable, Request request, Response response) Throwable t = throwable; // If throwable is a ResourceException, use its status and the cause. - if (throwable instanceof ResourceException) { - defaultStatus = ((ResourceException) throwable).getStatus(); + if (throwable instanceof ResourceException resourceException) { + defaultStatus = resourceException.getStatus(); if (throwable.getCause() != null && throwable.getCause() != throwable) { t = throwable.getCause(); diff --git a/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java b/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java index f4390e4711..058e071ba5 100644 --- a/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/LanguageTestCase.java @@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.List; import org.junit.jupiter.api.Test; /** @@ -18,19 +19,18 @@ * * @author Jerome Louvel */ -public class LanguageTestCase { +class LanguageTestCase { /** Testing {@link Language#valueOf(String)} */ @Test - public void testValueOf() { + void testValueOf() { assertSame(Language.FRENCH_FRANCE, Language.valueOf("fr-fr")); assertSame(Language.ALL, Language.valueOf("*")); } @Test - public void testUnmodifiable() { - assertThrows( - UnsupportedOperationException.class, - () -> Language.FRENCH_FRANCE.getSubTags().add("foo")); + void testUnmodifiable() { + final List subTags = Language.FRENCH_FRANCE.getSubTags(); + assertThrows(UnsupportedOperationException.class, () -> subTags.add("foo")); } } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java index fdd4276792..86ce633871 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java @@ -244,10 +244,10 @@ public void clientDoesNotComplyWithServer( final Server server = newServer(httpTransportProtocol); server.start(); + final int actualPort = server.getActualPort(); RuntimeException runtimeException = assertThrows( - RuntimeException.class, - () -> testHttpClient.sendRequest(server.getActualPort())); + RuntimeException.class, () -> testHttpClient.sendRequest(actualPort)); assertEquals("Error while sending request", runtimeException.getMessage()); } @@ -375,7 +375,6 @@ public String sendRequest(int port) { protected Context newClientContext() { Context context = new Context(); context.getParameters().add("httpClientTransportMode", httpClientTransportMode); - // context.getParameters().add("http3PemWorkDir", ); if (Protocol.HTTPS.equals(protocol)) { context.getParameters().add("truststorePath", testKeystoreFile.getPath()); diff --git a/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java index d557a7fb00..3d37da7596 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/HeaderUtilsTestCase.java @@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.restlet.data.Digest.ALGORITHM_MD5; import java.util.ArrayList; import java.util.Base64; @@ -22,10 +23,10 @@ import org.restlet.representation.Representation; import org.restlet.util.Series; -public class HeaderUtilsTestCase { +class HeaderUtilsTestCase { @Test - public void whenHeaderRetryAfterIsDecimalThenParsingIsStillFine() { + void whenHeaderRetryAfterIsDecimalThenParsingIsStillFine() { // Given a retry_after header that contains a decimal value Header header = new Header(HeaderConstants.HEADER_RETRY_AFTER, "2.1"); @@ -41,7 +42,7 @@ public void whenHeaderRetryAfterIsDecimalThenParsingIsStillFine() { } @Test - public void whenHeaderRetryAfterIsAlphabeticalThenParsingFailsSilently() { + void whenHeaderRetryAfterIsAlphabeticalThenParsingFailsSilently() { // Given a retry_after header that contains an alphabetical value Header header = new Header(HeaderConstants.HEADER_RETRY_AFTER, "2.1a"); @@ -57,7 +58,7 @@ public void whenHeaderRetryAfterIsAlphabeticalThenParsingFailsSilently() { } @Test - public void testExtracting() { + void testExtracting() { ArrayList
headers = new ArrayList<>(); String md5hash = "aaaaaaaaaaaaaaaa"; // encodes to "YWFhYWFhYWFhYWFhYWFhYQ==", the "==" at the end is padding @@ -68,16 +69,16 @@ public void testExtracting() { headers.add(header); // extract Content-MD5 header with padded Base64 encoding, make sure it - // decodes to original hash + // decodes to the original hash Representation rep = HeaderUtils.extractEntityHeaders(headers, null); - assertEquals(rep.getDigest().getAlgorithm(), org.restlet.data.Digest.ALGORITHM_MD5); - assertEquals(new String(rep.getDigest().getValue()), md5hash); + assertEquals(ALGORITHM_MD5, rep.getDigest().getAlgorithm()); + assertEquals(md5hash, new String(rep.getDigest().getValue())); - // extract header with UNpadded encoding, make sure it also decodes to - // original hash + // extract the header with UNpadded encoding, make sure it also decodes to + // the original hash header.setValue(encodedNoPadding); rep = HeaderUtils.extractEntityHeaders(headers, null); - assertEquals(rep.getDigest().getAlgorithm(), org.restlet.data.Digest.ALGORITHM_MD5); - assertEquals(new String(rep.getDigest().getValue()), md5hash); + assertEquals(ALGORITHM_MD5, rep.getDigest().getAlgorithm()); + assertEquals(md5hash, new String(rep.getDigest().getValue())); } } From daa434667df006fbcf21e0643b9b2806779c7901 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Sun, 5 Apr 2026 23:02:44 +0200 Subject: [PATCH 25/30] Take into account Sonar comments --- .../internal/HttpAzureSharedKeyHelper.java | 134 +++++++++--------- .../MultiPartRepresentation.java | 19 +-- 2 files changed, 78 insertions(+), 75 deletions(-) diff --git a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java index 777fbf7858..a6bf2d1316 100644 --- a/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java +++ b/org.restlet.ext.crypto/src/main/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelper.java @@ -10,6 +10,7 @@ import java.util.Base64; import java.util.Date; +import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.restlet.Request; @@ -56,17 +57,15 @@ private static String getCanonicalizedAzureHeaders(Series
requestHeaders headerName = header.getName().toLowerCase(); if (headerName.startsWith("x-ms-")) { - if (!azureHeaders.containsKey(headerName)) { - azureHeaders.put(headerName, requestHeaders.getValues(headerName)); - } + azureHeaders.computeIfAbsent(headerName, requestHeaders::getValues); } } // Concatenate all Azure headers StringBuilder sb = new StringBuilder(); - for (String key : azureHeaders.keySet()) { - sb.append(key).append(':').append(azureHeaders.get(key)).append("\n"); + for (Map.Entry entry : azureHeaders.entrySet()) { + sb.append(entry.getKey()).append(':').append(entry.getValue()).append("\n"); } return sb.toString(); @@ -101,75 +100,20 @@ public void formatResponse( Request request, Series
httpHeaders) { - // Set up the method name - final String methodName = request.getMethod().getName(); - - // Set up the Date header - String date = ""; - - if (httpHeaders.getFirstValue("x-ms-date", true) == null) { - // X-ms-Date header didn't override the standard Date header - date = httpHeaders.getFirstValue(HeaderConstants.HEADER_DATE, true); - if (date == null) { - // Add a fresh Date header - date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.getFirst()); - httpHeaders.add(HeaderConstants.HEADER_DATE, date); - } - } - // Set up the ContentType header - String contentMd5 = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_MD5, true); - if (contentMd5 == null) { - contentMd5 = ""; - } - - // Set up the ContentType header - String contentType = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE, true); - if (contentType == null) { - boolean applyPatch = false; - - // This patch seems to apply to Sun JVM only. - final String jvmVendor = System.getProperty("java.vm.vendor"); - if ((jvmVendor != null) && (jvmVendor.toLowerCase()).startsWith("sun")) { - final int majorVersionNumber = SystemUtils.getJavaMajorVersion(); - final int minorVersionNumber = SystemUtils.getJavaMinorVersion(); - - if (majorVersionNumber == 1) { - if (minorVersionNumber < 5) { - applyPatch = true; - } else if (minorVersionNumber == 5) { - // Sun fixed the bug in update 10 - applyPatch = (SystemUtils.getJavaUpdateVersion() < 10); - } - } - } - - if (applyPatch && !request.getMethod().equals(Method.PUT)) { - contentType = "application/x-www-form-urlencoded"; - } else { - contentType = ""; - } - } - - // Set up the canonicalized AzureHeaders - final String canonicalizedAzureHeaders = getCanonicalizedAzureHeaders(httpHeaders); - - // Set up the canonicalized path - final String canonicalizedResource = getCanonicalizedResourceName(request.getResourceRef()); - // Set up the message part final String rest = - methodName + request.getMethod().getName() + '\n' - + contentMd5 + + getContentMd5(httpHeaders) + '\n' - + contentType + + getContentTypeHeader(request, httpHeaders) + '\n' - + date + + getDateHeader(httpHeaders) + '\n' - + canonicalizedAzureHeaders + + getCanonicalizedAzureHeaders(httpHeaders) + '/' + challenge.getIdentifier() - + canonicalizedResource; + + getCanonicalizedResourceName(request.getResourceRef()); // Append the SharedKey credentials cw.append(challenge.getIdentifier()) @@ -184,4 +128,62 @@ public void formatResponse( IoUtils.toByteArray( challenge.getSecret()))))); } + + private static String getContentMd5(final Series
httpHeaders) { + String contentMd5 = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_MD5, true); + if (contentMd5 == null) { + contentMd5 = ""; + } + return contentMd5; + } + + private static String getContentTypeHeader( + final Request request, final Series
httpHeaders) { + String contentType = httpHeaders.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE, true); + + if (contentType != null) { + return contentType; + } + + boolean applyPatch = false; + + // This patch seems to apply to Sun JVM only. + final String jvmVendor = System.getProperty("java.vm.vendor"); + if ((jvmVendor != null) && (jvmVendor.toLowerCase()).startsWith("sun")) { + final int majorVersionNumber = SystemUtils.getJavaMajorVersion(); + final int minorVersionNumber = SystemUtils.getJavaMinorVersion(); + + if (majorVersionNumber == 1) { + if (minorVersionNumber < 5) { + applyPatch = true; + } else if (minorVersionNumber == 5) { + // Sun fixed the bug in update 10 + applyPatch = (SystemUtils.getJavaUpdateVersion() < 10); + } + } + } + + if (applyPatch && !request.getMethod().equals(Method.PUT)) { + contentType = "application/x-www-form-urlencoded"; + } else { + contentType = ""; + } + + return contentType; + } + + private static String getDateHeader(final Series
httpHeaders) { + String date = ""; + + if (httpHeaders.getFirstValue("x-ms-date", true) == null) { + // X-ms-Date header didn't override the standard Date header + date = httpHeaders.getFirstValue(HeaderConstants.HEADER_DATE, true); + if (date == null) { + // Add a fresh Date header + date = DateUtils.format(new Date(), DateUtils.FORMAT_RFC_1123.getFirst()); + httpHeaders.add(HeaderConstants.HEADER_DATE, date); + } + } + return date; + } } diff --git a/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java b/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java index 42317bae4b..782dd2462e 100644 --- a/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java +++ b/org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java @@ -33,6 +33,8 @@ */ public class MultiPartRepresentation extends InputRepresentation { + private static final String PARAMETER_BOUNDARY = "boundary"; + /** * Creates a #{@link Part} object based on a {@link Representation} plus metadata. * @@ -61,7 +63,7 @@ public static String getBoundary(MediaType mediaType) { final String result; if (mediaType != null) { - result = mediaType.getParameters().getFirstValue("boundary"); + result = mediaType.getParameters().getFirstValue(PARAMETER_BOUNDARY); } else { result = null; } @@ -81,10 +83,10 @@ public static MediaType setBoundary(MediaType mediaType, String boundary) { MediaType result = null; if (mediaType != null) { - if (mediaType.getParameters().getFirst("boundary") != null) { - result = new MediaType(mediaType.getParent(), "boundary", boundary); + if (mediaType.getParameters().getFirst(PARAMETER_BOUNDARY) != null) { + result = new MediaType(mediaType.getParent(), PARAMETER_BOUNDARY, boundary); } else { - result = new MediaType(mediaType, "boundary", boundary); + result = new MediaType(mediaType, PARAMETER_BOUNDARY, boundary); } } @@ -165,15 +167,13 @@ public MultiPartRepresentation(Representation multiPartEntity, Path storageLocat * MediaType#MULTIPART_FORM_DATA}, with a "boundary" parameter. * @param multiPartEntity The multipart entity to parse. * @param config The multipart configuration. - * @throws IOException */ public MultiPartRepresentation( - MediaType mediaType, InputStream multiPartEntity, MultiPartConfig config) - throws IOException { + MediaType mediaType, InputStream multiPartEntity, MultiPartConfig config) { super(null, mediaType); if (MediaType.MULTIPART_FORM_DATA.equals(getMediaType(), true)) { - this.boundary = getMediaType().getParameters().getFirstValue("boundary"); + this.boundary = getMediaType().getParameters().getFirstValue(PARAMETER_BOUNDARY); if (this.boundary != null) { if (multiPartEntity != null) { @@ -296,7 +296,8 @@ public void setBoundary(String boundary) { this.boundary = boundary; if (getMediaType() == null) { - setMediaType(new MediaType(MediaType.MULTIPART_FORM_DATA, "boundary", boundary)); + setMediaType( + new MediaType(MediaType.MULTIPART_FORM_DATA, PARAMETER_BOUNDARY, boundary)); } else { setMediaType(setBoundary(getMediaType(), boundary)); } From af72a42cd210aeeced93d56b9723de14fa6bc419 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Mon, 6 Apr 2026 10:51:30 +0200 Subject: [PATCH 26/30] Take into account Sonar comments --- .../ext/crypto/DigestVerifierTestCase.java | 8 ++-- .../ext/crypto/HttpDigestTestCase.java | 4 +- .../internal/HttpAwsS3HelperTestCase.java | 4 +- .../internal/HttpAwsS3HostNameTestCase.java | 8 ++-- .../internal/HttpAwsS3VerifierTestCase.java | 4 +- .../internal/HttpDigestHelperTestCase.java | 4 +- .../ext/freemarker/FreeMarkerTestCase.java | 4 +- .../freemarker/TemplateFilterTestCase.java | 6 +-- .../restlet/ext/jackson/JacksonTestCase.java | 16 +++---- .../restlet/ext/json/JsonpFilterTestCase.java | 11 +++-- .../ext/spring/SpringBeanFinderTestCase.java | 22 +++++----- .../ext/spring/SpringBeanRouterTestCase.java | 26 +++++------ .../restlet/ext/spring/SpringTestCase.java | 6 +-- .../ext/thymeleaf/ThymeleafTestCase.java | 4 +- .../ext/velocity/TemplateFilterTestCase.java | 6 +-- .../ext/velocity/VelocityTestCase.java | 6 +-- .../ext/xml/ResolvingTransformerTestCase.java | 6 +-- .../xml/TransformRepresentationTestCase.java | 6 +-- .../restlet/ext/xml/TransformerTestCase.java | 6 +-- .../restlet/ApplicationContextTestCase.java | 4 +- .../java/org/restlet/Bug1145TestCase.java | 4 +- .../test/java/org/restlet/CallTestCase.java | 24 +++++----- .../java/org/restlet/RestartTestCase.java | 2 +- .../data/AuthenticationInfoTestCase.java | 18 ++++---- .../org/restlet/data/ClientInfoTestCase.java | 14 +++--- .../java/org/restlet/data/CookieTestCase.java | 6 +-- .../org/restlet/data/FileClientTestCase.java | 4 +- .../restlet/data/FileReferenceTestCase.java | 4 +- .../java/org/restlet/data/FormTestCase.java | 6 +-- .../org/restlet/data/MediaTypeTestCase.java | 20 ++++----- .../java/org/restlet/data/MethodTestCase.java | 4 +- .../java/org/restlet/data/RangeTestCase.java | 24 +++++----- .../org/restlet/data/ReferenceTestCase.java | 44 +++++++++---------- .../restlet/data/RiapConnectorsTestCase.java | 2 +- .../java/org/restlet/data/RiapTestCase.java | 12 ++--- .../java/org/restlet/data/StatusTestCase.java | 10 ++--- .../java/org/restlet/engine/EngineTest.java | 4 +- .../CorsResponseFilterTestCase.java | 20 ++++----- .../RangeRepresentationTestCase.java | 6 +-- .../application/TunnelFilterTestCase.java | 16 +++---- .../engine/connector/AsyncTestCase.java | 10 ++--- .../HttpTransportProtocolsTestCase.java | 8 ++-- .../engine/header/ContentTypeTestCase.java | 6 +-- .../header/DispositionReaderTestCase.java | 4 +- .../header/DispositionWriterTestCase.java | 4 +- .../header/PreferenceReaderTestCase.java | 4 +- .../engine/header/RangeReaderTestCase.java | 8 ++-- .../restlet/engine/io/IoUtilsTestCase.java | 6 +-- .../engine/io/ReaderInputStreamTestCase.java | 4 +- .../io/UnclosableInputStreamTestCase.java | 4 +- .../resource/AnnotatedResource09TestCase.java | 4 +- .../resource/AnnotatedResource10TestCase.java | 4 +- .../resource/AnnotatedResource11TestCase.java | 4 +- .../resource/AnnotationUtilsTestCase.java | 6 +-- .../engine/util/HtmlEncodingTestCase.java | 6 +-- .../AppendableRepresentationTestCase.java | 4 +- .../DigesterRepresentationTestCase.java | 6 +-- .../FileRepresentationTestCase.java | 6 +-- .../routing/AbstractFilterTestCase.java | 6 +-- .../org/restlet/routing/RedirectTestCase.java | 4 +- .../restlet/routing/RouteListTestCase.java | 8 ++-- .../restlet/routing/ValidatorTestCase.java | 6 +-- .../restlet/security/HttpBasicTestCase.java | 4 +- .../org/restlet/security/MemoryRealmTest.java | 6 +-- .../org/restlet/security/RoleTestCase.java | 4 +- .../service/ConnegServiceTestCase.java | 4 +- .../service/MetadataServiceTestCase.java | 4 +- .../service/StatusServiceTestCase.java | 12 ++--- .../UserAgentTunnelFilterTestCase.java | 6 +-- 69 files changed, 283 insertions(+), 284 deletions(-) diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java index 34a23ad672..f51da96cb4 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestVerifierTestCase.java @@ -20,10 +20,10 @@ * * @author Jerome Louvel */ -public class DigestVerifierTestCase { +class DigestVerifierTestCase { @Test - public void test1() { + void test1() { MapVerifier mv = new MapVerifier(); mv.getLocalSecrets().put("scott", "tiger".toCharArray()); @@ -35,7 +35,7 @@ public void test1() { } @Test - public void test2() { + void test2() { MapVerifier mv = new MapVerifier(); mv.getLocalSecrets().put("scott", "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray()); @@ -54,7 +54,7 @@ public void test2() { } @Test - public void test3() { + void test3() { MapVerifier mv = new MapVerifier(); mv.getLocalSecrets().put("scott", "RuPXcqGIjq3/JsetpH/XUC15bgc=".toCharArray()); diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java index 1092c68402..97d4799457 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/HttpDigestTestCase.java @@ -35,10 +35,10 @@ * * @author Jerome Louvel */ -public class HttpDigestTestCase { +class HttpDigestTestCase { @Test - public void testDigest() { + void testDigest() { // Try unauthenticated request Request request = new Request(Method.GET, "/"); diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java index 6c37f968d3..b8ce657a44 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HelperTestCase.java @@ -20,10 +20,10 @@ import org.restlet.engine.header.HeaderConstants; import org.restlet.util.Series; -public class HttpAwsS3HelperTestCase { +class HttpAwsS3HelperTestCase { /** Test Amazon S3 authentication. */ @Test - public void testAwsS3() { + void testAwsS3() { HttpAwsS3Helper helper = new HttpAwsS3Helper(); // Example Object GET diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java index 1f293045af..6fcf678ad0 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3HostNameTestCase.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.restlet.data.Reference; -public class HttpAwsS3HostNameTestCase { +class HttpAwsS3HostNameTestCase { private String checkAddress(final String host, final String path) { return AwsUtils.getCanonicalizedResourceName( @@ -29,7 +29,7 @@ public String getPath() { } @Test - public void testGetCanonicalizedResourceName1() { + void testGetCanonicalizedResourceName1() { // http://s3-website-eu-west-1.amazonaws.com/reiabucket/louvel_cover150.jpg assertEquals( "/reiabucket/louvel_cover150.jpg", @@ -38,7 +38,7 @@ public void testGetCanonicalizedResourceName1() { } @Test - public void testGetCanonicalizedResourceName2() { + void testGetCanonicalizedResourceName2() { // http://reiabucket.s3.amazonaws.com/louvel_cover150.jpg assertEquals( "/reiabucket/louvel_cover150.jpg", @@ -46,7 +46,7 @@ public void testGetCanonicalizedResourceName2() { } @Test - public void testGetCanonicalizedResourceName3() { + void testGetCanonicalizedResourceName3() { // http://s3.amazonaws.com/reiabucket/louvel_cover150.jpg assertEquals( "/reiabucket/louvel_cover150.jpg", diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java index d8b33a3a91..f86ee2f22f 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsS3VerifierTestCase.java @@ -28,7 +28,7 @@ * * @author Jean-Philippe Steinmetz */ -public class HttpAwsS3VerifierTestCase { +class HttpAwsS3VerifierTestCase { private static final String ACCESS_ID = "0PN5J17HBGZHT7JJ3X82"; private static final String ACCESS_KEY = "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"; @@ -68,7 +68,7 @@ public void tearDownEach() throws Exception { } @Test - public void testVerify() { + void testVerify() { Request request = createRequest(); @SuppressWarnings("unchecked") Series
headers = diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java index bad5b289e5..52f0362820 100644 --- a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpDigestHelperTestCase.java @@ -22,11 +22,11 @@ import org.restlet.engine.Engine; import org.restlet.engine.security.AuthenticatorUtils; -public class HttpDigestHelperTestCase { +class HttpDigestHelperTestCase { /** Tests the authentication parsing for HTTP DIGEST. */ @Test - public void testParsingDigest() { + void testParsingDigest() { // make sure the Digest authentication scheme is registered Engine.getInstance().getRegisteredAuthenticators().add(new HttpDigestHelper()); ChallengeResponse cres1 = diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java index 804965c553..7afafb2b70 100644 --- a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java @@ -24,10 +24,10 @@ * * @author Jerome Louvel */ -public class FreeMarkerTestCase { +class FreeMarkerTestCase { @Test - public void testTemplate() throws Exception { + void testTemplate() throws Exception { // Create a temporary directory for the tests final File testDir = Files.createTempDirectory("FreeMarkerTestCase").toFile(); diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java index 4cec96e1b8..dec470bd1f 100644 --- a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/TemplateFilterTestCase.java @@ -29,17 +29,17 @@ * * @author Thierry Boileau */ -public class TemplateFilterTestCase { +class TemplateFilterTestCase { @Test - public void representationShouldBeUsedAsTemplate() throws Exception { + void representationShouldBeUsedAsTemplate() throws Exception { Request request = new Request(Method.GET, "/template.txt.fmt"); Response response = testApplication.handle(request); assertEquals("Method=GET/Path=/template.txt.fmt", response.getEntity().getText()); } @Test - public void representationShouldNotBeUsedAsTemplate() throws Exception { + void representationShouldNotBeUsedAsTemplate() throws Exception { Request request = new Request(Method.GET, "/notATemplate.txt"); Response response = testApplication.handle(request); diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java index e26cccadfe..a66b26ed85 100644 --- a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonTestCase.java @@ -24,7 +24,7 @@ * * @author Jerome Louvel */ -public class JacksonTestCase { +class JacksonTestCase { protected Customer createCustomer() { Date date = new Date(1356533333882L); @@ -58,7 +58,7 @@ protected Invoice createInvoice() { } @Test - public void testCsv() throws Exception { + void testCsv() throws Exception { Invoice invoice = createInvoice(); JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.TEXT_CSV, invoice); @@ -71,7 +71,7 @@ public void testCsv() throws Exception { } @Test - public void testException() throws Exception { + void testException() throws Exception { Customer customer = createCustomer(); MyException me = new MyException(customer, "CUST-1234"); @@ -90,7 +90,7 @@ public void testException() throws Exception { } @Test - public void testJson() throws Exception { + void testJson() throws Exception { Customer customer = createCustomer(); JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_JSON, customer); @@ -107,7 +107,7 @@ public void testJson() throws Exception { } @Test - public void testSmile() throws Exception { + void testSmile() throws Exception { Customer customer = createCustomer(); JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_JSON_SMILE, customer); @@ -116,7 +116,7 @@ public void testSmile() throws Exception { } @Test - public void testXml() throws Exception { + void testXml() throws Exception { Customer customer = createCustomer(); JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_XML, customer); @@ -132,7 +132,7 @@ public void testXml() throws Exception { } @Test - public void testXmlBomb() { + void testXmlBomb() { ClientResource cr = new ClientResource("clap://class/org/restlet/ext/jackson/jacksonBomb.xml"); Representation xmlRep = cr.get(); @@ -151,7 +151,7 @@ public void testXmlBomb() { } @Test - public void testYaml() throws Exception { + void testYaml() throws Exception { Customer customer = createCustomer(); JacksonRepresentation rep = new JacksonRepresentation<>(MediaType.APPLICATION_YAML, customer); diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java index 7b27595e9a..60ca95cbc7 100644 --- a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java @@ -27,10 +27,10 @@ * * @author Cyril Lakech */ -public class JsonpFilterTestCase { +class JsonpFilterTestCase { @Test - public void testAfterHandle() { + void testAfterHandle() { JsonpFilter filter = new JsonpFilter(null); @@ -54,7 +54,7 @@ public void testAfterHandle() { } @Test - public void testAfterHandleText() { + void testAfterHandleText() { JsonpFilter filter = new JsonpFilter(null); @@ -81,7 +81,7 @@ public void testAfterHandleText() { } @Test - public void testAfterHandle_without_callback_should_return_entity_unchanged() { + void testAfterHandle_without_callback_should_return_entity_unchanged() { JsonpFilter filter = new JsonpFilter(null); @@ -100,8 +100,7 @@ public void testAfterHandle_without_callback_should_return_entity_unchanged() { } @Test - public void testAfterHandle_with_other_mediatype_should_return_entity_unchanged() - throws Exception { + void testAfterHandle_with_other_mediatype_should_return_entity_unchanged() throws Exception { JsonpFilter filter = new JsonpFilter(null); diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java index cfea3f6c72..2591c1662e 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanFinderTestCase.java @@ -27,7 +27,7 @@ /** * @author Rhett Sutphin */ -public class SpringBeanFinderTestCase { +class SpringBeanFinderTestCase { private static class AnotherResource extends ServerResource {} @@ -95,7 +95,7 @@ protected void tearDownEach() { } @Test - public void testBeanResolutionFailsWithNeitherApplicationContextOrBeanFactory() { + void testBeanResolutionFailsWithNeitherApplicationContextOrBeanFactory() { IllegalStateException iae = assertThrows(IllegalStateException.class, () -> this.finder.create()); assertEquals( @@ -104,7 +104,7 @@ public void testBeanResolutionFailsWithNeitherApplicationContextOrBeanFactory() } @Test - public void testBeanResolutionFailsWhenNoMatchingBeanButThereIsABeanFactory() { + void testBeanResolutionFailsWhenNoMatchingBeanButThereIsABeanFactory() { this.finder.setBeanFactory(beanFactory); IllegalStateException iae = @@ -113,7 +113,7 @@ public void testBeanResolutionFailsWhenNoMatchingBeanButThereIsABeanFactory() { } @Test - public void testBeanResolutionFailsWhenNoMatchingBeanButThereIsAnApplicationContext() { + void testBeanResolutionFailsWhenNoMatchingBeanButThereIsAnApplicationContext() { this.finder.setApplicationContext(applicationContext); IllegalStateException iae = assertThrows(IllegalStateException.class, () -> this.finder.create()); @@ -121,7 +121,7 @@ public void testBeanResolutionFailsWhenNoMatchingBeanButThereIsAnApplicationCont } @Test - public void testExceptionWhenResourceBeanIsWrongType() { + void testExceptionWhenResourceBeanIsWrongType() { registerBeanFactoryBean(BEAN_NAME, String.class); this.finder.setBeanFactory(beanFactory); @@ -134,7 +134,7 @@ public void testExceptionWhenResourceBeanIsWrongType() { } @Test - public void testExceptionWhenServerResourceBeanIsWrongType() { + void testExceptionWhenServerResourceBeanIsWrongType() { registerBeanFactoryBean(BEAN_NAME, String.class); this.finder.setBeanFactory(beanFactory); @@ -147,7 +147,7 @@ public void testExceptionWhenServerResourceBeanIsWrongType() { } @Test - public void testPrefersApplicationContextOverBeanFactoryIfTheBeanIsInBoth() { + void testPrefersApplicationContextOverBeanFactoryIfTheBeanIsInBoth() { registerApplicationContextBean(BEAN_NAME, SomeResource.class); registerBeanFactoryBean(BEAN_NAME, AnotherResource.class); @@ -161,7 +161,7 @@ public void testPrefersApplicationContextOverBeanFactoryIfTheBeanIsInBoth() { } @Test - public void testReturnsResourceBeanWhenExists() { + void testReturnsResourceBeanWhenExists() { registerBeanFactoryBean(BEAN_NAME, SomeResource.class); this.finder.setBeanFactory(beanFactory); @@ -172,7 +172,7 @@ public void testReturnsResourceBeanWhenExists() { } @Test - public void testReturnsServerResourceBeanForLongFormOfCreate() { + void testReturnsServerResourceBeanForLongFormOfCreate() { registerBeanFactoryBean( BEAN_NAME, SomeServerResource.class, createServerResourcePropertyValues()); @@ -188,7 +188,7 @@ public void testReturnsServerResourceBeanForLongFormOfCreate() { } @Test - public void testReturnsServerResourceBeanWhenExists() { + void testReturnsServerResourceBeanWhenExists() { registerBeanFactoryBean( BEAN_NAME, SomeServerResource.class, createServerResourcePropertyValues()); @@ -200,7 +200,7 @@ public void testReturnsServerResourceBeanWhenExists() { } @Test - public void testUsesApplicationContextIfPresent() { + void testUsesApplicationContextIfPresent() { registerApplicationContextBean(BEAN_NAME, SomeResource.class); this.finder.setApplicationContext(applicationContext); diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java index 4fb34aa174..0991d4ae50 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringBeanRouterTestCase.java @@ -40,7 +40,7 @@ /** * @author Rhett Sutphin */ -public class SpringBeanRouterTestCase { +class SpringBeanRouterTestCase { private static class TestAuthenticator extends ChallengeAuthenticator { private TestAuthenticator() throws IllegalArgumentException { @@ -144,7 +144,7 @@ protected void tearDownEach() throws Exception { } @Test - public void testExplicitAttachmentsMayBeRestlets() { + void testExplicitAttachmentsMayBeRestlets() { String expected = "/protected/timber"; this.router.setAttachments(Collections.singletonMap(expected, "timber")); registerBeanDefinition("timber", null, TestAuthenticator.class, null); @@ -157,7 +157,7 @@ public void testExplicitAttachmentsMayBeRestlets() { } @Test - public void testExplicitAttachmentsTrumpBeanNames() { + void testExplicitAttachmentsTrumpBeanNames() { this.router.setAttachments(Collections.singletonMap(ORE_URI, "fish")); RouteList actualRoutes = actualRoutes(); assertEquals(2, actualRoutes.size(), "Wrong number of routes"); @@ -168,7 +168,7 @@ public void testExplicitAttachmentsTrumpBeanNames() { } @Test - public void testExplicitRoutingForNonResourceNonRestletBeansFails() { + void testExplicitRoutingForNonResourceNonRestletBeansFails() { this.router.setAttachments(Collections.singletonMap("/fail", "someOtherBean")); IllegalStateException ise = assertThrows(IllegalStateException.class, this::doPostProcess); @@ -178,7 +178,7 @@ public void testExplicitRoutingForNonResourceNonRestletBeansFails() { } @Test - public void testRoutesCreatedForBeanIdsIfAppropriate() { + void testRoutesCreatedForBeanIdsIfAppropriate() { String grain = "/renewable/grain/{grain_type}"; registerServerResourceBeanDefinition(grain, null); @@ -188,7 +188,7 @@ public void testRoutesCreatedForBeanIdsIfAppropriate() { } @Test - public void testRoutesCreatedForUrlAliases() { + void testRoutesCreatedForUrlAliases() { final Set actualUris = routeUris(actualRoutes()); assertEquals(2, actualUris.size(), "Wrong number of URIs"); assertTrue(actualUris.contains(ORE_URI), "Missing ore URI: " + actualUris); @@ -196,7 +196,7 @@ public void testRoutesCreatedForUrlAliases() { } @Test - public void testRoutesPointToFindersForBeans() { + void testRoutesPointToFindersForBeans() { final RouteList actualRoutes = actualRoutes(); assertEquals(2, actualRoutes.size(), "Wrong number of routes"); TemplateRoute oreRoute = matchRouteFor(ORE_URI); @@ -209,7 +209,7 @@ public void testRoutesPointToFindersForBeans() { } @Test - public void testRoutingIncludesAuthenticators() { + void testRoutingIncludesAuthenticators() { String expected = "/protected/timber"; registerBeanDefinition("timber", expected, TestAuthenticator.class, null); doPostProcess(); @@ -223,7 +223,7 @@ public void testRoutingIncludesAuthenticators() { } @Test - public void testRoutingIncludesFilters() { + void testRoutingIncludesFilters() { String expected = "/filtered/timber"; registerBeanDefinition("timber", expected, TestFilter.class, null); doPostProcess(); @@ -234,7 +234,7 @@ public void testRoutingIncludesFilters() { } @Test - public void testRoutingIncludesOtherRestlets() { + void testRoutingIncludesOtherRestlets() { String expected = "/singleton"; registerBeanDefinition("timber", expected, TestRestlet.class, null); doPostProcess(); @@ -245,7 +245,7 @@ public void testRoutingIncludesOtherRestlets() { } @Test - public void testRoutingIncludesResourceSubclasses() { + void testRoutingIncludesResourceSubclasses() { String expected = "/renewable/timber/{id}"; registerBeanDefinition( "timber", expected, TestResource.class, BeanDefinition.SCOPE_PROTOTYPE); @@ -257,7 +257,7 @@ public void testRoutingIncludesResourceSubclasses() { } @Test - public void testRoutingIncludesSpringRouterStyleExplicitlyMappedBeans() { + void testRoutingIncludesSpringRouterStyleExplicitlyMappedBeans() { final BeanDefinition bd = new RootBeanDefinition(ServerResource.class); bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); this.factory.registerBeanDefinition("timber", bd); @@ -274,7 +274,7 @@ public void testRoutingIncludesSpringRouterStyleExplicitlyMappedBeans() { } @Test - public void testRoutingSkipsResourcesWithoutAppropriateAliases() { + void testRoutingSkipsResourcesWithoutAppropriateAliases() { final BeanDefinition bd = new RootBeanDefinition(ServerResource.class); bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); this.factory.registerBeanDefinition("timber", bd); diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java index a8b05271bf..9705621135 100644 --- a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringTestCase.java @@ -25,12 +25,12 @@ * * @author Jerome Louvel */ -public class SpringTestCase { +class SpringTestCase { public static int TEST_PORT = 1337; // referenced in SpringTestCase.xml @Test - public void testSpring() throws Exception { + void testSpring() throws Exception { // Start the Restlet component Component component = (Component) ctx.getBean("component"); component.start(); @@ -40,7 +40,7 @@ public void testSpring() throws Exception { } @Test - public void testSpringServerProperties() { + void testSpringServerProperties() { Server server = (Server) ctx.getBean("server"); assertEquals("value1", server.getContext().getParameters().getFirstValue("key1")); diff --git a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java index 2b2be67715..5dcb449a8e 100644 --- a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java +++ b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafTestCase.java @@ -21,10 +21,10 @@ * * @author Thierry Boileau */ -public class ThymeleafTestCase { +class ThymeleafTestCase { @Test - public void testTemplate() throws Exception { + void testTemplate() throws Exception { ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); templateResolver.setPrefix("org/restlet/ext/thymeleaf/"); templateResolver.setSuffix(".html"); diff --git a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java index 918d4808ba..10a065535f 100644 --- a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java +++ b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/TemplateFilterTestCase.java @@ -29,17 +29,17 @@ * * @author Thierry Boileau */ -public class TemplateFilterTestCase { +class TemplateFilterTestCase { @Test - public void representationShouldBeUsedAsTemplate() throws Exception { + void representationShouldBeUsedAsTemplate() throws Exception { Request request = new Request(Method.GET, "/template.txt.vm"); Response response = testApplication.handle(request); assertEquals("Method=GET/Path=/template.txt.vm", response.getEntity().getText()); } @Test - public void representationShouldNotBeUsedAsTemplate() throws Exception { + void representationShouldNotBeUsedAsTemplate() throws Exception { Request request = new Request(Method.GET, "/notATemplate.txt"); Response response = testApplication.handle(request); diff --git a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java index b81ba7463f..6e400c6016 100644 --- a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java +++ b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityTestCase.java @@ -27,10 +27,10 @@ * * @author Jerome Louvel */ -public class VelocityTestCase { +class VelocityTestCase { @Test - public void testRepresentationTemplate() throws Exception { + void testRepresentationTemplate() throws Exception { // Create a temporary directory for the tests File testDir = new File(System.getProperty("java.io.tmpdir"), "VelocityTestCase"); testDir.mkdir(); @@ -59,7 +59,7 @@ public void testRepresentationTemplate() throws Exception { } @Test - public void testStandardTemplate() throws Exception { + void testStandardTemplate() throws Exception { // Create a temporary directory for the tests final File testDir = new File(System.getProperty("java.io.tmpdir"), "VelocityTestCase"); testDir.mkdir(); diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java index 05cf15e8ef..839d6482cc 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java @@ -45,7 +45,7 @@ * * @author Marc Portier */ -public class ResolvingTransformerTestCase { +class ResolvingTransformerTestCase { static class AssertResolvingHelper { @@ -135,7 +135,7 @@ public void handle(Request request, Response response) { // testing purely the resolver, no active transforming context (i.e., xslt engine) in this test @Test - public void testResolving() throws Exception { + void testResolving() throws Exception { Component comp = new Component(); // create an xml input representation @@ -187,7 +187,7 @@ public void testResolving() throws Exception { // functional test in the actual xslt engine context @Test - public void testTransform() throws Exception { + void testTransform() throws Exception { Component comp = new Component(); comp.getClients().add(Protocol.CLAP); diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java index ee351583de..8bd92bd79f 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformRepresentationTestCase.java @@ -20,7 +20,7 @@ * * @author Jerome Louvel */ -public class TransformRepresentationTestCase { +class TransformRepresentationTestCase { final String output1 = "cust123"; @@ -61,14 +61,14 @@ public class TransformRepresentationTestCase { MediaType.TEXT_XML); @Test - public void testSingleTransform() throws Exception { + void testSingleTransform() throws Exception { TransformRepresentation tr1 = new TransformRepresentation(this.source, this.xslt1); final String result = tr1.getText(); assertEquals(this.output1, result); } @Test - public void testDoubleTransform() throws Exception { + void testDoubleTransform() throws Exception { TransformRepresentation tr1 = new TransformRepresentation(this.source, this.xslt1); TransformRepresentation tr2 = new TransformRepresentation(tr1, this.xslt2); final String result = tr2.getText(); diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java index ab28dee2de..71589705ea 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java @@ -24,7 +24,7 @@ * * @author Jerome Louvel */ -public class TransformerTestCase { +class TransformerTestCase { static class FailureTracker { final StringBuffer trackedMessages = new StringBuffer(); @@ -71,7 +71,7 @@ void trackFailure(String message, int index, Throwable e) { MediaType.TEXT_XML); @Test - public void parallelTestTransform() { + void parallelTestTransform() { Component comp = new Component(); final TransformRepresentation tr = new TransformRepresentation(comp.getContext(), this.source, this.xslt); @@ -105,7 +105,7 @@ public void run() { } @Test - public void testTransform() throws Exception { + void testTransform() throws Exception { final Transformer transformer = new Transformer(Transformer.MODE_REQUEST, this.xslt); final String result = transformer.transform(this.source).getText(); diff --git a/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java b/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java index a07394b851..1a3e3f17c8 100644 --- a/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java +++ b/org.restlet/src/test/java/org/restlet/ApplicationContextTestCase.java @@ -26,7 +26,7 @@ * Tests that when issuing internal calls, the application context is kept intact in the caller * server resource. */ -public class ApplicationContextTestCase { +class ApplicationContextTestCase { public static class InternalApplication extends Application { @@ -86,7 +86,7 @@ protected void tearDownEach() throws Exception { } @Test - public void testApplicationContext() throws Exception { + void testApplicationContext() throws Exception { ClientResource res = new ClientResource("http://localhost:" + testPort + "/api/test"); Representation rep = res.get(MediaType.TEXT_PLAIN); // following https://github.com/restlet/restlet-framework-java/issues/1317 fix, diff --git a/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java b/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java index 3915b3325f..2dc1adc3cb 100644 --- a/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java +++ b/org.restlet/src/test/java/org/restlet/Bug1145TestCase.java @@ -22,7 +22,7 @@ import org.restlet.engine.Engine; import org.restlet.representation.StringRepresentation; -public class Bug1145TestCase { +class Bug1145TestCase { public static class Bug1145TestCaseRestlet extends Restlet { @Override public void handle(Request request, Response response) { @@ -61,7 +61,7 @@ public void tearDownEach() throws Exception { } @Test - public void test0() throws Exception { + void test0() throws Exception { Request request = new Request(Method.GET, "http://localhost:" + testPort); Response result = client.handle(request); assertEquals(Status.SUCCESS_OK, result.getStatus()); diff --git a/org.restlet/src/test/java/org/restlet/CallTestCase.java b/org.restlet/src/test/java/org/restlet/CallTestCase.java index ed6ea947b2..f2f2716bc1 100644 --- a/org.restlet/src/test/java/org/restlet/CallTestCase.java +++ b/org.restlet/src/test/java/org/restlet/CallTestCase.java @@ -24,7 +24,7 @@ * * @author Lars Heuer (heuer[at]semagia.com) */ -public class CallTestCase { +class CallTestCase { /** * Returns a connector call. * @@ -76,7 +76,7 @@ protected Response getResponse(Request request) { /** Tests context's base reference getting/setting. */ @Test - public void testBaseRef() { + void testBaseRef() { final Request request = getRequest(); final String resourceRefURI = "http://restlet.org/path/to/resource"; final Reference resourceRef = getReference(resourceRefURI); @@ -98,7 +98,7 @@ public void testBaseRef() { /** Tests client address getting/setting. */ @Test - public void testClientAddress() { + void testClientAddress() { final ClientInfo client = getRequest().getClientInfo(); String address = "127.0.0.1"; client.setAddress(address); @@ -108,7 +108,7 @@ public void testClientAddress() { /** Tests client agent getting/setting. */ @Test - public void testClientAgent() { + void testClientAgent() { final ClientInfo client = getRequest().getClientInfo(); String name = "Restlet"; client.setAgent(name); @@ -121,7 +121,7 @@ public void testClientAgent() { /** Tests client addresses getting/setting. */ @Test - public void testClientForwardedAddresses() { + void testClientForwardedAddresses() { final ClientInfo client = getRequest().getClientInfo(); String firstAddress = "127.0.0.1"; final String secondAddress = "192.168.99.10"; @@ -136,7 +136,7 @@ public void testClientForwardedAddresses() { /** Tests method getting/setting. */ @Test - public void testMethod() { + void testMethod() { final Request request = getRequest(); request.setMethod(Method.GET); assertEquals(Method.GET, request.getMethod()); @@ -147,7 +147,7 @@ public void testMethod() { /** Tests redirection reference getting/setting. */ @Test - public void testRedirectionRef() { + void testRedirectionRef() { final Request request = getRequest(); final Response response = getResponse(request); String uri = "http://restlet.org/"; @@ -163,7 +163,7 @@ public void testRedirectionRef() { /** Tests referrer reference getting/setting. */ @Test - public void testReferrerRef() { + void testReferrerRef() { final Request request = getRequest(); String uri = "http://restlet.org/"; Reference reference = getReference(uri); @@ -178,7 +178,7 @@ public void testReferrerRef() { /** Tests resource reference getting/setting. */ @Test - public void testResourceRef() { + void testResourceRef() { final Request request = getRequest(); String uri = "http://restlet.org/"; Reference reference = getReference(uri); @@ -192,7 +192,7 @@ public void testResourceRef() { /** Tests server address getting/setting. */ @Test - public void testServerAddress() { + void testServerAddress() { final Request request = getRequest(); final Response response = getResponse(request); String address = "127.0.0.1"; @@ -206,7 +206,7 @@ public void testServerAddress() { /** Tests server agent getting/setting. */ @Test - public void testServerAgent() { + void testServerAgent() { final Request request = getRequest(); final Response response = getResponse(request); String name = "Restlet"; @@ -220,7 +220,7 @@ public void testServerAgent() { /** Tests status getting/setting. */ @Test - public void testStatus() { + void testStatus() { final Request request = getRequest(); final Response response = getResponse(request); response.setStatus(Status.SUCCESS_OK); diff --git a/org.restlet/src/test/java/org/restlet/RestartTestCase.java b/org.restlet/src/test/java/org/restlet/RestartTestCase.java index 3cfab88a00..fa651ae719 100644 --- a/org.restlet/src/test/java/org/restlet/RestartTestCase.java +++ b/org.restlet/src/test/java/org/restlet/RestartTestCase.java @@ -20,7 +20,7 @@ * * @author Jerome Louvel */ -public class RestartTestCase { +class RestartTestCase { @Test void testRestart() throws Exception { diff --git a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java index 9da949c4e6..59d67ac473 100644 --- a/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/AuthenticationInfoTestCase.java @@ -19,10 +19,10 @@ * * @author Kelly McLaughlin (mclaughlin77[at]gmail.com) */ -public class AuthenticationInfoTestCase { +class AuthenticationInfoTestCase { /** Test parsing an Authorization-Info header string. */ @Test - public void testAuthenticationInfoHeaderParse() { + void testAuthenticationInfoHeaderParse() { AuthenticationInfo authInfo = new AuthenticationInfo("00000002", 1, "MDAzMTAw1", "auth", null); String authInfoHeader = "nc=00000001, qop=auth, cnonce=\"MDAzMTAw1\", nextnonce=00000002"; @@ -35,7 +35,7 @@ public void testAuthenticationInfoHeaderParse() { /** Test cnonce getting/setting. */ @Test - public void testCnonce() { + void testCnonce() { AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals("testcnonce", authInfo.getClientNonce()); @@ -47,7 +47,7 @@ public void testCnonce() { /** Equality tests. */ @Test - public void testEquals() { + void testEquals() { final AuthenticationInfo authInfo1 = new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); final AuthenticationInfo authInfo2 = @@ -59,7 +59,7 @@ public void testEquals() { /** Test nextnonce getting/setting. */ @Test - public void testNextNonce() { + void testNextNonce() { AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getNextServerNonce(), "testnonce"); @@ -71,7 +71,7 @@ public void testNextNonce() { /** Test nonce-count getting/setting. */ @Test - public void testNonceCount() { + void testNonceCount() { AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getNonceCount(), 1111111); @@ -83,7 +83,7 @@ public void testNonceCount() { /** Test message-qop getting/setting. */ @Test - public void testQop() { + void testQop() { AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getQuality(), "auth"); @@ -95,7 +95,7 @@ public void testQop() { /** Test response-auth getting/setting. */ @Test - public void testResponseAuth() { + void testResponseAuth() { AuthenticationInfo authInfo = new AuthenticationInfo("testnonce", 1111111, "testcnonce", "auth", "FFFFFF"); assertEquals(authInfo.getResponseDigest(), "FFFFFF"); @@ -106,7 +106,7 @@ public void testResponseAuth() { } @Test - public void testUnEquals() { + void testUnEquals() { final AuthenticationInfo authInfo1 = new AuthenticationInfo("testnonce1", 1111111, "testcnonce1", "auth", "FFFFFF"); final AuthenticationInfo authInfo2 = diff --git a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java index 368887d4a7..0b3acbc718 100644 --- a/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ClientInfoTestCase.java @@ -31,7 +31,7 @@ * * @author Jerome Louvel */ -public class ClientInfoTestCase { +class ClientInfoTestCase { @Nested class MixLanguageMediaTypeContentNegotiationTextCase { @@ -49,7 +49,7 @@ void setup() { } @Test - public void shouldReturnEnUsAndTextXml() { + void shouldReturnEnUsAndTextXml() { List variants = List.of( new Variant(TEXT_XML, ENGLISH_US), @@ -61,7 +61,7 @@ public void shouldReturnEnUsAndTextXml() { } @Test - public void shouldReturnEnAndTextXml() { + void shouldReturnEnAndTextXml() { List variants = List.of(new Variant(TEXT_XML, ENGLISH), new Variant(TEXT_XML, FRENCH)); Variant pv = connegService.getPreferredVariant(variants, request, ms); @@ -72,7 +72,7 @@ public void shouldReturnEnAndTextXml() { // Testing quality priority over parent metadata @Test - public void shouldReturnFrFrAndText() { + void shouldReturnFrFrAndText() { List variants = List.of(new Variant(TEXT_PLAIN, ENGLISH), new Variant(TEXT_XML, FRENCH_FRANCE)); Variant pv = connegService.getPreferredVariant(variants, request, ms); @@ -83,7 +83,7 @@ public void shouldReturnFrFrAndText() { // Testing quality priority over parent metadata @Test - public void shouldReturnFrFrAndXml() { + void shouldReturnFrFrAndXml() { List variants = List.of( new Variant(APPLICATION_XML, ENGLISH_US), @@ -96,7 +96,7 @@ public void shouldReturnFrFrAndXml() { // Leveraging parent media types @Test - public void shouldPreferEnUsAndApplicationXml() { + void shouldPreferEnUsAndApplicationXml() { List variants = List.of( new Variant(APPLICATION_XML, ENGLISH_US), @@ -110,7 +110,7 @@ public void shouldPreferEnUsAndApplicationXml() { /** Conneg tests for IE which accepts all media types. */ @Test - public void testConnegIe() { + void testConnegIe() { ClientInfo ci = new ClientInfo(); Preference allMediaTypesPreference = new Preference<>(MediaType.ALL, 1.0F); ci.getAcceptedMediaTypes().add(allMediaTypesPreference); diff --git a/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java b/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java index 291ec23fb1..8ad9128295 100644 --- a/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/CookieTestCase.java @@ -18,11 +18,11 @@ * * @author Jerome Louvel */ -public class CookieTestCase { +class CookieTestCase { /** Equality tests. */ @Test - public void testEquals() { + void testEquals() { Cookie c1 = new Cookie(1, "name1", "value1", "path1", "domain1"); Cookie c2 = new Cookie(1, "name1", "value1", "path1", "domain1"); @@ -33,7 +33,7 @@ public void testEquals() { /** Inequality tests. */ @Test - public void testUnEquals() { + void testUnEquals() { Cookie c1 = new Cookie(1, "name1", "value1", "path1", "domain1"); Cookie c2 = new Cookie(2, "name2", "value2", "path2", "domain2"); assertNotEquals(c1, c2); diff --git a/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java b/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java index ced7cf2e4f..71c120ef86 100644 --- a/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FileClientTestCase.java @@ -23,10 +23,10 @@ * * @author Jerome Louvel */ -public class FileClientTestCase { +class FileClientTestCase { @Test - public void testFileClient() throws IOException { + void testFileClient() throws IOException { Engine.register(); Engine.clearThreadLocalVariables(); diff --git a/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java b/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java index f90e336824..04c17e3602 100644 --- a/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FileReferenceTestCase.java @@ -18,10 +18,10 @@ * * @author Jerome Louvel */ -public class FileReferenceTestCase { +class FileReferenceTestCase { @Test - public void testCreation() { + void testCreation() { String path = "D:\\Restlet\\build.xml"; LocalReference fr = LocalReference.createFileReference(path); fr.getFile(); diff --git a/org.restlet/src/test/java/org/restlet/data/FormTestCase.java b/org.restlet/src/test/java/org/restlet/data/FormTestCase.java index 787fb44340..6e46ed17ac 100644 --- a/org.restlet/src/test/java/org/restlet/data/FormTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/FormTestCase.java @@ -20,10 +20,10 @@ * * @author Jerome Louvel */ -public class FormTestCase { +class FormTestCase { @Test - public void testParsing() throws IOException { + void testParsing() throws IOException { Form form = new Form(); form.add("name", "John D. Mitchell"); form.add("email", "john@bob.net"); @@ -37,7 +37,7 @@ public void testParsing() throws IOException { } @Test - public void testEmptyParameter() { + void testEmptyParameter() { // Manual construction of form Form form = new Form(); form.add("normalParam", "abcd"); diff --git a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java index e32ca832c8..b8ce8d765a 100644 --- a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java @@ -24,7 +24,7 @@ * * @author Jerome Louvel */ -public class MediaTypeTestCase { +class MediaTypeTestCase { /** * Makes sure that a {@link MediaType} instance initialized on the specified name has the @@ -46,7 +46,7 @@ public void assertMediaType(String name, String main, String sub, boolean concre /** Makes sure concrete types are properly initialized. */ @Test - public void testConcrete() { + void testConcrete() { assertMediaType("application/xml", "application", "xml", true); assertMediaType("application/ xml ", "application", "xml", true); assertMediaType(" application /xml", "application", "xml", true); @@ -56,7 +56,7 @@ public void testConcrete() { /** Makes sure concrete types are properly initialized. */ @Test - public void testParameters() { + void testParameters() { MediaType mt = MediaType.valueOf("application/atom+xml;type=entry"); assertEquals("entry", mt.getParameters().getFirstValue("type")); @@ -66,7 +66,7 @@ public void testParameters() { /** Equality tests. */ @Test - public void testEquals() { + void testEquals() { MediaType mt1 = new MediaType("application/xml"); MediaType mt2 = MediaType.APPLICATION_XML; assertEquals(mt1, mt2); @@ -98,7 +98,7 @@ public void testEquals() { /** Test inclusion. */ @Test - public void testIncludes() { + void testIncludes() { MediaType mt1 = MediaType.APPLICATION_ALL; MediaType mt2 = MediaType.APPLICATION_XML; assertTrue(mt1.includes(mt1)); @@ -203,7 +203,7 @@ public void testIncludes() { } @Test - public void testMostSpecificMediaType() { + void testMostSpecificMediaType() { assertEquals( MediaType.TEXT_ALL, MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_ALL)); assertEquals( @@ -231,7 +231,7 @@ public void testMostSpecificMediaType() { /** Makes sure that 'abstract' types are properly initialised. */ @Test - public void testNotConcrete() { + void testNotConcrete() { // */* assertMediaType("", "*", "*", false); assertMediaType(" ", "*", "*", false); @@ -271,7 +271,7 @@ public void testNotConcrete() { /** Test references that are unequal. */ @Test - public void testUnEquals() { + void testUnEquals() { MediaType mt1 = new MediaType("application/xml"); MediaType mt2 = new MediaType("application/xml2"); assertNotEquals(mt1, mt2); @@ -294,7 +294,7 @@ public void testUnEquals() { /** Testing {@link MediaType#valueOf(String)} and {@link MediaType#register(String, String)} */ @Test - public void testValueOf() { + void testValueOf() { assertSame(MediaType.APPLICATION_XML, MediaType.valueOf("application/xml")); assertSame(MediaType.ALL, MediaType.valueOf("*/*")); final MediaType newType = MediaType.valueOf("application/x-restlet-test"); @@ -323,7 +323,7 @@ public void testValueOf() { @Test @SuppressWarnings("unchecked") - public void testUnmodifiable() { + void testUnmodifiable() { final Form form = new Form(); form.add("name1", "value1"); diff --git a/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java b/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java index c8b1687663..b6cdc42849 100644 --- a/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/MethodTestCase.java @@ -21,14 +21,14 @@ * * @author Andreas Wundsam */ -public class MethodTestCase { +class MethodTestCase { /** * validate that Method caching works, i.e., the value returned by Method.valueOf("GET") is the * cached constant Method.GET. */ @Test - public void testCaching() { + void testCaching() { Assertions.assertEquals( Method.GET, Method.valueOf("GET"), diff --git a/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java b/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java index 9a8a083eb0..3d284fba7c 100644 --- a/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RangeTestCase.java @@ -40,7 +40,7 @@ * * @author Jerome Louvel */ -public class RangeTestCase { +class RangeTestCase { private static final Tag ENTITY_TAG = new Tag("TestRangeGetRestlet"); @@ -113,7 +113,7 @@ void noRange() throws IOException { } @Test - public void fullRange() throws IOException { + void fullRange() throws IOException { request.setRanges(List.of(new Range(0, 10))); Response response = testRangeApplication.handle(request); assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); @@ -125,7 +125,7 @@ public void fullRange() throws IOException { } @Test - public void rangeFirst2Bytes() throws Exception { + void rangeFirst2Bytes() throws Exception { request.setRanges(List.of(new Range(Range.INDEX_FIRST, 2))); Response response = testRangeApplication.handle(request); assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); @@ -137,7 +137,7 @@ public void rangeFirst2Bytes() throws Exception { } @Test - public void range2To4Bytes() throws Exception { + void range2To4Bytes() throws Exception { request.setRanges(List.of(new Range(2, 2))); Response response = testRangeApplication.handle(request); assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); @@ -149,7 +149,7 @@ public void range2To4Bytes() throws Exception { } @Test - public void range2To9Bytes() throws Exception { + void range2To9Bytes() throws Exception { request.setRanges(List.of(new Range(2, 7))); Response response = testRangeApplication.handle(request); assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); @@ -161,7 +161,7 @@ public void range2To9Bytes() throws Exception { } @Test - public void rangeLast7Bytes() throws Exception { + void rangeLast7Bytes() throws Exception { request.setRanges(List.of(new Range(Range.INDEX_LAST, 7))); Response response = testRangeApplication.handle(request); assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); @@ -173,7 +173,7 @@ public void rangeLast7Bytes() throws Exception { } @Test - public void range2toMaxBytes() throws Exception { + void range2toMaxBytes() throws Exception { request.setRanges(List.of(new Range(2, Range.SIZE_MAX))); Response response = testRangeApplication.handle(request); assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); @@ -184,7 +184,7 @@ public void range2toMaxBytes() throws Exception { } @Test - public void range2To1000Bytes() throws Exception { + void range2To1000Bytes() throws Exception { request.setRanges(List.of(new Range(2, 1000))); Response response = testRangeApplication.handle(request); assertEquals(Status.SUCCESS_PARTIAL_CONTENT, response.getStatus()); @@ -206,7 +206,7 @@ void setUp() { } @Test - public void rangeShouldApply() throws Exception { + void rangeShouldApply() throws Exception { request.getConditions().setRangeTag(ENTITY_TAG); Response response = testRangeApplication.handle(request); @@ -219,7 +219,7 @@ public void rangeShouldApply() throws Exception { } @Test - public void rangeShouldNotApply() throws Exception { + void rangeShouldNotApply() throws Exception { Tag entityTag = new Tag(UUID.randomUUID().toString()); request.getConditions().setRangeTag(entityTag); Response response = testRangeApplication.handle(request); @@ -233,7 +233,7 @@ public void rangeShouldNotApply() throws Exception { class TestPut { @Test - public void testPut() throws IOException { + void testPut() throws IOException { Path testFilePath = testDirPath.resolve("essai.txt"); testFilePath.toFile().delete(); @@ -328,7 +328,7 @@ void putNewFileWithRangeWithoutSize() throws IOException { } @Test - public void testMultipleRanges() { + void testMultipleRanges() { Request request = new Request(Method.GET, "/testGet"); request.setRanges(List.of(new Range(1), new Range(2))); diff --git a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java index c568f6ffa6..7d08203bb5 100644 --- a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java @@ -27,7 +27,7 @@ * @author Jerome Louvel * @author Lars Heuer (heuer[at]semagia.com) Semagia */ -public class ReferenceTestCase { +class ReferenceTestCase { protected static final String DEFAULT_SCHEME = "http"; protected static final String DEFAULT_SCHEMEPART = "//"; @@ -57,7 +57,7 @@ protected Reference getReference() { /** Test addition methods. */ @Test - public void testAdditions() throws Exception { + void testAdditions() throws Exception { final Reference ref = new Reference("http://restlet.org"); ref.addQueryParameter("abc", "123"); assertEquals("http://restlet.org?abc=123", ref.toString()); @@ -70,7 +70,7 @@ public void testAdditions() throws Exception { } @Test - public void testEmptyRef() { + void testEmptyRef() { Reference reference = new Reference(); reference.setAuthority("testAuthority"); // must not produce NPE @@ -119,7 +119,7 @@ public void testEmptyRef() { /** Equality tests. */ @Test - public void testEquals() { + void testEquals() { final Reference ref1 = getDefaultReference(); final Reference ref2 = getDefaultReference(); assertEquals(ref1, ref2); @@ -127,7 +127,7 @@ public void testEquals() { } @Test - public void testGetLastSegment() { + void testGetLastSegment() { Reference reference = new Reference("http://hostname"); assertNull(reference.getLastSegment()); @@ -149,7 +149,7 @@ public void testGetLastSegment() { /** Test hostname getting/setting. */ @Test - public void testHostName() { + void testHostName() { final Reference ref = getReference(); String host = "restlet.org"; ref.setHostDomain(host); @@ -162,7 +162,7 @@ public void testHostName() { } @Test - public void testMatrix() { + void testMatrix() { final Reference ref1 = new Reference("http://domain.tld/whatever/a=1;b=2;c=4?x=a&y=b"); final Reference ref2 = new Reference("http://domain.tld/whatever/a=1/foo;b=2;c=4;d?x=a&y=b"); @@ -192,7 +192,7 @@ public void testMatrix() { } @Test - public void testOriginalRef() { + void testOriginalRef() { Reference ref = new Reference("http://localhost/test"); Series
headers = new Series<>(Header.class); headers.add(HeaderConstants.HEADER_X_FORWARDED_PROTO, "HTTPS"); @@ -205,7 +205,7 @@ public void testOriginalRef() { /** Test the computation of parent references, for absolute and relative URIs. */ @Test - public void testParentRef() { + void testParentRef() { Reference baseRef = new Reference("http://test.com/foo/bar"); Reference parentRef = baseRef.getParentRef(); assertEquals("http://test.com/foo/", parentRef.toString()); @@ -217,7 +217,7 @@ public void testParentRef() { /** Tests the URI parsing. */ @Test - public void testParsing() { + void testParsing() { final String base = "http://a/b/c/d;p?q"; final String uri01 = "g:h"; @@ -500,7 +500,7 @@ public void testParsing() { /** Test port getting/setting. */ @Test - public void testPort() { + void testPort() { Reference ref = getDefaultReference(); int port = 8080; ref.setHostPort(port); @@ -513,7 +513,7 @@ public void testPort() { } @Test - public void testProtocolConstructors() { + void testProtocolConstructors() { assertEquals("http://restlet.org", new Reference(Protocol.HTTP, "restlet.org").toString()); assertEquals( "https://restlet.org:8443", @@ -525,7 +525,7 @@ public void testProtocolConstructors() { } @Test - public void testQuery() { + void testQuery() { Reference ref1 = new Reference("http://localhost/search?q=anythingelse%"); String query = ref1.getQuery(); @@ -544,7 +544,7 @@ public void testQuery() { } @Test - public void testQueryWithUri() { + void testQueryWithUri() { Reference ref = new Reference( new Reference("http://localhost:8111/"), @@ -640,7 +640,7 @@ private void testRef4( } @Test - public void testRiap() { + void testRiap() { Reference baseRef = new Reference("riap://component/exist/db/"); Reference ref = new Reference(baseRef, "something.xq"); assertEquals("riap://component/exist/db/something.xq", ref.getTargetRef().toString()); @@ -648,7 +648,7 @@ public void testRiap() { /** Test scheme getting/setting. */ @Test - public void testScheme() { + void testScheme() { final Reference ref = getDefaultReference(); assertEquals(DEFAULT_SCHEME, ref.getScheme()); final String scheme = "https"; @@ -660,7 +660,7 @@ public void testScheme() { /** Test scheme-specific part getting/setting. */ @Test - public void testSchemeSpecificPart() { + void testSchemeSpecificPart() { final Reference ref = getDefaultReference(); String part = "//restlet.org"; assertEquals(part, ref.getSchemeSpecificPart()); @@ -671,7 +671,7 @@ public void testSchemeSpecificPart() { /** Test setting of the last segment. */ @Test - public void testSetLastSegment() { + void testSetLastSegment() { Reference ref = new Reference("http://localhost:1234"); ref.addSegment("test"); assertEquals("http://localhost:1234/test", ref.toString()); @@ -691,7 +691,7 @@ public void testSetLastSegment() { } @Test - public void testTargetRef() { + void testTargetRef() { Reference ref = new Reference( "http://twitter.com?status=RT @gamasutra: Devil May Cry : Born Again http://www.gamasutra.com/view/feature/177267/"); @@ -708,7 +708,7 @@ public void testTargetRef() { /** Test references that are unequal. */ @Test - public void testUnEquals() throws Exception { + void testUnEquals() throws Exception { final String uri1 = "http://restlet.org/"; final String uri2 = "http://restlet.net/"; final Reference ref1 = new Reference(uri1); @@ -718,7 +718,7 @@ public void testUnEquals() throws Exception { } @Test - public void testUserinfo() { + void testUserinfo() { final Reference reference = new Reference("http://localhost:81"); // This format is depre. however, we may prevent failures. reference.setUserInfo("login:password"); @@ -753,7 +753,7 @@ public void testUserinfo() { } @Test - public void testValidity() { + void testValidity() { String uri = "http ://domain.tld/whatever/"; Reference ref = new Reference(uri); assertEquals("http%20://domain.tld/whatever/", ref.toString()); diff --git a/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java b/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java index 827a04484d..25e9b962c2 100644 --- a/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RiapConnectorsTestCase.java @@ -26,7 +26,7 @@ import org.restlet.routing.Router; /** Unit test case for the RIAP Internal routing protocol. */ -public class RiapConnectorsTestCase { +class RiapConnectorsTestCase { /** Test the RIAP client and server connectors. */ @ParameterizedTest diff --git a/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java b/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java index 44b2fa1717..20fbd4bd44 100644 --- a/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/RiapTestCase.java @@ -33,7 +33,7 @@ * * @author Marc Portier */ -public class RiapTestCase { +class RiapTestCase { private static final String DEFAULT_MSG = "no-default"; @@ -104,14 +104,14 @@ public void handle(Request request, Response response) { } @Test - public void testEcho() throws Exception { + void testEcho() throws Exception { String msg = "this%20message"; Representation echoRep = sendLocalRequestAndGetRepresentation("/echo/" + msg); assertEquals(msg, echoRep.getText(), "expected echo of uri-remainder"); } @Test - public void testObject() throws Exception { + void testObject() throws Exception { final Representation objRep = sendLocalRequestAndGetRepresentation("/object"); assertSame( JUST_SOME_OBJ, @@ -120,19 +120,19 @@ public void testObject() throws Exception { } @Test - public void testNull() throws Exception { + void testNull() throws Exception { final Representation nullRep = sendLocalRequestAndGetRepresentation("/null"); assertNull(((ObjectRepresentation) nullRep).getObject(), "expected null"); } @Test - public void testWhatever() throws Exception { + void testWhatever() throws Exception { final Representation anyRep = sendLocalRequestAndGetRepresentation("/whatever"); assertEquals(DEFAULT_MSG, anyRep.getText(), "expected echo of uri-remainder"); } @Test - public void testSelfAggregated() throws Exception { + void testSelfAggregated() throws Exception { final Representation aggRep = sendLocalRequestAndGetRepresentation("/self-aggregated"); final String expectedResult = buildAggregate(ECHO_TEST_MSG, ECHO_TEST_MSG); assertEquals(expectedResult, aggRep.getText(), "expected specific aggregated message"); diff --git a/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java b/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java index 7251fa447b..c28a975fc5 100644 --- a/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/StatusTestCase.java @@ -19,10 +19,10 @@ * * @author Jerome Louvel */ -public class StatusTestCase { +class StatusTestCase { @Test - public void testCustomDescription() { + void testCustomDescription() { final String customDescription = "My custom description"; final Status s = new Status(Status.CLIENT_ERROR_NOT_FOUND, customDescription); assertEquals(customDescription, s.getDescription()); @@ -30,7 +30,7 @@ public void testCustomDescription() { /** Equality tests. */ @Test - public void testEquals() { + void testEquals() { final Status s1 = new Status(201); final Status s2 = Status.SUCCESS_CREATED; @@ -41,7 +41,7 @@ public void testEquals() { /** Tests for status classes. */ @Test - public void testStatusClasses() { + void testStatusClasses() { final Status s1 = new Status(287); assertTrue(s1.isSuccess()); @@ -52,7 +52,7 @@ public void testStatusClasses() { /** Unequality tests. */ @Test - public void testUnEquals() { + void testUnEquals() { final Status s1 = new Status(200); final Status s2 = Status.SUCCESS_CREATED; diff --git a/org.restlet/src/test/java/org/restlet/engine/EngineTest.java b/org.restlet/src/test/java/org/restlet/engine/EngineTest.java index 64372a0b1b..91ae860e1d 100644 --- a/org.restlet/src/test/java/org/restlet/engine/EngineTest.java +++ b/org.restlet/src/test/java/org/restlet/engine/EngineTest.java @@ -16,10 +16,10 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class EngineTest { +class EngineTest { @Test - public void engineVersionShouldBeEqualToMavenProjectVersion() { + void engineVersionShouldBeEqualToMavenProjectVersion() { // When I retrieve the Maven project's version as stated in the pom file. Properties properties = new Properties(); try (InputStream resourceAsStream = diff --git a/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java index dd07f7ce32..d0bdede48e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/CorsResponseFilterTestCase.java @@ -28,7 +28,7 @@ /** * @author Manuel Boillod */ -public class CorsResponseFilterTestCase { +class CorsResponseFilterTestCase { private CorsFilter corsFilter; @@ -49,7 +49,7 @@ public void setUpEach() throws Exception { // INVALID CORS REQUESTS @Test - public void testGet_withoutOrigin() { + void testGet_withoutOrigin() { Request request = new Request(); request.setMethod(Method.GET); Response response = corsFilter.handle(request); @@ -57,7 +57,7 @@ public void testGet_withoutOrigin() { } @Test - public void testOption_withoutOrigin() { + void testOption_withoutOrigin() { Request request = new Request(); request.setMethod(Method.OPTIONS); Response response = corsFilter.handle(request); @@ -65,7 +65,7 @@ public void testOption_withoutOrigin() { } @Test - public void testOption_withoutRequestMethod() { + void testOption_withoutRequestMethod() { Request request = new Request(); request.setMethod(Method.OPTIONS); request.getHeaders().set("Origin", "localhost"); @@ -76,7 +76,7 @@ public void testOption_withoutRequestMethod() { // VALID CORS REQUESTS @Test - public void testGet() { + void testGet() { Request request = new Request(); request.setMethod(Method.GET); request.getHeaders().set("Origin", "localhost"); @@ -89,7 +89,7 @@ public void testGet() { } @Test - public void testGet_withAuthenticationAllowed() { + void testGet_withAuthenticationAllowed() { corsFilter.setAllowedCredentials(true); Request request = new Request(); @@ -104,7 +104,7 @@ public void testGet_withAuthenticationAllowed() { } @Test - public void testOption_requestGet() { + void testOption_requestGet() { Request request = new Request(); request.setMethod(Method.OPTIONS); request.getHeaders().set("Origin", "localhost"); @@ -120,7 +120,7 @@ public void testOption_requestGet() { } @Test - public void testOption_requestGet_skippingResource() { + void testOption_requestGet_skippingResource() { corsFilter.setSkippingResourceForCorsOptions(true); Request request = new Request(); @@ -139,7 +139,7 @@ public void testOption_requestGet_skippingResource() { } @Test - public void testOption_requestPost_skippingResource() { + void testOption_requestPost_skippingResource() { corsFilter.setSkippingResourceForCorsOptions(true); Request request = new Request(); @@ -158,7 +158,7 @@ public void testOption_requestPost_skippingResource() { } @Test - public void testOption_requestGet_withAuthenticationAllowed() { + void testOption_requestGet_withAuthenticationAllowed() { corsFilter.setAllowedCredentials(true); Request request = new Request(); diff --git a/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java index b835daa5db..42e38f63fc 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/RangeRepresentationTestCase.java @@ -20,10 +20,10 @@ * * @author Jerome Louvel */ -public class RangeRepresentationTestCase { +class RangeRepresentationTestCase { @Test - public void testAppendable() throws Exception { + void testAppendable() throws Exception { StringRepresentation sr = new StringRepresentation("1234567890"); RangeRepresentation rr = new RangeRepresentation(sr); rr.setRange(new Range(2, 5)); @@ -35,7 +35,7 @@ public void testAppendable() throws Exception { } @Test - public void testSize() throws Exception { + void testSize() throws Exception { StringRepresentation sr = new StringRepresentation("1234567890"); RangeRepresentation rr = new RangeRepresentation(sr); rr.setRange(new Range(5, 10000)); diff --git a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java index b3ea9a2d89..79a06aa89e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java @@ -36,7 +36,7 @@ import org.restlet.util.Series; /** Tests cases for the tunnel filter. */ -public class TunnelFilterTestCase { +class TunnelFilterTestCase { /** . */ private static final String EFFECTED = "http://example.org/adf.asdf/af.html"; @@ -221,7 +221,7 @@ protected void tearDownEach() throws Exception { } @Test - public void testExtMappingOff1() { + void testExtMappingOff1() { extensionTunnelOff(); createGet(UNEFFECTED); this.accLanguages.add(new Preference<>(Language.valueOf("ajh"))); @@ -235,7 +235,7 @@ public void testExtMappingOff1() { } @Test - public void testExtMappingOff2() { + void testExtMappingOff2() { extensionTunnelOff(); createGet(EFFECTED); this.accLanguages.add(new Preference<>(Language.valueOf("ajh"))); @@ -249,7 +249,7 @@ public void testExtMappingOff2() { } @Test - public void testExtMappingOn() { + void testExtMappingOn() { createGet(UNEFFECTED); filter(); check(UNEFFECTED, "ab"); @@ -335,7 +335,7 @@ public void testExtMappingOn() { } @Test - public void testMethodTunnelingViaHeader() { + void testMethodTunnelingViaHeader() { tunnelFilter.getTunnelService().setMethodTunnel(true); Map attributesHeader = new HashMap<>(); Series
headers = new Series<>(Header.class); @@ -367,7 +367,7 @@ public void testMethodTunnelingViaHeader() { } @Test - public void testWithMatrixParam() { + void testWithMatrixParam() { createGet(EFFECTED + ";abcdef"); filter(); check("http://example.org/adf.asdf/af;abcdef", null); @@ -378,7 +378,7 @@ public void testWithMatrixParam() { } @Test - public void testMethodTunnelingViaUserAgent() { + void testMethodTunnelingViaUserAgent() { tunnelFilter.getTunnelService().setExtensionsTunnel(false); tunnelFilter.getTunnelService().setHeadersTunnel(false); tunnelFilter.getTunnelService().setMethodTunnel(false); @@ -408,7 +408,7 @@ public void testMethodTunnelingViaUserAgent() { } @Test - public void testMethodTunnelingViaQuery() { + void testMethodTunnelingViaQuery() { tunnelFilter.getTunnelService().setExtensionsTunnel(false); tunnelFilter.getTunnelService().setHeadersTunnel(false); tunnelFilter.getTunnelService().setMethodTunnel(false); diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java index 441398e9ad..a80faced4b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/AsyncTestCase.java @@ -39,7 +39,7 @@ * * @author Florian Buecklers */ -public class AsyncTestCase { +class AsyncTestCase { private Context context; @@ -170,22 +170,22 @@ public void handle(Request request, Response response) { } @Test - public void testGet() throws Exception { + void testGet() throws Exception { testCall(context, 10, Method.GET); } @Test - public void testPost() throws Exception { + void testPost() throws Exception { testCall(context, 10, Method.POST); } @Test - public void testPut() throws Exception { + void testPut() throws Exception { testCall(context, 10, Method.PUT); } @Test - public void testDelete() throws Exception { + void testDelete() throws Exception { testCall(context, 10, Method.DELETE); } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java index 86ce633871..577635477c 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/HttpTransportProtocolsTestCase.java @@ -46,7 +46,7 @@ import org.restlet.util.Series; /** Test the support of HTTP2 and HTTP3 transport protocols. */ -public class HttpTransportProtocolsTestCase { +class HttpTransportProtocolsTestCase { protected static final String KEYSTORE_FILE_NAME = "dummy.p12"; protected static final String KEYSTORE_PASSWORD = "testtest"; @@ -227,7 +227,7 @@ abstract static class HttpTransportProtocolTest { @ParameterizedTest(name = "server: {0} / client: {1}") @MethodSource("validTestCases") - public void clientCompliesWithServer( + void clientCompliesWithServer( final String httpTransportProtocol, final TestHttpClient testHttpClient) throws Exception { final Server server = newServer(httpTransportProtocol); @@ -238,7 +238,7 @@ public void clientCompliesWithServer( @ParameterizedTest(name = "server: {0} / client: {1}") @MethodSource("invalidTestCases") - public void clientDoesNotComplyWithServer( + void clientDoesNotComplyWithServer( final String httpTransportProtocol, final TestHttpClient testHttpClient) throws Exception { final Server server = newServer(httpTransportProtocol); @@ -253,7 +253,7 @@ public void clientDoesNotComplyWithServer( @ParameterizedTest(name = "server: {0}") @ValueSource(strings = {"", "invalid", "http3"}) - public void invalidServerConfiguration(final String httpTransportProtocol) { + void invalidServerConfiguration(final String httpTransportProtocol) { final Server server = newServer(httpTransportProtocol); final Exception exception = assertThrows(IllegalArgumentException.class, server::start); diff --git a/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java index 6a543d2691..5df054627b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/ContentTypeTestCase.java @@ -18,17 +18,17 @@ * * @author Jerome Louvel */ -public class ContentTypeTestCase { +class ContentTypeTestCase { @Test - public void testParsingInvalid() { + void testParsingInvalid() { String h1 = "application/docbook+xml; version='my version 1.0'"; assertThrows(IllegalArgumentException.class, () -> new ContentType(h1)); } @Test - public void testParsing() { + void testParsing() { String h1 = "application/docbook+xml; version=\"my version 1.0\""; String h2 = "application/docbook+xml; version='my%20version%201.0'"; diff --git a/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java index af2a1c05db..e8d1e7a61e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/DispositionReaderTestCase.java @@ -20,10 +20,10 @@ * * @author Kevin Conaway */ -public class DispositionReaderTestCase { +class DispositionReaderTestCase { @Test - public void testParseContentDisposition() throws IOException { + void testParseContentDisposition() throws IOException { Disposition disposition = new DispositionReader("attachment; fileName=\"file.txt\"").readValue(); assertEquals("file.txt", disposition.getParameters().getFirstValue("fileName")); diff --git a/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java index 09118d4abb..059c4a79bb 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/DispositionWriterTestCase.java @@ -22,10 +22,10 @@ * * @author Kevin Conaway */ -public class DispositionWriterTestCase { +class DispositionWriterTestCase { @Test - public void testFormatContentDisposition() { + void testFormatContentDisposition() { Disposition disposition = new Disposition(); assertEquals("", DispositionWriter.write(disposition)); diff --git a/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java index d0532044ad..4e4809393b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/PreferenceReaderTestCase.java @@ -21,7 +21,7 @@ * * @author Jerome Louvel */ -public class PreferenceReaderTestCase { +class PreferenceReaderTestCase { /** * Tests the parsing of a single preference header. * @@ -50,7 +50,7 @@ private void testMediaType(String headerValue, boolean testEquals) { /** Tests the preferences parsing. */ @Test - public void testParsing() { + void testParsing() { testMediaType( "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;LEVEL=2;q=0.4;ext1, */*;q=0.5", true); diff --git a/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java index a3333c046e..8271dcfea5 100644 --- a/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/header/RangeReaderTestCase.java @@ -20,10 +20,10 @@ * * @author Thierry Boileau */ -public class RangeReaderTestCase { +class RangeReaderTestCase { @Test - public void testUpdateRangeFirst9Bytes() { + void testUpdateRangeFirst9Bytes() { final String contentRangeHeaderValue = "bytes 1-9/10"; final Representation representation = new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); @@ -37,7 +37,7 @@ public void testUpdateRangeFirst9Bytes() { } @Test - public void testUpdateRange0To100Bytes() { + void testUpdateRange0To100Bytes() { final String contentRangeHeaderValue = "bytes 0-100/10"; final Representation representation = new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); @@ -51,7 +51,7 @@ public void testUpdateRange0To100Bytes() { } @Test - public void testUpdateRange1To9Bytes() { + void testUpdateRange1To9Bytes() { final String contentRangeHeaderValue = "bytes 1-9/*"; final Representation representation = new StringRepresentation("0123456789", MediaType.TEXT_PLAIN); diff --git a/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java index 858426219d..2c71855d54 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/IoUtilsTestCase.java @@ -26,10 +26,10 @@ * * @author Kevin Conaway */ -public class IoUtilsTestCase { +class IoUtilsTestCase { @Test - public void testGetStream() throws IOException { + void testGetStream() throws IOException { StringWriter writer = new StringWriter(); OutputStream out = IoUtils.getStream(writer, CharacterSet.UTF_8); out.write("testé".getBytes(StandardCharsets.UTF_8)); @@ -39,7 +39,7 @@ public void testGetStream() throws IOException { } @Test - public void testPipe() throws IOException { + void testPipe() throws IOException { final byte[] content = new byte[] {1, 2, 3, -1, -2, -3, 4, 5, 6}; ByteArrayInputStream bais = new ByteArrayInputStream(content); diff --git a/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java index de72d5a998..b88e8e87db 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/ReaderInputStreamTestCase.java @@ -23,10 +23,10 @@ * * @author Jerome Louvel */ -public class ReaderInputStreamTestCase { +class ReaderInputStreamTestCase { @Test - public void testConversion() throws IOException { + void testConversion() throws IOException { StringBuilder buf = new StringBuilder(); for (int i = 0; i < 5000; i++) { diff --git a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java index 007e9eae9c..738ebfc1c6 100644 --- a/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/io/UnclosableInputStreamTestCase.java @@ -20,7 +20,7 @@ * * @author Kevin Conaway */ -public class UnclosableInputStreamTestCase { +class UnclosableInputStreamTestCase { static class MockInputStream extends InputStream { boolean closed = false; @@ -37,7 +37,7 @@ public int read() { } @Test - public void testClose() throws IOException { + void testClose() throws IOException { final MockInputStream mock = new MockInputStream(); final InputStream keepAlive = new UnclosableInputStream(mock); diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java index 0b2ecebb58..7066f5446b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource09TestCase.java @@ -34,7 +34,7 @@ * * @author Thierry Boileau */ -public class AnnotatedResource09TestCase extends JettyConnectorTestCase { +class AnnotatedResource09TestCase extends JettyConnectorTestCase { public static final Method SI = new Method("SI", "What a method!", "http://restlet.org", true, true); @@ -68,7 +68,7 @@ static Stream methodsProvider() { @ParameterizedTest @MethodSource("methodsProvider") - public void testCustomMethod(final Method method) throws IOException, ResourceException { + void testCustomMethod(final Method method) throws IOException, ResourceException { final String methodName = method.getName().toLowerCase(); Request request = createRequest(method); diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java index 8207aa2b64..31c2be7686 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource10TestCase.java @@ -27,7 +27,7 @@ * * @author Thierry Boileau */ -public class AnnotatedResource10TestCase extends JettyConnectorTestCase { +class AnnotatedResource10TestCase extends JettyConnectorTestCase { protected Application createApplication(final String path) { return new Application() { @@ -47,7 +47,7 @@ public Restlet createInboundRoot() { * @throws ResourceException */ @Test - public void test() throws IOException, ResourceException { + void test() throws IOException, ResourceException { Request request = createRequest(Method.GET); Response response = handle(request); assertEquals(Status.SUCCESS_OK, response.getStatus()); diff --git a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java index 173d4ddf89..7accc54e4e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/jetty/resource/AnnotatedResource11TestCase.java @@ -27,7 +27,7 @@ * * @author Thierry Boileau */ -public class AnnotatedResource11TestCase extends JettyConnectorTestCase { +class AnnotatedResource11TestCase extends JettyConnectorTestCase { protected Application createApplication(final String path) { return new Application() { @@ -47,7 +47,7 @@ public Restlet createInboundRoot() { * @throws ResourceException */ @Test - public void test() throws IOException, ResourceException { + void test() throws IOException, ResourceException { Request request = createRequest(Method.GET); Response response = handle(request); assertEquals(Status.SUCCESS_OK, response.getStatus()); diff --git a/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java index 1675ff355e..4fecc573d2 100644 --- a/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/resource/AnnotationUtilsTestCase.java @@ -22,7 +22,7 @@ * * @author Valdis Rigdon */ -public class AnnotationUtilsTestCase { +class AnnotationUtilsTestCase { public interface IChild extends IParent {} @@ -36,7 +36,7 @@ public interface IParent { } @Test - public void testGetAnnotationsWithGenericParameterType() { + void testGetAnnotationsWithGenericParameterType() { List infos = AnnotationUtils.getInstance().getAnnotations(IChild.class); assertEquals(4, infos.size(), "Wrong count: " + infos); boolean found = false; @@ -57,7 +57,7 @@ public void testGetAnnotationsWithGenericParameterType() { } @Test - public void testGetAnnotationsWithGenericReturnType() { + void testGetAnnotationsWithGenericReturnType() { List infos = AnnotationUtils.getInstance().getAnnotations(IChild.class); assertEquals(4, infos.size(), "Wrong count: " + infos); boolean found = false; diff --git a/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java index 9df0c37600..a8ca484f5b 100644 --- a/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/util/HtmlEncodingTestCase.java @@ -21,7 +21,7 @@ * * @author Thierry Boileau */ -public class HtmlEncodingTestCase { +class HtmlEncodingTestCase { private static final String MIXED_ENCODED_STRING = "azertyéè@à&%ù€®®®&&&;&testest"; @@ -38,7 +38,7 @@ static Stream htmlEscapeArgumentsProvider() { @ParameterizedTest @MethodSource("htmlEscapeArgumentsProvider") - public void testEncoding(final String toEscape, final String expected) { + void testEncoding(final String toEscape, final String expected) { assertEquals( expected, StringUtils.htmlEscape(toEscape), "Error while escaping " + toEscape); } @@ -54,7 +54,7 @@ static Stream htmlUnescapeArgumentsProvider() { @ParameterizedTest @MethodSource("htmlUnescapeArgumentsProvider") - public void testDecoding(final String toUnescape, final String expected) { + void testDecoding(final String toUnescape, final String expected) { assertEquals( expected, StringUtils.htmlUnescape(toUnescape), diff --git a/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java index 927433aac6..9cd3c750cb 100644 --- a/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/AppendableRepresentationTestCase.java @@ -17,10 +17,10 @@ * * @author Jerome Louvel */ -public class AppendableRepresentationTestCase { +class AppendableRepresentationTestCase { @Test - public void testAppendable() throws Exception { + void testAppendable() throws Exception { AppendableRepresentation ar = new AppendableRepresentation(); ar.append("abcd"); ar.append("efgh"); diff --git a/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java index 2b3804f5cd..7503c4d5d1 100644 --- a/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/DigesterRepresentationTestCase.java @@ -34,7 +34,7 @@ * * @author Thierry Boileau */ -public class DigesterRepresentationTestCase { +class DigesterRepresentationTestCase { /** Component used for the tests. */ private Component component; @@ -61,7 +61,7 @@ protected void tearDownEach() throws Exception { } @Test - public void checkDigestSentByClient() { + void checkDigestSentByClient() { Client client = new Client(Protocol.HTTP); Request request = new Request(Method.PUT, "http://localhost:" + serverPort + "/checkRequestEntity"); @@ -72,7 +72,7 @@ public void checkDigestSentByClient() { } @Test - public void checkDigestSentByServer() { + void checkDigestSentByServer() { Client client = new Client(Protocol.HTTP); Request request = new Request(Method.GET, "http://localhost:" + serverPort + "/checkResponseEntity"); diff --git a/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java index 95925707eb..7bde190854 100644 --- a/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java +++ b/org.restlet/src/test/java/org/restlet/representation/FileRepresentationTestCase.java @@ -37,7 +37,7 @@ * * @author Kevin Conaway */ -public class FileRepresentationTestCase { +class FileRepresentationTestCase { private Component component; @@ -67,7 +67,7 @@ protected void tearDownEach() throws Exception { } @Test - public void testConstructors() { + void testConstructors() { final File file = new File("test.txt"); final FileRepresentation r = new FileRepresentation(file, MediaType.TEXT_PLAIN); @@ -78,7 +78,7 @@ public void testConstructors() { } @Test - public void testFileName() throws Exception { + void testFileName() throws Exception { Application application = new Application() { @Override diff --git a/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java b/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java index 7da675cfeb..1e1740f532 100644 --- a/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/AbstractFilterTestCase.java @@ -55,7 +55,7 @@ public abstract class AbstractFilterTestCase { /** Test Restlet instance attaching/detaching. */ @Test - public void testAttachDetachInstance() throws Exception { + void testAttachDetachInstance() throws Exception { final Filter filter = getFilter(); assertFalse(filter.hasNext()); filter.setNext(getRestlet()); @@ -72,7 +72,7 @@ public void testAttachDetachInstance() throws Exception { /** Test not started Filter. */ @Test - public void testIllegalStartedState() { + void testIllegalStartedState() { final Filter filter = getFilter(); filter.setNext(getRestlet()); assertTrue(filter.hasNext()); @@ -93,7 +93,7 @@ public void testIllegalStartedState() { /** Test with null target. */ @Test - public void testIllegalTarget() throws Exception { + void testIllegalTarget() throws Exception { final Filter filter = getFilter(); filter.start(); assertTrue(filter.isStarted()); diff --git a/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java b/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java index 50e315fcfc..3ccd668b33 100644 --- a/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/RedirectTestCase.java @@ -30,7 +30,7 @@ * * @author Jerome Louvel */ -public class RedirectTestCase { +class RedirectTestCase { private static final int TEST_PORT = 1337; @@ -54,7 +54,7 @@ public void tearDown() { /** Tests the cookies parsing. */ @Test - public void testRedirect() throws Exception { + void testRedirect() throws Exception { // Create components final Component clientComponent = new Component(); final Component proxyComponent = new Component(); diff --git a/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java b/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java index f1fcb25c68..163a00446f 100644 --- a/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/RouteListTestCase.java @@ -23,7 +23,7 @@ * * @author Kevin Conaway */ -public class RouteListTestCase { +class RouteListTestCase { static class MockScoringRoute extends Route { int score; @@ -40,7 +40,7 @@ public float score(Request request, Response response) { } @Test - public void testGetLast() { + void testGetLast() { final RouteList list = new RouteList(); assertNull(list.getLast(null, null, 1f)); @@ -56,7 +56,7 @@ public void testGetLast() { } @Test - public void testGetNext() { + void testGetNext() { final RouteList list = new RouteList(); assertNull(list.getNext(null, null, 1f)); @@ -77,7 +77,7 @@ public void testGetNext() { } @Test - public void testGetRandom() { + void testGetRandom() { final RouteList list = new RouteList(); assertNull(list.getRandom(null, null, 1f)); diff --git a/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java b/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java index 879779a932..b7590d78bc 100644 --- a/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java +++ b/org.restlet/src/test/java/org/restlet/routing/ValidatorTestCase.java @@ -20,10 +20,10 @@ * * @author Jerome Louvel */ -public class ValidatorTestCase { +class ValidatorTestCase { @Test - public void testRequired() { + void testRequired() { // Create mock call Request rq = new Request(); Response rs = new Response(rq); @@ -45,7 +45,7 @@ public void testRequired() { } @Test - public void testFormat() { + void testFormat() { // Create mock call Request rq = new Request(); Response rs = new Response(rq); diff --git a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java index d9cc0225c4..4341c0005c 100644 --- a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java @@ -40,7 +40,7 @@ * @author Stian Soiland * @author Jerome Louvel */ -public class HttpBasicTestCase { +class HttpBasicTestCase { public static class AuthenticatedRestlet extends Restlet { @Override @@ -128,7 +128,7 @@ class TestHttpBasicServer { private Client client; @Test - public void HttpBasicNone() throws Exception { + void HttpBasicNone() throws Exception { final Response response = client.handle(request); assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); } diff --git a/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java b/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java index 1c67b33722..1a6053993d 100644 --- a/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java +++ b/org.restlet/src/test/java/org/restlet/security/MemoryRealmTest.java @@ -13,10 +13,10 @@ import org.junit.jupiter.api.Test; -public class MemoryRealmTest { +class MemoryRealmTest { @Test - public void whenUnmappingAGroupAndRoleFromAMemoryRealmThenMappingIsDropped() { + void whenUnmappingAGroupAndRoleFromAMemoryRealmThenMappingIsDropped() { // given a Memory Realm, a Group and a Role MemoryRealm memoryRealm = new MemoryRealm(); Group group = new Group(); @@ -35,7 +35,7 @@ public void whenUnmappingAGroupAndRoleFromAMemoryRealmThenMappingIsDropped() { } @Test - public void whenUnmappingAUserAndRoleFromAMemoryRealmThenMappingIsDropped() { + void whenUnmappingAUserAndRoleFromAMemoryRealmThenMappingIsDropped() { // given a Memory Realm, a Group and a Role MemoryRealm memoryRealm = new MemoryRealm(); User user = new User(); diff --git a/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java b/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java index d2083ec02c..17988cc013 100644 --- a/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/RoleTestCase.java @@ -20,10 +20,10 @@ * @author Thierry Boileau * @author Jerome Louvel */ -public class RoleTestCase { +class RoleTestCase { @Test - public void testRoleEquality() { + void testRoleEquality() { Application app1 = new Application(); Application app2 = new Application(); diff --git a/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java index 7f37986970..87a67b41b5 100644 --- a/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/ConnegServiceTestCase.java @@ -25,10 +25,10 @@ * * @author Jerome Louvel */ -public class ConnegServiceTestCase { +class ConnegServiceTestCase { @Test - public void testStrict() { + void testStrict() { List variants = new ArrayList<>(); Variant variant = new Variant(MediaType.APPLICATION_XML); variants.add(variant); diff --git a/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java index 71b43ab08f..6be478b3f0 100644 --- a/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/MetadataServiceTestCase.java @@ -18,10 +18,10 @@ * * @author Jerome Louvel */ -public class MetadataServiceTestCase { +class MetadataServiceTestCase { @Test - public void testStrict() { + void testStrict() { MetadataService ms = new MetadataService(); MediaType ma = ms.getMediaType("ma"); assertNull(ma); diff --git a/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java index 1eae214823..6290b899b6 100644 --- a/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/StatusServiceTestCase.java @@ -33,7 +33,7 @@ * @author Jerome Louvel */ @SuppressWarnings("unchecked") -public class StatusServiceTestCase { +class StatusServiceTestCase { StatusService statusService = new StatusService(); @@ -52,7 +52,7 @@ void cleanUp() { } @Test - public void shouldConvertToStatus() { + void shouldConvertToStatus() { AnnotatedNotSerializableException statusException = new AnnotatedNotSerializableException("test message", 50); @@ -63,7 +63,7 @@ public void shouldConvertToStatus() { } @Test - public void exceptionShouldNotBeSerialized() throws IOException { + void exceptionShouldNotBeSerialized() throws IOException { AnnotatedNotSerializableException statusException = new AnnotatedNotSerializableException("test message", 50); Status status = new Status(400, statusException); @@ -83,7 +83,7 @@ public void exceptionShouldNotBeSerialized() throws IOException { } @Test - public void shouldSerializeAnnotatedException() throws IOException { + void shouldSerializeAnnotatedException() throws IOException { Status status = new Status(400, AnnotatedSerializableException.withoutCause("test message", 50)); @@ -98,7 +98,7 @@ public void shouldSerializeAnnotatedException() throws IOException { } @Test - public void shouldSerializeAnnotatedExceptionWithCause() throws IOException { + void shouldSerializeAnnotatedExceptionWithCause() throws IOException { Status status = new Status(400, AnnotatedSerializableException.withCause("test message", 50)); @@ -114,7 +114,7 @@ public void shouldSerializeAnnotatedExceptionWithCause() throws IOException { } @Test - public void shouldSerializeAnnotatedExceptionWithStackTrace() throws IOException { + void shouldSerializeAnnotatedExceptionWithStackTrace() throws IOException { Status status = new Status(400, AnnotatedSerializableException.withCause("test message", 50)); diff --git a/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java b/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java index 3f307908f5..803091c722 100644 --- a/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/service/UserAgentTunnelFilterTestCase.java @@ -23,7 +23,7 @@ import org.restlet.routing.Router; /** Tests cases for the tunneling of preferences based on user agent. */ -public class UserAgentTunnelFilterTestCase { +class UserAgentTunnelFilterTestCase { private Application application; @@ -57,7 +57,7 @@ public Restlet createInboundRoot() { } @Test - public void testTunnelOff() { + void testTunnelOff() { this.application.getTunnelService().setUserAgentTunnel(false); Request request = createRequest(); Response response = new Response(request); @@ -67,7 +67,7 @@ public void testTunnelOff() { } @Test - public void testTunnelOn() { + void testTunnelOn() { this.application.getTunnelService().setUserAgentTunnel(true); Request request = createRequest(); Response response = new Response(request); From e9cd1deeafe2db6531d2a7e0378e0cacba7c1b99 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Mon, 6 Apr 2026 12:22:16 +0200 Subject: [PATCH 27/30] Take into account Sonar comments --- .../org/restlet/ext/gson/GsonConverter.java | 3 +- .../org/restlet/ext/gson/GsonTestCase.java | 2 +- .../restlet/ext/jackson/JacksonConverter.java | 33 ++++---- .../ext/thymeleaf/ThymeleafConverter.java | 7 +- .../ext/velocity/VelocityConverter.java | 7 +- .../org/restlet/ext/xml/XmlConverter.java | 46 +++++------ .../restlet/ext/xml/XmlRepresentation.java | 78 +++++++++++-------- .../src/main/java/org/restlet/Response.java | 4 +- .../org/restlet/data/ChallengeRequest.java | 3 +- .../org/restlet/data/ChallengeResponse.java | 3 +- .../java/org/restlet/data/CookieSetting.java | 1 + .../java/org/restlet/data/Expectation.java | 2 +- .../main/java/org/restlet/data/Language.java | 6 +- .../engine/application/FlexibleConneg.java | 2 +- .../engine/application/StrictConneg.java | 7 +- .../engine/connector/ConnectorHelper.java | 10 ++- .../engine/connector/HttpsServerHelper.java | 4 +- .../engine/connector/JettyServerHelper.java | 3 +- .../converter/StatusInfoHtmlConverter.java | 4 +- .../engine/local/ClapClientHelper.java | 26 +++---- .../java/org/restlet/engine/local/Entity.java | 43 +++++----- .../engine/local/EntityClientHelper.java | 10 +-- .../engine/local/FileClientHelper.java | 68 ++++++++-------- .../engine/local/RiapServerHelper.java | 2 +- .../main/java/org/restlet/routing/Filter.java | 4 +- .../org/restlet/security/MemoryRealm.java | 22 +++--- .../test/java/org/restlet/CallTestCase.java | 20 ----- .../engine/connector/GetChunkedTestCase.java | 6 +- .../connector/GetQueryParamTestCase.java | 1 + .../restlet/engine/connector/GetTestCase.java | 1 + 30 files changed, 211 insertions(+), 217 deletions(-) diff --git a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java index 8397361ca0..7a53c4b848 100644 --- a/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java +++ b/org.restlet.ext.gson/src/main/java/org/restlet/ext/gson/GsonConverter.java @@ -136,8 +136,7 @@ public T toObject(Representation source, Class target, Resource resource) } @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) - throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) { Representation result = null; if (source instanceof GsonRepresentation) { diff --git a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java index 2f6af0444b..a07d1938a8 100644 --- a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java +++ b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java @@ -209,7 +209,7 @@ final void testToObjectRepresentationClassOfTResource() throws IOException { } @Test - final void testToRepresentationObjectVariantResource() throws IOException { + final void testToRepresentationObjectVariantResource() { Variant v = new Variant(MediaType.APPLICATION_JSON); Representation rep = gsonConverter.toRepresentation(user, v, null); assertNotNull(rep); diff --git a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java index 562618c1e5..5feae97ece 100644 --- a/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java +++ b/org.restlet.ext.jackson/src/main/java/org/restlet/ext/jackson/JacksonConverter.java @@ -60,7 +60,7 @@ public class JacksonConverter extends ConverterHelper { * @return The marshaling {@link JacksonRepresentation}. */ protected JacksonRepresentation create(MediaType mediaType, T source) { - return new JacksonRepresentation(mediaType, source); + return new JacksonRepresentation<>(mediaType, source); } /** @@ -72,7 +72,7 @@ protected JacksonRepresentation create(MediaType mediaType, T source) { * @return The unmarshaling {@link JacksonRepresentation}. */ protected JacksonRepresentation create(Representation source, Class objectClass) { - return new JacksonRepresentation(source, objectClass); + return new JacksonRepresentation<>(source, objectClass); } @Override @@ -125,18 +125,16 @@ protected boolean isCompatible(Variant variant) { @Override public float score(Object source, Variant target, Resource resource) { - float result = -1.0F; + final float result; if (source instanceof JacksonRepresentation) { result = 1.0F; + } else if (target == null) { + result = 0.5F; + } else if (isCompatible(target)) { + result = 0.8F; } else { - if (target == null) { - result = 0.5F; - } else if (isCompatible(target)) { - result = 0.8F; - } else { - result = 0.5F; - } + result = 0.5F; } return result; @@ -144,7 +142,7 @@ public float score(Object source, Variant target, Resource resource) { @Override public float score(Representation source, Class target, Resource resource) { - float result = -1.0F; + final float result; if (source instanceof JacksonRepresentation) { result = 1.0F; @@ -152,6 +150,8 @@ public float score(Representation source, Class target, Resource resource result = 1.0F; } else if (isCompatible(source)) { result = 0.8F; + } else { + result = -1.0F; } return result; @@ -161,7 +161,7 @@ public float score(Representation source, Class target, Resource resource @Override public T toObject(Representation source, Class target, Resource resource) throws IOException { - Object result = null; + final Object result; // The source for the Jackson conversion JacksonRepresentation jacksonSource = null; @@ -171,13 +171,14 @@ public T toObject(Representation source, Class target, Resource resource) jacksonSource = create(source, target); } - if (jacksonSource != null) { - // Handle the conversion + if (jacksonSource != null) { // Handle the conversion if ((target != null) && JacksonRepresentation.class.isAssignableFrom(target)) { result = jacksonSource; } else { result = jacksonSource.getObject(); } + } else { + result = null; } return (T) result; @@ -187,8 +188,8 @@ public T toObject(Representation source, Class target, Resource resource) public Representation toRepresentation(Object source, Variant target, Resource resource) { Representation result = null; - if (source instanceof JacksonRepresentation) { - result = (JacksonRepresentation) source; + if (source instanceof JacksonRepresentation jacksonRepresentation) { + result = jacksonRepresentation; } else { if (target.getMediaType() == null) { target.setMediaType(MediaType.APPLICATION_JSON); diff --git a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java index 05e93327f7..c7d3596925 100644 --- a/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java +++ b/org.restlet.ext.thymeleaf/src/main/java/org/restlet/ext/thymeleaf/ThymeleafConverter.java @@ -8,7 +8,6 @@ */ package org.restlet.ext.thymeleaf; -import java.io.IOException; import java.util.List; import java.util.Locale; import org.restlet.data.MediaType; @@ -61,14 +60,12 @@ public float score(Representation source, Class target, Resource resource } @Override - public T toObject(Representation source, Class target, Resource resource) - throws IOException { + public T toObject(Representation source, Class target, Resource resource) { return null; } @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) - throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) { if (source instanceof ITemplateResource iTemplateResource) { Locale locale = Locale.getDefault(); diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java index 4bde848a59..9cbdf6768f 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/VelocityConverter.java @@ -8,7 +8,6 @@ */ package org.restlet.ext.velocity; -import java.io.IOException; import java.util.List; import org.apache.velocity.Template; import org.restlet.data.MediaType; @@ -60,14 +59,12 @@ public float score(Representation source, Class target, Resource resource } @Override - public T toObject(Representation source, Class target, Resource resource) - throws IOException { + public T toObject(Representation source, Class target, Resource resource) { return null; } @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) - throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) { if (source instanceof Template template) { TemplateRepresentation tr = new TemplateRepresentation(template, target.getMediaType()); diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java index af9b6ecb88..feab246fd7 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlConverter.java @@ -111,36 +111,38 @@ public float score(Representation source, Class target, Resource resource @Override public T toObject(Representation source, Class target, Resource resource) throws IOException { + if (target == null) { + return null; + } - Object result = null; - if (target != null) { - if (Document.class.isAssignableFrom(target)) { - if (source instanceof DomRepresentation) { - result = ((DomRepresentation) source).getDocument(); - } else { - result = new DomRepresentation(source).getDocument(); - } - } else if (DomRepresentation.class.isAssignableFrom(target)) { - if (source instanceof DomRepresentation) { - result = source; - } else { - result = new DomRepresentation(source); - } - } else if (SaxRepresentation.class.isAssignableFrom(target)) { - if (source instanceof SaxRepresentation) { - result = source; - } else { - result = new SaxRepresentation(source); - } + final Object result; + if (Document.class.isAssignableFrom(target)) { + if (source instanceof DomRepresentation domRepresentation) { + result = domRepresentation.getDocument(); + } else { + result = new DomRepresentation(source).getDocument(); + } + } else if (DomRepresentation.class.isAssignableFrom(target)) { + if (source instanceof DomRepresentation) { + result = source; + } else { + result = new DomRepresentation(source); } + } else if (SaxRepresentation.class.isAssignableFrom(target)) { + if (source instanceof SaxRepresentation) { + result = source; + } else { + result = new SaxRepresentation(source); + } + } else { + return null; } return (T) result; } @Override - public Representation toRepresentation(Object source, Variant target, Resource resource) - throws IOException { + public Representation toRepresentation(Object source, Variant target, Resource resource) { Representation result = null; if (source instanceof Document document) { diff --git a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java index 71921ae941..d65139fd37 100644 --- a/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java +++ b/org.restlet.ext.xml/src/main/java/org/restlet/ext/xml/XmlRepresentation.java @@ -20,6 +20,9 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.SchemaFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; import org.restlet.Context; @@ -70,10 +73,10 @@ public abstract class XmlRepresentation extends WriterRepresentation */ private static void appendTextContent(Node node, StringBuilder sb) { switch (node.getNodeType()) { - case Node.TEXT_NODE: - case Node.CDATA_SECTION_NODE: - case Node.COMMENT_NODE: - case Node.PROCESSING_INSTRUCTION_NODE: + case Node.TEXT_NODE, + Node.CDATA_SECTION_NODE, + Node.COMMENT_NODE, + Node.PROCESSING_INSTRUCTION_NODE: sb.append(node.getNodeValue()); break; case Node.ENTITY_REFERENCE_NODE: @@ -82,10 +85,10 @@ private static void appendTextContent(Node node, StringBuilder sb) { sb.append((char) ch); } break; - case Node.ELEMENT_NODE: - case Node.ATTRIBUTE_NODE: - case Node.ENTITY_NODE: - case Node.DOCUMENT_FRAGMENT_NODE: + case Node.ELEMENT_NODE, + Node.ATTRIBUTE_NODE, + Node.ENTITY_NODE, + Node.DOCUMENT_FRAGMENT_NODE: for (int i = 0; i < node.getChildNodes().getLength(); i++) { appendTextContent(node.getChildNodes().item(i), sb); } @@ -102,14 +105,11 @@ private static void appendTextContent(Node node, StringBuilder sb) { * @return A SAX source. * @throws IOException */ - public static javax.xml.transform.sax.SAXSource getSaxSource(Representation xmlRepresentation) - throws IOException { - javax.xml.transform.sax.SAXSource result = null; + public static SAXSource getSaxSource(Representation xmlRepresentation) throws IOException { + SAXSource result = null; if (xmlRepresentation != null) { - result = - new javax.xml.transform.sax.SAXSource( - new InputSource(xmlRepresentation.getStream())); + result = new SAXSource(new InputSource(xmlRepresentation.getStream())); if (xmlRepresentation.getLocationRef() != null) { result.setSystemId(xmlRepresentation.getLocationRef().getTargetRef().toString()); @@ -124,17 +124,16 @@ public static javax.xml.transform.sax.SAXSource getSaxSource(Representation xmlR * * @return The wrapped schema. * @throws IOException + * @throws SAXException */ private static javax.xml.validation.Schema getSchema(Representation schemaRepresentation) - throws Exception { + throws IOException, SAXException { javax.xml.validation.Schema result = null; if (schemaRepresentation != null) { - final javax.xml.transform.stream.StreamSource streamSource = - new javax.xml.transform.stream.StreamSource(schemaRepresentation.getStream()); + final StreamSource streamSource = new StreamSource(schemaRepresentation.getStream()); result = - javax.xml.validation.SchemaFactory.newInstance( - getSchemaLanguageUri(schemaRepresentation)) + SchemaFactory.newInstance(getSchemaLanguageUri(schemaRepresentation)) .newSchema(streamSource); } @@ -275,6 +274,11 @@ protected XmlRepresentation(MediaType mediaType, long expectedSize) { this.schema = null; } + @Override + public boolean equals(final Object obj) { + return super.equals(obj); + } + /** * Evaluates an XPath expression as a boolean. If the evaluation fails, null will be returned. * @@ -299,7 +303,7 @@ protected Document getDocument() throws Exception { * @return A document builder properly configured. */ protected DocumentBuilder getDocumentBuilder() throws IOException { - DocumentBuilder result = null; + DocumentBuilder result; try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -317,12 +321,7 @@ protected DocumentBuilder getDocumentBuilder() throws IOException { dbf.setIgnoringComments(isIgnoringComments()); dbf.setIgnoringElementContentWhitespace(isIgnoringExtraWhitespaces()); - try { - dbf.setXIncludeAware(isXIncludeAware()); - } catch (UnsupportedOperationException uoe) { - Context.getCurrentLogger() - .log(Level.FINE, "The JAXP parser doesn't support XInclude.", uoe); - } + setXIncludeAwareAndMuteExceptions(dbf); javax.xml.validation.Schema xsd = getSchema(); if (xsd != null) { @@ -339,6 +338,15 @@ protected DocumentBuilder getDocumentBuilder() throws IOException { return result; } + private void setXIncludeAwareAndMuteExceptions(final DocumentBuilderFactory dbf) { + try { + dbf.setXIncludeAware(isXIncludeAware()); + } catch (UnsupportedOperationException uoe) { + Context.getCurrentLogger() + .log(Level.FINE, "The JAXP parser doesn't support XInclude.", uoe); + } + } + /** * Returns a DOM source. * @@ -404,7 +412,7 @@ public Map getNamespaces() { return this.namespaces; } - /** {@inheritDoc javax.xml.namespace.NamespaceContext#getNamespaceURI(java.lang.String} */ + /** {@inheritDoc} */ public String getNamespaceURI(String prefix) { return (this.namespaces == null) ? null : this.namespaces.get(prefix); } @@ -440,7 +448,7 @@ public Double getNumber(String expression) { return (Double) internalEval(expression, javax.xml.xpath.XPathConstants.NUMBER); } - /** {@inheritDoc javax.xml.namespace.NamespaceContext#getPrefix(java.lang.String} */ + /** {@inheritDoc} */ public String getPrefix(String namespaceURI) { String result = null; @@ -457,7 +465,7 @@ public String getPrefix(String namespaceURI) { return result; } - /** {@inheritDoc javax.xml.namespace.NamespaceContext#getPrefixes(java.lang.String} */ + /** {@inheritDoc} */ public Iterator getPrefixes(String namespaceURI) { final List result = new ArrayList<>(); @@ -478,7 +486,7 @@ public Iterator getPrefixes(String namespaceURI) { * @return A SAX source. * @throws IOException */ - public javax.xml.transform.sax.SAXSource getSaxSource() throws IOException { + public SAXSource getSaxSource() throws IOException { return getSaxSource(this); } @@ -498,9 +506,8 @@ public javax.xml.validation.Schema getSchema() { * @return A stream of XML markup. * @throws IOException */ - public javax.xml.transform.stream.StreamSource getStreamSource() throws IOException { - final javax.xml.transform.stream.StreamSource result = - new javax.xml.transform.stream.StreamSource(getStream()); + public StreamSource getStreamSource() throws IOException { + final StreamSource result = new StreamSource(getStream()); if (getLocationRef() != null) { result.setSystemId(getLocationRef().getTargetRef().toString()); @@ -548,6 +555,11 @@ private Object internalEval(String expression, javax.xml.namespace.QName returnT } } + @Override + public int hashCode() { + return super.hashCode(); + } + /** * Indicates if the parser should be coalescing text. If true, the parser will convert CDATA * nodes to text nodes and append it to the adjacent (if any) text node. By default, the value diff --git a/org.restlet/src/main/java/org/restlet/Response.java b/org.restlet/src/main/java/org/restlet/Response.java index f13a9cf05b..581f02e71e 100644 --- a/org.restlet/src/main/java/org/restlet/Response.java +++ b/org.restlet/src/main/java/org/restlet/Response.java @@ -26,14 +26,14 @@ /** * Generic response sent by server connectors. It is then received by client connectors. Responses - * are uniform across all types of connectors, protocols and components. + * are uniform across all types of connectors, protocols, and components. * * @see org.restlet.Request * @see org.restlet.Uniform * @author Jerome Louvel */ public class Response extends Message { - private static final ThreadLocal CURRENT = new ThreadLocal(); + private static final ThreadLocal CURRENT = new ThreadLocal<>(); /** * Returns the response associated with the current thread. diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java index 0229901fe6..5c1f2a9ab1 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeRequest.java @@ -65,7 +65,8 @@ public boolean equals(final Object obj) { return false; } - return Objects.equals(getParameters(), that.getParameters()) + return super.equals(that) + && Objects.equals(getParameters(), that.getParameters()) && Objects.equals(getRealm(), that.getRealm()) && Objects.equals(getScheme(), that.getScheme()); } diff --git a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java index 4ebaf8be23..d19a4afae5 100644 --- a/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java +++ b/org.restlet/src/main/java/org/restlet/data/ChallengeResponse.java @@ -248,7 +248,8 @@ public boolean equals(Object obj) { if (!(obj instanceof ChallengeResponse that)) { return false; } - return Objects.equals(getRawValue(), that.getRawValue()) + return super.equals(that) + && Objects.equals(getRawValue(), that.getRawValue()) && Objects.equals(getIdentifier(), that.getIdentifier()) && Objects.equals(getScheme(), that.getScheme()) && Arrays.equals(getSecret(), that.getSecret()); diff --git a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java index bf01add5b0..9cfe6e59e6 100644 --- a/org.restlet/src/main/java/org/restlet/data/CookieSetting.java +++ b/org.restlet/src/main/java/org/restlet/data/CookieSetting.java @@ -152,6 +152,7 @@ public boolean equals(Object obj) { return super.equals(obj) && this.maxAge == that.maxAge && this.secure == that.secure + && accessRestricted == that.accessRestricted && Objects.equals(this.comment, that.comment); } diff --git a/org.restlet/src/main/java/org/restlet/data/Expectation.java b/org.restlet/src/main/java/org/restlet/data/Expectation.java index 6e098048d5..924a81ca44 100644 --- a/org.restlet/src/main/java/org/restlet/data/Expectation.java +++ b/org.restlet/src/main/java/org/restlet/data/Expectation.java @@ -77,7 +77,7 @@ public boolean equals(Object obj) { return Objects.equals(getName(), that.getName()) && Objects.equals(getValue(), that.getValue()) - && getParameters().equals(that.getParameters()); + && Objects.equals(getParameters(), that.getParameters()); } /** diff --git a/org.restlet/src/main/java/org/restlet/data/Language.java b/org.restlet/src/main/java/org/restlet/data/Language.java index cc8cd1badf..8ab77dddb2 100644 --- a/org.restlet/src/main/java/org/restlet/data/Language.java +++ b/org.restlet/src/main/java/org/restlet/data/Language.java @@ -15,8 +15,8 @@ /** * Language used in representations and preferences. A language tag is composed of one or more - * parts: A primary language tag and a possibly empty series of subtags. When formatted as a string, - * parts are separated by hyphens. + * parts: A primary language tag and a possibly empty series of sub-tags. When formatted as a + * string, hyphens separate parts. * * @author Jerome Louvel */ @@ -115,7 +115,7 @@ public boolean equals(final Object obj) { @Override public Language getParent() { - Language result = null; + final Language result; if ((getSubTags() != null) && !getSubTags().isEmpty()) { result = Language.valueOf(getPrimaryTag()); diff --git a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java index dd1efbe768..dc7c0dd79e 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/FlexibleConneg.java @@ -132,7 +132,7 @@ protected List> getEncodingPrefs() { * @param * @param userPreferences The user preferences to enrich. * @param defaultValue The default value. - * @param allValue The ALL value. + * @param allValue The 'ALL' value. * @return The enriched user preferences. */ @SuppressWarnings("unchecked") diff --git a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java index be1cdffb77..4b6a06934a 100644 --- a/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java +++ b/org.restlet/src/main/java/org/restlet/engine/application/StrictConneg.java @@ -222,7 +222,7 @@ public float scoreMediaType(MediaType mediaType) { } /** - * Scores a list of metadata relatively to enriched client preferences. + * Scores a list of metadata relatively enriched client preferences. * * @param metadataList The list of metadata to score. * @return The score. @@ -312,9 +312,8 @@ public float scoreVariant(Variant variant) { ((languageScore * 4.0F) + (mediaTypeScore * 3.0F) + (characterSetScore * 2.0F) - + (encodingScore * 1.0F) + (annotationScore * 2.0F)) - / 12.0F; + + (encodingScore) / 12.0F; // Take into account the affinity with the input // entity result *= variantInfo.getInputScore(); @@ -324,7 +323,7 @@ public float scoreVariant(Variant variant) { ((languageScore * 4.0F) + (mediaTypeScore * 3.0F) + (characterSetScore * 2.0F) - + (encodingScore * 1.0F)) + + (encodingScore)) / 10.0F; } } diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java index 84ec7396e7..8e45567ce0 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/ConnectorHelper.java @@ -10,9 +10,11 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import org.restlet.Application; import org.restlet.Connector; import org.restlet.data.Protocol; import org.restlet.engine.RestletHelper; +import org.restlet.service.ConnectorService; /** * Base connector helper. @@ -26,14 +28,14 @@ public abstract class ConnectorHelper extends RestletHelper * * @return The connector service associated with a request. */ - public static org.restlet.service.ConnectorService getConnectorService() { - org.restlet.service.ConnectorService result = null; - org.restlet.Application application = org.restlet.Application.getCurrent(); + public static ConnectorService getConnectorService() { + final ConnectorService result; + Application application = Application.getCurrent(); if (application != null) { result = application.getConnectorService(); } else { - result = new org.restlet.service.ConnectorService(); + result = new ConnectorService(); } return result; diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java index 64ebdd6610..d04a227c92 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/HttpsServerHelper.java @@ -30,6 +30,7 @@ import org.restlet.data.Protocol; import org.restlet.engine.security.RestletSslContextFactoryServer; import org.restlet.engine.ssl.DefaultSslContextFactory; +import org.restlet.engine.ssl.SslUtils; /** * Jetty HTTPS server connector. Here is the list of additional parameters that are supported. They @@ -189,8 +190,7 @@ private Path getHttp3PemWorkDirectoryPath() { private SslContextFactory.Server getServerSslContextFactory() { try { - return new RestletSslContextFactoryServer( - org.restlet.engine.ssl.SslUtils.getSslContextFactory(this)); + return new RestletSslContextFactoryServer(SslUtils.getSslContextFactory(this)); } catch (RuntimeException e) { getLogger().log(Level.WARNING, "Unable to create the Jetty SSL context factory", e); throw e; diff --git a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java index 5c8ba81d62..b0c52c7535 100644 --- a/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/connector/JettyServerHelper.java @@ -352,8 +352,7 @@ private org.eclipse.jetty.server.Server createServer() { // Connectors createConnectors(jettyServer).forEach(jettyServer::addConnector); - // Low-resource monitor (must be created after connectors have been - // added) + // Low-resource monitor (must be created after connectors have been added) LowResourceMonitor lowResourceMonitor = createLowResourceMonitor(jettyServer); jettyServer.addBean(lowResourceMonitor); diff --git a/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java b/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java index feb3969893..0493ef6faf 100644 --- a/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java +++ b/org.restlet/src/main/java/org/restlet/engine/converter/StatusInfoHtmlConverter.java @@ -160,11 +160,13 @@ public T toObject(Representation source, Class target, Resource resource) @Override public Representation toRepresentation(Object source, Variant target, Resource resource) throws IOException { - Representation result = null; + final Representation result; if (source != null && StatusInfo.class.isAssignableFrom(source.getClass())) { StatusInfo si = (StatusInfo) source; result = toHtml(si); + } else { + result = null; } return result; diff --git a/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java index 9e4fd361b4..9faf52af62 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/ClapClientHelper.java @@ -83,14 +83,12 @@ protected void handleClassLoader(Request request, Response response, ClassLoader // The ClassLoader returns a directory listing in some cases. // As this listing is partial, it is of little value in the context // of the CLAP client, so we have to ignore them. - if (url != null) { - if (url.getProtocol().equals("file")) { - File file = new File(url.getFile()); - modificationDate = new Date(file.lastModified()); + if (url != null && url.getProtocol().equals("file")) { + File file = new File(url.getFile()); + modificationDate = new Date(file.lastModified()); - if (file.isDirectory()) { - url = null; - } + if (file.isDirectory()) { + url = null; } } @@ -103,11 +101,9 @@ protected void handleClassLoader(Request request, Response response, ClassLoader InputStream inputStream = url.openStream(); // check for empty input stream on jar directories - if (url.getProtocol().equals("jar")) { - if (inputStream.available() == 0) { - response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return; - } + if (url.getProtocol().equals("jar") && inputStream.available() == 0) { + response.setStatus(Status.CLIENT_ERROR_NOT_FOUND); + return; } Representation output = @@ -126,8 +122,10 @@ protected void handleClassLoader(Request request, Response response, ClassLoader } // Update the metadata based on file extensions - String name = path.substring(path.lastIndexOf('/') + 1); - Entity.updateMetadata(name, output, true, getMetadataService()); + if (path != null) { + String name = path.substring(path.lastIndexOf('/') + 1); + Entity.updateMetadata(name, output, true, getMetadataService()); + } // Update the response response.setEntity(output); diff --git a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java index 4b556c4d50..279f509b44 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/Entity.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/Entity.java @@ -134,28 +134,31 @@ public static void updateMetadata( if (current != null) { // Metadata extension detected - if (current instanceof MediaType mediaType) { - variant.setMediaType(mediaType); - } else if (current instanceof CharacterSet characterSet) { - variant.setCharacterSet(characterSet); - } else if (current instanceof Encoding encoding) { - // Do we need to add this metadata? - boolean found = false; - for (int i = 0; !found && i < variant.getEncodings().size(); i++) { - found = current.includes(variant.getEncodings().get(i)); + switch (current) { + case MediaType mediaType -> variant.setMediaType(mediaType); + case CharacterSet characterSet -> variant.setCharacterSet(characterSet); + case Encoding encoding -> { + // Do we need to add this metadata? + boolean found = false; + for (int i = 0; !found && i < variant.getEncodings().size(); i++) { + found = current.includes(variant.getEncodings().get(i)); + } + if (!found) { + variant.getEncodings().add(encoding); + } } - if (!found) { - variant.getEncodings().add(encoding); - } - } else if (current instanceof Language language) { - // Do we need to add this metadata? - boolean found = false; - for (int i = 0; !found && i < variant.getLanguages().size(); i++) { - found = current.includes(variant.getLanguages().get(i)); - } - if (!found) { - variant.getLanguages().add(language); + case Language language -> { + // Do we need to add this metadata? + boolean found = false; + for (int i = 0; !found && i < variant.getLanguages().size(); i++) { + found = current.includes(variant.getLanguages().get(i)); + } + if (!found) { + variant.getLanguages().add(language); + } } + default -> + throw new IllegalStateException("Unexpected metadata: " + current); } } diff --git a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java index 70f60f2325..c67ce10845 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/EntityClientHelper.java @@ -86,12 +86,12 @@ protected String getReencodedVariantEntityName( int i = 0; int j = 0; boolean stop = false; - char[] encodeds = encodedEntityName.toCharArray(); - char[] decodeds = decodedVariantEntityName.toCharArray(); + char[] encodedChars = encodedEntityName.toCharArray(); + char[] decodedChars = decodedVariantEntityName.toCharArray(); - for (i = 0; (i < decodeds.length) && (j < encodeds.length) && !stop; i++) { - char decodedChar = decodeds[i]; - char encodedChar = encodeds[j]; + for (i = 0; (i < decodedChars.length) && (j < encodedChars.length) && !stop; i++) { + char decodedChar = decodedChars[i]; + char encodedChar = encodedChars[j]; if (encodedChar == '%') { String dec = Reference.decode(encodedEntityName.substring(j, j + 3)); diff --git a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java index 76f642fbad..ea821e2752 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/FileClientHelper.java @@ -126,26 +126,27 @@ protected boolean checkExtensionsConsistency(File file) { */ private boolean checkMetadataConsistency(String fileName, Representation representation) { if (representation != null) { - Variant var = new Variant(); - Entity.updateMetadata(fileName, var, false, getMetadataService()); + Variant variant = new Variant(); + Entity.updateMetadata(fileName, variant, false, getMetadataService()); - // "var" contains the theoretical correct metadata - if (!var.getLanguages().isEmpty() + // "variant" contains the theoretical correct metadata + if (!variant.getLanguages().isEmpty() && !representation.getLanguages().isEmpty() - && !new HashSet<>(var.getLanguages()) + && !new HashSet<>(variant.getLanguages()) .containsAll(representation.getLanguages())) { return false; } - if ((var.getMediaType() != null) + if ((variant.getMediaType() != null) && (representation.getMediaType() != null) - && !(var.getMediaType().includes(representation.getMediaType()))) { + && !(variant.getMediaType().includes(representation.getMediaType()))) { return false; } - return var.getEncodings().isEmpty() + return variant.getEncodings().isEmpty() || representation.getEncodings().isEmpty() - || new HashSet<>(var.getEncodings()).containsAll(representation.getEncodings()); + || new HashSet<>(variant.getEncodings()) + .containsAll(representation.getEncodings()); } return true; } @@ -396,9 +397,6 @@ private Status doHandleFilePut(Request request, String path, File file) { : null; if (file.exists()) { - // The PUT call is handled in two phases: - // 1- write a temporary file - // 2- rename the target file if (range != null) { return updateFileWithPartialContent(request, file, range); } @@ -421,6 +419,7 @@ private Status doHandleFilePut(Request request, String path, File file) { } } + /** The update is handled in two phases: write a temporary file, replace the target file. */ private Status updateFileWithPartialContent(Request request, File file, Range range) { File tmp = null; @@ -436,27 +435,7 @@ private Status updateFileWithPartialContent(Request request, File file, Range ra Files.copy(file.toPath(), tmp.toPath()); } - try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { - // Go to the desired offset. - if (range.getIndex() == Range.INDEX_LAST) { - if (raf.length() <= range.getSize()) { - raf.seek(range.getSize()); - } else { - raf.seek(raf.length() - range.getSize()); - } - } else { - raf.seek(range.getIndex()); - } - - // Write the entity to the temporary file. - if (request.isEntityAvailable()) { - IoUtils.copy(request.getEntity().getStream(), raf); - } - } catch (IOException ioe) { - getLogger().log(WARNING, "Unable to close the temporary file", ioe); - cleanTemporaryFileIfUploadNotResumed(tmp); - return new Status(SERVER_ERROR_INTERNAL, ioe); - } + updateRangeInFile(request, range, tmp); return replaceFileByTemporaryFile(request, file, tmp); } catch (IOException ioe) { @@ -468,6 +447,27 @@ private Status updateFileWithPartialContent(Request request, File file, Range ra } } + private void updateRangeInFile(final Request request, final Range range, final File tmp) + throws IOException { + try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { + // Go to the desired offset. + if (range.getIndex() == Range.INDEX_LAST) { + if (raf.length() <= range.getSize()) { + raf.seek(range.getSize()); + } else { + raf.seek(raf.length() - range.getSize()); + } + } else { + raf.seek(range.getIndex()); + } + + // Write the entity to the temporary file. + if (request.isEntityAvailable()) { + IoUtils.copy(request.getEntity().getStream(), raf); + } + } + } + private Status replaceFile(Request request, File file) { File tmp = null; try { @@ -495,7 +495,7 @@ private Status replaceFileByTemporaryFile(Request request, File file, File tmp) return new Status(SERVER_ERROR_INTERNAL, "Unable to delete the existing file"); } - // Finally move the temporary file to the existing file location + // Finally, move the temporary file to the existing file location if (tmp.renameTo(file)) { if (request.isEntityAvailable()) { return SUCCESS_NO_CONTENT; diff --git a/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java b/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java index 1e4edab94b..7d9d3cbd71 100644 --- a/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java +++ b/org.restlet/src/main/java/org/restlet/engine/local/RiapServerHelper.java @@ -33,7 +33,7 @@ public RiapServerHelper(Server server) { // Lazy initialization with double-check. if (server != null && RiapServerHelper.instance == null) { - synchronized (this.getClass()) { + synchronized (RiapServerHelper.class) { if (RiapServerHelper.instance == null) { RiapServerHelper.instance = this; } diff --git a/org.restlet/src/main/java/org/restlet/routing/Filter.java b/org.restlet/src/main/java/org/restlet/routing/Filter.java index 232861ba71..45aa680c82 100644 --- a/org.restlet/src/main/java/org/restlet/routing/Filter.java +++ b/org.restlet/src/main/java/org/restlet/routing/Filter.java @@ -111,8 +111,6 @@ protected int beforeHandle(Request request, Response response) { * @return The continuation status. Either {@link #CONTINUE} or {@link #STOP}. */ protected int doHandle(Request request, Response response) { - final int result = CONTINUE; - if (getNext() != null) { getNext().handle(request, response); @@ -132,7 +130,7 @@ protected int doHandle(Request request, Response response) { + " was executed without a next Restlet attached to it."); } - return result; + return CONTINUE; } /** diff --git a/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java b/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java index 702f723ac8..7f642884b0 100644 --- a/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java +++ b/org.restlet/src/main/java/org/restlet/security/MemoryRealm.java @@ -166,7 +166,7 @@ public Set findGroups(User user) { * @return The set of groups. */ public Set findGroups(User user, boolean inheritOnly) { - Set result = new HashSet(); + Set result = new HashSet<>(); List stack; // Recursively find user groups @@ -220,16 +220,16 @@ public Set findRoles(Application application, Set userGroups) { throw new IllegalArgumentException("The application argument can't be null"); } - Set result = new HashSet(); + Set result = new HashSet<>(); Object source; for (RoleMapping mapping : getRoleMappings()) { source = mapping.getSource(); - if ((userGroups != null) && userGroups.contains(source)) { - if (mapping.getTarget().getApplication() == application) { - result.add(mapping.getTarget()); - } + if ((userGroups != null) + && userGroups.contains(source) + && mapping.getTarget().getApplication() == application) { + result.add(mapping.getTarget()); } } @@ -249,16 +249,16 @@ public Set findRoles(Application application, User user) { throw new IllegalArgumentException("The application argument can't be null"); } - Set result = new HashSet(); + Set result = new HashSet<>(); Object source; for (RoleMapping mapping : getRoleMappings()) { source = mapping.getSource(); - if ((user != null) && user.equals(source)) { - if (mapping.getTarget().getApplication() == application) { - result.add(mapping.getTarget()); - } + if ((user != null) + && user.equals(source) + && mapping.getTarget().getApplication() == application) { + result.add(mapping.getTarget()); } } diff --git a/org.restlet/src/test/java/org/restlet/CallTestCase.java b/org.restlet/src/test/java/org/restlet/CallTestCase.java index f2f2716bc1..6f2c4decc4 100644 --- a/org.restlet/src/test/java/org/restlet/CallTestCase.java +++ b/org.restlet/src/test/java/org/restlet/CallTestCase.java @@ -17,7 +17,6 @@ import org.restlet.data.Method; import org.restlet.data.Reference; import org.restlet.data.Status; -import org.restlet.engine.adapter.Call; /** * Test {@link org.restlet.engine.adapter.Call}. @@ -25,25 +24,6 @@ * @author Lars Heuer (heuer[at]semagia.com) */ class CallTestCase { - /** - * Returns a connector call. - * - * @return A connector call instance. - */ - protected Call getCall() { - return new Call() { - - @Override - protected boolean isClientKeepAlive() { - return false; - } - - @Override - protected boolean isServerKeepAlive() { - return false; - } - }; - } /** * Returns a reference with the specified URI. diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java index 8260772fc5..01d69d4b3e 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetChunkedTestCase.java @@ -37,7 +37,7 @@ */ public class GetChunkedTestCase extends BaseConnectorsTestCase { - private static final String text = "" + "a".repeat(1000) + ""; + private static final String TEXT = "" + "a".repeat(1000) + ""; @Override protected void doTest(final int serverPort) throws Exception { @@ -51,7 +51,7 @@ protected void doTest(final int serverPort) throws Exception { assertEquals( Status.SUCCESS_OK, response.getStatus(), response.getStatus().getDescription()); assertChunkedHeader(response); - assertEquals(text, response.getEntity().getText()); + assertEquals(TEXT, response.getEntity().getText()); } finally { response.release(); client.stop(); @@ -78,7 +78,7 @@ public GetChunkedTestResource() { @Override public Representation get(Variant variant) { - final Representation rep = new StringRepresentation(text, MediaType.APPLICATION_XML); + final Representation rep = new StringRepresentation(TEXT, MediaType.APPLICATION_XML); rep.setSize(Representation.UNKNOWN_SIZE); // force chunked encoding return rep; } diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java index fba3ffdf1c..7ac98eb6eb 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetQueryParamTestCase.java @@ -64,6 +64,7 @@ public Restlet createInboundRoot() { } public static class GetTestResource extends ServerResource { + @Override @Get public String toString() { Form query = getQuery(); diff --git a/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java b/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java index 1c19e43301..1f52712695 100644 --- a/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/connector/GetTestCase.java @@ -61,6 +61,7 @@ public Restlet createInboundRoot() { } public static class GetTestResource extends ServerResource { + @Override @Get public String toString() { return "Hello world"; From 9c125fe0309d805c770ba4838c3e7649b497a312 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Tue, 7 Apr 2026 09:56:43 +0200 Subject: [PATCH 28/30] Take into account Sonar comments --- .../ext/velocity/TemplateRepresentation.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java index e45689547f..0724da4cea 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2026 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.ext.velocity; @@ -14,6 +14,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; + import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; @@ -188,14 +189,10 @@ public TemplateRepresentation( String templateName, Map dataModel, MediaType mediaType) { super(mediaType); - try { - setDataModel(dataModel); - this.engine = new VelocityEngine(); - this.template = null; - this.templateName = templateName; - } catch (Exception e) { - e.printStackTrace(); - } + setDataModel(dataModel); + this.engine = new VelocityEngine(); + this.template = null; + this.templateName = templateName; } /** From 142c9b555b99dd0168a5b8992da9bae640d25570 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Tue, 7 Apr 2026 22:58:46 +0200 Subject: [PATCH 29/30] Taken into account PR review comment --- .../ext/velocity/TemplateRepresentation.java | 5 +- .../org/restlet/service/ConverterService.java | 2 +- .../main/java/org/restlet/util/Series.java | 5 +- .../org/restlet/data/MediaTypeTestCase.java | 401 ++++----- .../restlet/data/ProductTokenTestCase.java | 166 ++-- .../org/restlet/data/ReferenceTestCase.java | 766 ++++++++---------- .../application/TunnelFilterTestCase.java | 86 +- 7 files changed, 712 insertions(+), 719 deletions(-) diff --git a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java index 0724da4cea..9ae58df7f6 100644 --- a/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java +++ b/org.restlet.ext.velocity/src/main/java/org/restlet/ext/velocity/TemplateRepresentation.java @@ -1,9 +1,9 @@ /** * Copyright 2005-2026 Qlik - *

+ *

* The content of this file is subject to the terms of the Apache 2.0 open * source license available at https://www.opensource.org/licenses/apache-2.0 - *

+ *

* Restlet is a registered trademark of QlikTech International AB. */ package org.restlet.ext.velocity; @@ -14,7 +14,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; - import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; diff --git a/org.restlet/src/main/java/org/restlet/service/ConverterService.java b/org.restlet/src/main/java/org/restlet/service/ConverterService.java index 3ecd952855..d94ccc6792 100644 --- a/org.restlet/src/main/java/org/restlet/service/ConverterService.java +++ b/org.restlet/src/main/java/org/restlet/service/ConverterService.java @@ -28,7 +28,7 @@ * can work in both directions. Actual converters can be plugged into the engine to support this * service.
*
- * Root object classes used for conversion shouldn't be generic classes otherwise important + * Root object classes used for conversion shouldn't be generic classes, otherwise important * contextual type information will be missing at runtime due to Java type erasure mechanism. If * needed, create a fully resolved subclasses and/or a container classes. * diff --git a/org.restlet/src/main/java/org/restlet/util/Series.java b/org.restlet/src/main/java/org/restlet/util/Series.java index 2a5ce01789..52b674ceff 100644 --- a/org.restlet/src/main/java/org/restlet/util/Series.java +++ b/org.restlet/src/main/java/org/restlet/util/Series.java @@ -80,7 +80,8 @@ public Series(Class entryClass, List delegate) { * * @param name The parameter name. * @param value The parameter value. - * @return True (as per the general contract of the Collection.add method). + * @return True (as per the general contract of the {@link java.util.Collection#add(Object)} + * method). */ public boolean add(String name, String value) { return add(createEntry(name, value)); @@ -344,7 +345,7 @@ public String[] getValuesArray(String name, String defaultValue) { /** * Returns a map of name, value pairs. The order of the map keys is respected based on the - * series order. When a name has multiple values, only the first one is put in the map. + * series order. When a name has multiple values, only the first one is returned. * * @return The map of name, value pairs. */ diff --git a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java index b8ce8d765a..e5a4ebb192 100644 --- a/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/MediaTypeTestCase.java @@ -15,8 +15,27 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; - +import static org.restlet.data.MediaType.ALL; +import static org.restlet.data.MediaType.APPLICATION_ALL; +import static org.restlet.data.MediaType.APPLICATION_ALL_XML; +import static org.restlet.data.MediaType.APPLICATION_ATOM; +import static org.restlet.data.MediaType.APPLICATION_ATOMPUB_SERVICE; +import static org.restlet.data.MediaType.APPLICATION_OCTET_STREAM; +import static org.restlet.data.MediaType.APPLICATION_XML; +import static org.restlet.data.MediaType.IMAGE_ALL; +import static org.restlet.data.MediaType.TEXT_ALL; +import static org.restlet.data.MediaType.TEXT_PLAIN; +import static org.restlet.data.MediaType.getMostSpecific; +import static org.restlet.data.MediaType.register; +import static org.restlet.data.MediaType.valueOf; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; import org.restlet.util.Series; /** @@ -31,36 +50,41 @@ class MediaTypeTestCase { * expected values. * * @param name type to analyze. - * @param main expected main type. - * @param sub expected subtype. - * @param concrete expected 'concrete' flag. + * @param expectedMain expected main type. + * @param expectedSub expected subtype. + * @param expectedConcrete expected 'concrete' flag. */ - public void assertMediaType(String name, String main, String sub, boolean concrete) { + public void assertMediaType( + String name, String expectedMain, String expectedSub, boolean expectedConcrete) { MediaType type; type = new MediaType(name); - assertEquals(main, type.getMainType()); - assertEquals(sub, type.getSubType()); - assertEquals(concrete, type.isConcrete()); + assertEquals(expectedMain, type.getMainType()); + assertEquals(expectedSub, type.getSubType()); + assertEquals(expectedConcrete, type.isConcrete()); } /** Makes sure concrete types are properly initialized. */ - @Test - void testConcrete() { - assertMediaType("application/xml", "application", "xml", true); - assertMediaType("application/ xml ", "application", "xml", true); - assertMediaType(" application /xml", "application", "xml", true); - assertMediaType(" application / xml ", "application", "xml", true); - assertMediaType("application/atom+xml;type=entry", "application", "atom+xml", true); + @ParameterizedTest + @CsvSource({ + "application/xml,application,xml,true", + "application/ xml,application,xml,true,", + "application /xml,application,xml,true", + "application / xml ,application,xml,true", + "application/atom+xml;type=entry,application,atom+xml,true", + }) + void testConcrete( + String name, String expectedMain, String expectedSub, boolean expectedConcrete) { + assertMediaType(name, expectedMain, expectedSub, expectedConcrete); } /** Makes sure concrete types are properly initialized. */ @Test void testParameters() { - MediaType mt = MediaType.valueOf("application/atom+xml;type=entry"); + MediaType mt = valueOf("application/atom+xml;type=entry"); assertEquals("entry", mt.getParameters().getFirstValue("type")); - mt = MediaType.valueOf("multipart/x-mixed-replace; boundary=\"My boundary\""); + mt = valueOf("multipart/x-mixed-replace; boundary=\"My boundary\""); assertEquals("\"My boundary\"", mt.getParameters().getFirstValue("boundary")); } @@ -68,7 +92,7 @@ void testParameters() { @Test void testEquals() { MediaType mt1 = new MediaType("application/xml"); - MediaType mt2 = MediaType.APPLICATION_XML; + MediaType mt2 = APPLICATION_XML; assertEquals(mt1, mt2); assertEquals(mt1, mt2); @@ -91,182 +115,176 @@ void testEquals() { assertTrue(mt1Bis.equals(mt3, true)); mt1 = new MediaType("application/*"); - mt2 = MediaType.APPLICATION_ALL; + mt2 = APPLICATION_ALL; assertEquals(mt1, mt2); assertEquals(mt1, mt2); } /** Test inclusion. */ - @Test - void testIncludes() { - MediaType mt1 = MediaType.APPLICATION_ALL; - MediaType mt2 = MediaType.APPLICATION_XML; - assertTrue(mt1.includes(mt1)); - assertTrue(mt2.includes(mt2)); - assertTrue(mt1.includes(mt2)); - assertFalse(mt2.includes(mt1)); - - mt1 = MediaType.APPLICATION_ALL_XML; - mt2 = MediaType.APPLICATION_XML; - assertTrue(mt1.includes(mt1)); - assertTrue(mt2.includes(mt2)); - assertTrue(mt1.includes(mt2)); - assertFalse(mt2.includes(mt1)); - - mt1 = MediaType.APPLICATION_ALL_XML; - mt2 = MediaType.APPLICATION_ATOMPUB_SERVICE; - assertTrue(mt1.includes(mt1)); - assertTrue(mt2.includes(mt2)); - assertTrue(mt1.includes(mt2)); - assertFalse(mt2.includes(mt1)); - - mt1 = MediaType.IMAGE_ALL; - mt2 = MediaType.APPLICATION_OCTET_STREAM; - assertFalse(mt1.includes(mt2)); - assertFalse(mt2.includes(mt1)); - - assertFalse(mt1.includes(null)); - - /* - * test inclusion for media types with parameters. The rule is: media - * type A includes media type B iff for each parameter name/value pair - * in A, B contains the same parameter name/value pair + @Nested + class Inclusion { + + @ParameterizedTest + @MethodSource("inclusionTestCases") + void shouldInclude(MediaType mt, MediaType included) { + assertTrue(mt.includes(included)); + } + + static Stream inclusionTestCases() { + return Stream.of( + Arguments.of(APPLICATION_ALL, APPLICATION_ALL), + Arguments.of(APPLICATION_XML, APPLICATION_XML), + Arguments.of(APPLICATION_ALL, APPLICATION_XML), + Arguments.of(APPLICATION_ALL_XML, APPLICATION_ALL_XML), + Arguments.of(APPLICATION_XML, APPLICATION_XML), + Arguments.of(APPLICATION_ALL_XML, APPLICATION_XML), + Arguments.of(APPLICATION_ATOMPUB_SERVICE, APPLICATION_ATOMPUB_SERVICE), + Arguments.of(APPLICATION_ALL_XML, APPLICATION_ATOMPUB_SERVICE)); + } + + @ParameterizedTest + @MethodSource("noInclusionTestCases") + void shouldNotInclude(MediaType mt, MediaType notIncluded) { + assertFalse(mt.includes(notIncluded)); + } + + static Stream noInclusionTestCases() { + return Stream.of( + Arguments.of(APPLICATION_XML, APPLICATION_ALL), + Arguments.of(APPLICATION_XML, APPLICATION_ALL_XML), + Arguments.of(APPLICATION_ATOMPUB_SERVICE, APPLICATION_ALL_XML), + Arguments.of(IMAGE_ALL, APPLICATION_OCTET_STREAM), + Arguments.of(APPLICATION_OCTET_STREAM, IMAGE_ALL), + Arguments.of(IMAGE_ALL, null)); + } + + /** + * test inclusion for media types with parameters. The rule is: media type A includes media + * type B iff for each parameter name/value pair in A, B contains the same parameter + * name/value pair */ - - // set up test data - - MediaType typeWithNoParams = new MediaType("application/sometype"); - - Series singleParam = new Series<>(Parameter.class); - singleParam.add(new Parameter("name1", "value1")); - MediaType typeWithSingleParam = new MediaType("application/sometype", singleParam); - - Series singleMatchingParam = new Series<>(Parameter.class); - singleMatchingParam.add(new Parameter("name1", "value1")); - MediaType typeWithSingleMatchingParam = - new MediaType("application/sometype", singleMatchingParam); - - Series singleNonMatchingParamValue = new Series<>(Parameter.class); - singleNonMatchingParamValue.add(new Parameter("name1", "value2")); - MediaType typeWithSingleNonMatchingParamValue = - new MediaType("application/sometype", singleNonMatchingParamValue); - - Series singleNonMatchingParamName = new Series<>(Parameter.class); - singleNonMatchingParamName.add(new Parameter("name2", "value2")); - MediaType typeWithSingleNonMatchingParamName = - new MediaType("application/sometype", singleNonMatchingParamName); - - Series twoParamsOneMatches = new Series<>(Parameter.class); - twoParamsOneMatches.add(new Parameter("name1", "value1")); - twoParamsOneMatches.add(new Parameter("name2", "value2")); - MediaType typeWithTwoParamsOneMatches = - new MediaType("application/sometype", twoParamsOneMatches); - - // SCENARIO 1: test whether type with no params includes type with one - // param - - assertTrue(typeWithNoParams.includes(typeWithSingleParam, true)); - assertTrue(typeWithNoParams.includes(typeWithSingleParam, false)); - - // SCENARIO 2: test whether type with one param includes type with no - // params - - assertTrue(typeWithSingleParam.includes(typeWithNoParams, true)); - assertFalse(typeWithSingleParam.includes(typeWithNoParams, false)); - - // SCENARIO 3: test whether type with single param includes type with - // matching single param. - // Note that this is distinct from testing whether a type includes - // itself, as there is a special check for that. - assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, true)); - assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, false)); - - // SCENARIO 4: test whether type with single param includes type with - // single param having different name - assertTrue(typeWithSingleParam.includes(typeWithSingleNonMatchingParamName, true)); - assertFalse(typeWithSingleParam.includes(typeWithSingleNonMatchingParamName, false)); - - // SCENARIO 5: test whether type with single param includes type with - // single param having same name but different value - assertTrue(typeWithSingleParam.includes(typeWithSingleNonMatchingParamValue, true)); - assertFalse(typeWithSingleParam.includes(typeWithSingleNonMatchingParamValue, false)); - - // SCENARIO 6: test whether type with single param includes type with - // two params, one matching - assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, true)); - assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, false)); - - // SCENARIO 7: test whether type with two params includes type with - // single matching param - assertTrue(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, true)); - assertFalse(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, false)); + @Test + void testIncludes() { + // set up test data + + MediaType typeWithNoParams = new MediaType("application/sometype"); + + Series singleParam = new Series<>(Parameter.class); + singleParam.add(new Parameter("name1", "value1")); + MediaType typeWithSingleParam = new MediaType("application/sometype", singleParam); + + Series singleMatchingParam = new Series<>(Parameter.class); + singleMatchingParam.add(new Parameter("name1", "value1")); + MediaType typeWithSingleMatchingParam = + new MediaType("application/sometype", singleMatchingParam); + + Series singleNonMatchingParamValue = new Series<>(Parameter.class); + singleNonMatchingParamValue.add(new Parameter("name1", "value2")); + MediaType typeWithSingleNonMatchingParamValue = + new MediaType("application/sometype", singleNonMatchingParamValue); + + Series singleNonMatchingParamName = new Series<>(Parameter.class); + singleNonMatchingParamName.add(new Parameter("name2", "value2")); + MediaType typeWithSingleNonMatchingParamName = + new MediaType("application/sometype", singleNonMatchingParamName); + + Series twoParamsOneMatches = new Series<>(Parameter.class); + twoParamsOneMatches.add(new Parameter("name1", "value1")); + twoParamsOneMatches.add(new Parameter("name2", "value2")); + MediaType typeWithTwoParamsOneMatches = + new MediaType("application/sometype", twoParamsOneMatches); + + // SCENARIO 1: test whether type with no params includes type with one + // param + + assertTrue(typeWithNoParams.includes(typeWithSingleParam, true)); + assertTrue(typeWithNoParams.includes(typeWithSingleParam, false)); + + // SCENARIO 2: test whether type with one param includes type with no + // params + + assertTrue(typeWithSingleParam.includes(typeWithNoParams, true)); + assertFalse(typeWithSingleParam.includes(typeWithNoParams, false)); + + // SCENARIO 3: test whether type with single param includes type with + // matching single param. + // Note that this is distinct from testing whether a type includes + // itself, as there is a special check for that. + assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, true)); + assertTrue(typeWithSingleParam.includes(typeWithSingleMatchingParam, false)); + + // SCENARIO 4: test whether type with single param includes type with + // single param having different name + assertTrue(typeWithSingleParam.includes(typeWithSingleNonMatchingParamName, true)); + assertFalse(typeWithSingleParam.includes(typeWithSingleNonMatchingParamName, false)); + + // SCENARIO 5: test whether type with single param includes type with + // single param having the same name but different value + assertTrue(typeWithSingleParam.includes(typeWithSingleNonMatchingParamValue, true)); + assertFalse(typeWithSingleParam.includes(typeWithSingleNonMatchingParamValue, false)); + + // SCENARIO 6: test whether type with single param includes type with + // two params, one matching + assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, true)); + assertTrue(typeWithSingleParam.includes(typeWithTwoParamsOneMatches, false)); + + // SCENARIO 7: test whether type with two params includes type with + // single matching param + assertTrue(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, true)); + assertFalse(typeWithTwoParamsOneMatches.includes(typeWithSingleParam, false)); + } } @Test void testMostSpecificMediaType() { - assertEquals( - MediaType.TEXT_ALL, MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_ALL)); - assertEquals( - MediaType.TEXT_ALL, MediaType.getMostSpecific(MediaType.TEXT_ALL, MediaType.ALL)); - - assertEquals( - MediaType.TEXT_PLAIN, - MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_ALL, MediaType.TEXT_PLAIN)); - assertEquals( - MediaType.TEXT_PLAIN, - MediaType.getMostSpecific(MediaType.ALL, MediaType.TEXT_PLAIN, MediaType.TEXT_ALL)); - assertEquals( - MediaType.TEXT_PLAIN, - MediaType.getMostSpecific(MediaType.TEXT_ALL, MediaType.ALL, MediaType.TEXT_PLAIN)); - assertEquals( - MediaType.TEXT_PLAIN, - MediaType.getMostSpecific(MediaType.TEXT_ALL, MediaType.TEXT_PLAIN, MediaType.ALL)); - assertEquals( - MediaType.TEXT_PLAIN, - MediaType.getMostSpecific(MediaType.TEXT_PLAIN, MediaType.ALL, MediaType.TEXT_ALL)); - assertEquals( - MediaType.TEXT_PLAIN, - MediaType.getMostSpecific(MediaType.TEXT_PLAIN, MediaType.TEXT_ALL, MediaType.ALL)); + assertEquals(TEXT_ALL, getMostSpecific(ALL, TEXT_ALL)); + assertEquals(TEXT_ALL, getMostSpecific(TEXT_ALL, ALL)); + + assertEquals(TEXT_PLAIN, getMostSpecific(ALL, TEXT_ALL, TEXT_PLAIN)); + assertEquals(TEXT_PLAIN, getMostSpecific(ALL, TEXT_PLAIN, TEXT_ALL)); + assertEquals(TEXT_PLAIN, getMostSpecific(TEXT_ALL, ALL, TEXT_PLAIN)); + assertEquals(TEXT_PLAIN, getMostSpecific(TEXT_ALL, TEXT_PLAIN, ALL)); + assertEquals(TEXT_PLAIN, getMostSpecific(TEXT_PLAIN, ALL, TEXT_ALL)); + assertEquals(TEXT_PLAIN, getMostSpecific(TEXT_PLAIN, TEXT_ALL, ALL)); } /** Makes sure that 'abstract' types are properly initialised. */ - @Test - void testNotConcrete() { - // */* - assertMediaType("", "*", "*", false); - assertMediaType(" ", "*", "*", false); - assertMediaType("*/", "*", "*", false); - assertMediaType("*/ ", "*", "*", false); - assertMediaType(" * /", "*", "*", false); - assertMediaType("/*", "*", "*", false); - assertMediaType(" /*", "*", "*", false); - assertMediaType("/ * ", "*", "*", false); - assertMediaType(" / * ", "*", "*", false); - assertMediaType("*/*", "*", "*", false); - assertMediaType(" * /*", "*", "*", false); - assertMediaType("*/ * ", "*", "*", false); - assertMediaType(" * / * ", "*", "*", false); - - // */xml - assertMediaType("/xml", "*", "xml", false); - assertMediaType("/ xml ", "*", "xml", false); - assertMediaType(" /xml", "*", "xml", false); - assertMediaType(" / xml ", "*", "xml", false); - assertMediaType("*/xml", "*", "xml", false); - assertMediaType(" * /xml", "*", "xml", false); - assertMediaType("*/ xml ", "*", "xml", false); - assertMediaType(" * / xml ", "*", "xml", false); - - // application/* - assertMediaType("application", "application", "*", false); - assertMediaType(" application ", "application", "*", false); - assertMediaType("application/", "application", "*", false); - assertMediaType(" application /", "application", "*", false); - assertMediaType(" application / ", "application", "*", false); - assertMediaType("application/*", "application", "*", false); - assertMediaType(" application /*", "application", "*", false); - assertMediaType("application/ * ", "application", "*", false); - assertMediaType(" application /*", "application", "*", false); + @ParameterizedTest + @CsvSource({ + "'',*,*,false", + "' ',*,*,false", + "*/,*,*,false", + "*/ ,*,*,false", + " * /,*,*,false", + "/*,*,*,false", + " /*,*,*,false", + "/ * ,*,*,false", + " / * ,*,*,false", + "*/*,*,*,false", + " * /*,*,*,false", + "*/ * ,*,*,false", + " * / * ,*,*,false", + "/xml,*,xml,false", + "/ xml ,*,xml,false", + " /xml,*,xml,false", + " / xml ,*,xml,false", + "*/xml,*,xml,false", + " * /xml,*,xml,false", + "*/ xml ,*,xml,false", + " * / xml ,*,xml,false", + "application,application,*,false", + " application ,application,*,false", + "application/,application,*,false", + " application /,application,*,false", + " application / ,application,*,false", + "application/*,application,*,false", + " application /*,application,*,false", + "application/ * ,application,*,false", + " application /*,application,*,false" + }) + void testNotConcrete( + String name, String expectedMain, String expectedSub, boolean expectedConcrete) { + assertMediaType(name, expectedMain, expectedSub, expectedConcrete); } /** Test references that are unequal. */ @@ -288,37 +306,36 @@ void testUnEquals() { assertNotEquals(mt1Bis, mt3); mt1 = new MediaType("application/1"); - mt2 = MediaType.APPLICATION_ALL; + mt2 = APPLICATION_ALL; assertNotEquals(mt1, mt2); } /** Testing {@link MediaType#valueOf(String)} and {@link MediaType#register(String, String)} */ @Test void testValueOf() { - assertSame(MediaType.APPLICATION_XML, MediaType.valueOf("application/xml")); - assertSame(MediaType.ALL, MediaType.valueOf("*/*")); - final MediaType newType = MediaType.valueOf("application/x-restlet-test"); + assertSame(APPLICATION_XML, valueOf("application/xml")); + assertSame(ALL, valueOf("*/*")); + final MediaType newType = valueOf("application/x-restlet-test"); assertEquals("application", newType.getMainType()); assertEquals("x-restlet-test", newType.getSubType()); assertEquals("application/x-restlet-test", newType.getName()); // Should not have got registered by call to valueOf() alone - assertNotSame(newType, MediaType.valueOf("application/x-restlet-test")); + assertNotSame(newType, valueOf("application/x-restlet-test")); - final MediaType registeredType = - MediaType.register("application/x-restlet-test", "Restlet testcase"); + final MediaType registeredType = register("application/x-restlet-test", "Restlet testcase"); assertNotSame(newType, registeredType); // didn't touch old value assertEquals("application/x-restlet-test", registeredType.getName()); assertEquals("Restlet testcase", registeredType.getDescription()); // Later valueOf calls always returns the registered type - assertSame(registeredType, MediaType.valueOf("application/x-restlet-test")); - assertSame(registeredType, MediaType.valueOf("application/x-restlet-test")); + assertSame(registeredType, valueOf("application/x-restlet-test")); + assertSame(registeredType, valueOf("application/x-restlet-test")); // Test toString() equivalence - MediaType mediaType = MediaType.valueOf("application/atom+xml; name=value"); + MediaType mediaType = valueOf("application/atom+xml; name=value"); assertEquals("application/atom+xml; name=value", mediaType.toString()); - assertEquals(MediaType.APPLICATION_ATOM, mediaType.getParent()); + assertEquals(APPLICATION_ATOM, mediaType.getParent()); } @Test diff --git a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java index c7e3c044fe..ff9c28f76f 100644 --- a/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ProductTokenTestCase.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -92,74 +93,103 @@ private static Stream mainProductTestCases() { null)); } - @Test - void testProductTokens() { - final String userAgent1 = - "Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)"; - final String userAgent2 = "Advanced Browser (http://www.avantbrowser.com)"; - final String userAgent3 = "Mozilla/5.0"; - final String userAgent4 = "Mozilla"; - final String userAgent5 = - "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:1.8) Gecko/20051107 Camino/1.0b1"; - final String userAgent6 = - "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)"; - final String userAgent7 = "Restlet-Framework/2.2-SNAPSHOT"; - - List list = ProductReader.read(userAgent1); - assertEquals(1, list.size()); - assertEquals("Mozilla", list.getFirst().getName()); - assertEquals("4.0", list.getFirst().getVersion()); - assertEquals( - "compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;", - list.getFirst().getComment()); - - list = ProductReader.read(userAgent2); - assertEquals(1, list.size()); - assertEquals("Advanced Browser", list.getFirst().getName()); - assertNull(list.getFirst().getVersion()); - assertEquals("http://www.avantbrowser.com", list.getFirst().getComment()); - - list = ProductReader.read(userAgent3); - assertEquals(1, list.size()); - assertEquals("Mozilla", list.getFirst().getName()); - assertEquals("5.0", list.getFirst().getVersion()); - assertNull(list.getFirst().getComment()); - - list = ProductReader.read(userAgent4); - assertEquals(1, list.size()); - assertEquals("Mozilla", list.getFirst().getName()); - assertNull(list.getFirst().getVersion()); - assertNull(list.getFirst().getComment()); - - list = ProductReader.read(userAgent5); - assertEquals(3, list.size()); - assertEquals("Mozilla", list.getFirst().getName()); - assertEquals("5.0", list.get(0).getVersion()); - assertEquals("Macintosh; U; PPC Mac OS X; en-US; rv:1.8", list.get(0).getComment()); - assertEquals("Gecko", list.get(1).getName()); - assertEquals("20051107", list.get(1).getVersion()); - assertNull(list.get(1).getComment()); - assertEquals("Camino", list.get(2).getName()); - assertEquals("1.0b1", list.get(2).getVersion()); - assertNull(list.get(2).getComment()); - - list = ProductReader.read(userAgent6); - assertEquals(3, list.size()); - assertEquals("Mozilla", list.getFirst().getName()); - assertEquals("5.0", list.get(0).getVersion()); - assertEquals("X11; U; Linux i686; en-US; rv:1.8.1", list.get(0).getComment()); - assertEquals("Gecko", list.get(1).getName()); - assertEquals("20061024", list.get(1).getVersion()); - assertNull(list.get(1).getComment()); - assertEquals("Iceweasel", list.get(2).getName()); - assertEquals("2.0", list.get(2).getVersion()); - assertEquals("Debian-2.0+dfsg-1", list.get(2).getComment()); - - list = ProductReader.read(userAgent7); - assertEquals(1, list.size()); - assertEquals("Restlet-Framework", list.getFirst().getName()); - assertEquals("2.2-SNAPSHOT", list.getFirst().getVersion()); - assertNull(list.getFirst().getComment()); + @Nested + class ProductTokens { + + @Test + void testProductTokensAol() { + final String userAgent = + "Mozilla/4.0 (compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;)"; + + List list = ProductReader.read(userAgent); + assertEquals(1, list.size()); + assertEquals("Mozilla", list.getFirst().getName()); + assertEquals("4.0", list.getFirst().getVersion()); + assertEquals( + "compatible; MSIE 6.0; America Online Browser 1.1; rev1.1; Windows NT 5.1;", + list.getFirst().getComment()); + } + + @Test + void testProductTokensAvantBrowser() { + final String userAgent = "Advanced Browser (http://www.avantbrowser.com)"; + + List list = ProductReader.read(userAgent); + assertEquals(1, list.size()); + assertEquals("Advanced Browser", list.getFirst().getName()); + assertNull(list.getFirst().getVersion()); + assertEquals("http://www.avantbrowser.com", list.getFirst().getComment()); + } + + @Test + void testProductTokensMozilla5() { + final String userAgent = "Mozilla/5.0"; + List list = ProductReader.read(userAgent); + + assertEquals(1, list.size()); + assertEquals("Mozilla", list.getFirst().getName()); + assertEquals("5.0", list.getFirst().getVersion()); + assertNull(list.getFirst().getComment()); + } + + @Test + void testProductTokensMozilla() { + final String userAgent = "Mozilla"; + + List list = ProductReader.read(userAgent); + + assertEquals(1, list.size()); + assertEquals("Mozilla", list.getFirst().getName()); + assertNull(list.getFirst().getVersion()); + assertNull(list.getFirst().getComment()); + } + + @Test + void testProductTokensMozillaMacintosh() { + final String userAgent = + "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US; rv:1.8) Gecko/20051107 Camino/1.0b1"; + + List list = ProductReader.read(userAgent); + assertEquals(3, list.size()); + assertEquals("Mozilla", list.getFirst().getName()); + assertEquals("5.0", list.get(0).getVersion()); + assertEquals("Macintosh; U; PPC Mac OS X; en-US; rv:1.8", list.get(0).getComment()); + assertEquals("Gecko", list.get(1).getName()); + assertEquals("20051107", list.get(1).getVersion()); + assertNull(list.get(1).getComment()); + assertEquals("Camino", list.get(2).getName()); + assertEquals("1.0b1", list.get(2).getVersion()); + assertNull(list.get(2).getComment()); + } + + @Test + void testProductTokensMozillaIceWeasel() { + final String userAgent = + "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)"; + List list = ProductReader.read(userAgent); + + assertEquals(3, list.size()); + assertEquals("Mozilla", list.getFirst().getName()); + assertEquals("5.0", list.get(0).getVersion()); + assertEquals("X11; U; Linux i686; en-US; rv:1.8.1", list.get(0).getComment()); + assertEquals("Gecko", list.get(1).getName()); + assertEquals("20061024", list.get(1).getVersion()); + assertNull(list.get(1).getComment()); + assertEquals("Iceweasel", list.get(2).getName()); + assertEquals("2.0", list.get(2).getVersion()); + assertEquals("Debian-2.0+dfsg-1", list.get(2).getComment()); + } + + @Test + void testProductTokensRestlet() { + final String userAgent = "Restlet-Framework/2.2-SNAPSHOT"; + + List list = ProductReader.read(userAgent); + assertEquals(1, list.size()); + assertEquals("Restlet-Framework", list.getFirst().getName()); + assertEquals("2.2-SNAPSHOT", list.getFirst().getVersion()); + assertNull(list.getFirst().getComment()); + } } @Test diff --git a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java index 7d08203bb5..133a383131 100644 --- a/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java +++ b/org.restlet/src/test/java/org/restlet/data/ReferenceTestCase.java @@ -16,24 +16,23 @@ import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import org.restlet.engine.header.HeaderConstants; import org.restlet.engine.util.ReferenceUtils; import org.restlet.util.Series; -/** - * Test {@link org.restlet.data.Reference}. - * - * @author Jerome Louvel - * @author Lars Heuer (heuer[at]semagia.com) Semagia - */ class ReferenceTestCase { + protected static final String DEFAULT_SCHEME = "http"; - protected static final String DEFAULT_SCHEMEPART = "//"; + protected static final String DEFAULT_SCHEME_PART = "//"; /** - * Returns a reference that is initialized with http://restlet.org. + * Returns a reference initialized with http://restlet.org. * * @return Reference instance. */ @@ -51,13 +50,13 @@ protected Reference getDefaultReference() { protected Reference getReference() { final Reference ref = new Reference(); ref.setScheme(DEFAULT_SCHEME); - ref.setSchemeSpecificPart(DEFAULT_SCHEMEPART); + ref.setSchemeSpecificPart(DEFAULT_SCHEME_PART); return ref; } /** Test addition methods. */ @Test - void testAdditions() throws Exception { + void testAdditions() { final Reference ref = new Reference("http://restlet.org"); ref.addQueryParameter("abc", "123"); assertEquals("http://restlet.org?abc=123", ref.toString()); @@ -83,7 +82,7 @@ void testEmptyRef() { reference = new Reference(); reference.setHostDomain("localhost"); // must not produce NPE assertEquals("localhost", reference.getAuthority()); - reference.setHostPort(Integer.valueOf(4711)); // must not produce NPE + reference.setHostPort(4711); // must not produce NPE assertEquals("localhost:4711", reference.getAuthority()); reference.setUserInfo("sdgj:skdfj"); // must not produce NPE assertEquals("sdgj:skdfj@localhost:4711", reference.getAuthority()); @@ -117,13 +116,28 @@ void testEmptyRef() { reference.setSegments(segments); // must not produce NPE } + @ParameterizedTest + @CsvSource({ + "http://localhost/abc,/def", + "http://localhost/abc/,/def", + "http://localhost/abc?query,/def", + "http://localhost/abc#fragment,/def", + "http://localhost/abc?query#fragment,/def", + "http://localhost/abc#fragment?query,/def", + "http://localhost#fragment/abc?query,/def" + }) + void testSetPath(String reference, String path) { + final Reference ref = new Reference(reference); + ref.setPath(path); + assertEquals(path, ref.getPath()); + } + /** Equality tests. */ @Test void testEquals() { final Reference ref1 = getDefaultReference(); final Reference ref2 = getDefaultReference(); assertEquals(ref1, ref2); - assertEquals(ref1, ref2); } @Test @@ -199,8 +213,8 @@ void testOriginalRef() { headers.add(HeaderConstants.HEADER_X_FORWARDED_PORT, "123"); Reference originalRef = ReferenceUtils.getOriginalRef(ref, headers); - assertEquals(originalRef.getSchemeProtocol(), Protocol.HTTPS); - assertEquals(originalRef.getHostPort(), 123); + assertEquals(Protocol.HTTPS, originalRef.getSchemeProtocol()); + assertEquals(123, originalRef.getHostPort()); } /** Test the computation of parent references, for absolute and relative URIs. */ @@ -215,300 +229,18 @@ void testParentRef() { assertEquals("/foo/", parentRef.toString()); } - /** Tests the URI parsing. */ - @Test - void testParsing() { - final String base = "http://a/b/c/d;p?q"; - - final String uri01 = "g:h"; - final String uri02 = "g"; - final String uri03 = "./g"; - final String uri04 = "g/"; - final String uri05 = "/g"; - final String uri06 = "//g"; - final String uri07 = "?y"; - final String uri08 = "g?y"; - final String uri09 = "#s"; - final String uri10 = "g#s"; - final String uri11 = "g?y#s"; - final String uri12 = ";x"; - final String uri13 = "g;x"; - final String uri14 = "g;x?y#s"; - final String uri15 = ""; - final String uri16 = "."; - final String uri17 = "./"; - final String uri18 = ".."; - final String uri19 = "../"; - final String uri20 = "../g"; - final String uri21 = "../.."; - final String uri22 = "../../"; - final String uri23 = "../../g"; - final String uri24 = "../../../g"; - final String uri25 = "../../../../g"; - final String uri26 = "/./g"; - final String uri27 = "/../g"; - final String uri28 = "g."; - final String uri29 = ".g"; - final String uri30 = "g.."; - final String uri31 = "..g"; - final String uri32 = "./../g"; - final String uri33 = "./g/."; - final String uri34 = "g/./h"; - final String uri35 = "g/../h"; - final String uri36 = "g;x=1/./y"; - final String uri37 = "g;x=1/../y"; - - final String uri101 = "g:h"; - final String uri102 = "http://a/b/c/g"; - final String uri103 = "http://a/b/c/g"; - final String uri104 = "http://a/b/c/g/"; - final String uri105 = "http://a/g"; - final String uri106 = "http://g"; - final String uri107 = "http://a/b/c/d;p?y"; - final String uri108 = "http://a/b/c/g?y"; - final String uri109 = "http://a/b/c/d;p?q#s"; - final String uri110 = "http://a/b/c/g#s"; - final String uri111 = "http://a/b/c/g?y#s"; - final String uri112 = "http://a/b/c/;x"; - final String uri113 = "http://a/b/c/g;x"; - final String uri114 = "http://a/b/c/g;x?y#s"; - final String uri115 = "http://a/b/c/d;p?q"; - final String uri116 = "http://a/b/c/"; - final String uri117 = "http://a/b/c/"; - final String uri118 = "http://a/b/"; - final String uri119 = "http://a/b/"; - final String uri120 = "http://a/b/g"; - final String uri121 = "http://a/"; - final String uri122 = "http://a/"; - final String uri123 = "http://a/g"; - final String uri124 = "http://a/g"; - final String uri125 = "http://a/g"; - final String uri126 = "http://a/g"; - final String uri127 = "http://a/g"; - final String uri128 = "http://a/b/c/g."; - final String uri129 = "http://a/b/c/.g"; - final String uri130 = "http://a/b/c/g.."; - final String uri131 = "http://a/b/c/..g"; - final String uri132 = "http://a/b/g"; - final String uri133 = "http://a/b/c/g/"; - final String uri134 = "http://a/b/c/g/h"; - final String uri135 = "http://a/b/c/h"; - final String uri136 = "http://a/b/c/g;x=1/y"; - final String uri137 = "http://a/b/c/y"; - - final Reference host = new Reference("http://host.com"); - final Reference slashdir = new Reference(host, "/dir"); - final Reference dir = new Reference(host, "dir"); - final Reference dirslash = new Reference(host, "dir/"); - final Reference fulldir = new Reference("http://host.com/dir"); - final Reference fulldirsub = new Reference(fulldir, "sub"); - final Reference fulldirslashsub = new Reference(fulldir, "/sub"); - final Reference slashdirsub = new Reference(slashdir, "sub"); - final Reference slashdirslashsub = new Reference(slashdir, "/sub"); - final Reference dirslashsub = new Reference(dirslash, "sub"); - final Reference fullsub = new Reference("http://host.com/dir/sub"); - - // Test the parsing of references into its components - testRef0( - "foo://example.com:8042/over/there?name=ferret#nose", - "foo", - "example.com:8042", - "/over/there", - "name=ferret", - "nose"); - testRef0( - "urn:example:animal:ferret:nose", - "urn", - null, - "example:animal:ferret:nose", - null, - null); - testRef0("mailto:fred@example.com", "mailto", null, "fred@example.com", null, null); - testRef0("foo://info.example.com?fred", "foo", "info.example.com", null, "fred", null); - testRef0("*", null, null, "*", null, null); - testRef0("http://localhost?query", "http", "localhost", null, "query", null); - testRef0("http://localhost#?query", "http", "localhost", null, null, "?query"); - testRef0("http://localhost/?query", "http", "localhost", "/", "query", null); - testRef0("http://localhost/#?query", "http", "localhost", "/", null, "?query"); - testRef0( - "http://localhost/path#frag/ment", "http", "localhost", "/path", null, "frag/ment"); - testRef0("http://localhost/path?qu/ery", "http", "localhost", "/path", "qu/ery", null); - - // Test the resolution of relative references - testRef1(base, uri01, uri101); - testRef1(base, uri02, uri102); - testRef1(base, uri03, uri103); - testRef1(base, uri04, uri104); - testRef1(base, uri05, uri105); - testRef1(base, uri06, uri106); - testRef1(base, uri07, uri107); - testRef1(base, uri08, uri108); - testRef1(base, uri09, uri109); - testRef1(base, uri10, uri110); - testRef1(base, uri11, uri111); - testRef1(base, uri12, uri112); - testRef1(base, uri13, uri113); - testRef1(base, uri14, uri114); - testRef1(base, uri15, uri115); - testRef1(base, uri16, uri116); - testRef1(base, uri17, uri117); - testRef1(base, uri18, uri118); - testRef1(base, uri19, uri119); - testRef1(base, uri20, uri120); - testRef1(base, uri21, uri121); - testRef1(base, uri22, uri122); - testRef1(base, uri23, uri123); - testRef1(base, uri24, uri124); - testRef1(base, uri25, uri125); - testRef1(base, uri26, uri126); - testRef1(base, uri27, uri127); - testRef1(base, uri28, uri128); - testRef1(base, uri29, uri129); - testRef1(base, uri30, uri130); - testRef1(base, uri31, uri131); - testRef1(base, uri32, uri132); - testRef1(base, uri33, uri133); - testRef1(base, uri34, uri134); - testRef1(base, uri35, uri135); - testRef1(base, uri36, uri136); - testRef1(base, uri37, uri137); - - // Test the relativization of absolute references - testRef2(base, uri102, uri02); - testRef2(base, uri104, uri04); - testRef2(base, uri107, uri07); - testRef2(base, uri108, uri08); - testRef2(base, uri109, uri09); - testRef2(base, uri110, uri10); - testRef2(base, uri111, uri11); - testRef2(base, uri112, uri12); - testRef2(base, uri113, uri13); - testRef2(base, uri114, uri14); - testRef2(base, uri116, uri16); - testRef2(base, uri118, uri18); - testRef2(base, uri120, uri20); - testRef2(base, uri121, uri21); - testRef2(base, uri123, uri23); - testRef2(uri104, uri116, uri18); - testRef2(uri104, uri118, uri21); - - // Test the toString method with or without query/fragment - testRef3("http://localhost/path#fragment", true, true, "http://localhost/path#fragment"); - testRef3("http://localhost/path#fragment", true, false, "http://localhost/path"); - testRef3("http://localhost/path#fragment", false, true, "http://localhost/path#fragment"); - testRef3("http://localhost/path#fragment", false, false, "http://localhost/path"); - - testRef3("http://localhost/path?query", true, true, "http://localhost/path?query"); - testRef3("http://localhost/path?query", true, false, "http://localhost/path?query"); - testRef3("http://localhost/path?query", false, true, "http://localhost/path"); - testRef3("http://localhost/path?query", false, false, "http://localhost/path"); - - testRef3( - "http://localhost/path?query#fragment", - true, - true, - "http://localhost/path?query#fragment"); - testRef3( - "http://localhost/path?query#fragment", true, false, "http://localhost/path?query"); - testRef3( - "http://localhost/path?query#fragment", - false, - true, - "http://localhost/path#fragment"); - testRef3("http://localhost/path?query#fragment", false, false, "http://localhost/path"); - - testRef3( - "http://localhost/path#fragment?query", - true, - true, - "http://localhost/path#fragment?query"); - testRef3("http://localhost/path#fragment?query", true, false, "http://localhost/path"); - testRef3( - "http://localhost/path#fragment?query", - false, - true, - "http://localhost/path#fragment?query"); - testRef3("http://localhost/path#fragment?query", false, false, "http://localhost/path"); - - testRef4( - host, - "http", - "host.com", - null, - "http://host.com", - "http://host.com", - "http://host.com", - null, - null); - testRef4(slashdir, null, null, "/dir", null, "/dir", "http://host.com/dir", null, "/dir"); - testRef4(dir, null, null, "dir", null, "dir", "http://host.com/dir", null, "dir"); - testRef4(dirslash, null, null, "dir/", null, "dir/", "http://host.com/dir/", null, "dir/"); - testRef4( - fulldir, - "http", - "host.com", - "/dir", - "http://host.com/dir", - "http://host.com/dir", - "http://host.com/dir", - null, - null); - - testRef4(fulldirsub, null, null, "sub", null, "sub", "http://host.com/sub", null, "sub"); - testRef4( - fulldirslashsub, - null, - null, - "/sub", - null, - "/sub", - "http://host.com/sub", - null, - "/sub"); - testRef4(slashdirsub, null, null, "sub", null, "sub", "http://host.com/sub", null, "sub"); - testRef4( - slashdirslashsub, - null, - null, - "/sub", - null, - "/sub", - "http://host.com/sub", - null, - "/sub"); - testRef4( - dirslashsub, - null, - null, - "sub", - null, - "sub", - "http://host.com/dir/sub", - null, - "sub"); - testRef4( - fullsub, - "http", - "host.com", - "/dir/sub", - "http://host.com/dir/sub", - "http://host.com/dir/sub", - "http://host.com/dir/sub", - null, - null); - } - /** Test port getting/setting. */ - @Test - void testPort() { + @ParameterizedTest + @ValueSource(ints = {8080, 9090}) + void testPort(int port) { Reference ref = getDefaultReference(); - int port = 8080; ref.setHostPort(port); assertEquals(port, ref.getHostPort()); - port = 9090; - ref.setHostPort(port); - assertEquals(port, ref.getHostPort()); - ref = new Reference("http://[::1]:8182"); + } + + @Test + void testPortIPv6() { + Reference ref = new Reference("http://[::1]:8182"); assertEquals(8182, ref.getHostPort()); } @@ -552,93 +284,6 @@ void testQueryWithUri() { assertEquals("contrats/123?srvgwt=localhost:9997", ref.getRelativeRef().toString()); } - /** - * Tests the parsing of a reference into its components - * - * @param reference - * @param scheme - * @param authority - * @param path - * @param query - * @param fragment - */ - private void testRef0( - String reference, - String scheme, - String authority, - String path, - String query, - String fragment) { - final Reference ref = new Reference(reference); - assertEquals(scheme, ref.getScheme()); - assertEquals(authority, ref.getAuthority()); - assertEquals(path, ref.getPath()); - assertEquals(query, ref.getQuery()); - assertEquals(fragment, ref.getFragment()); - } - - /** - * Test the resolution of relative references. - * - * @param baseUri - * @param relativeUri - * @param expectedAbsoluteUri - */ - private void testRef1(String baseUri, String relativeUri, String expectedAbsoluteUri) { - final Reference baseRef = new Reference(baseUri); - final Reference relativeRef = new Reference(baseRef, relativeUri); - final Reference absoluteRef = relativeRef.getTargetRef(); - assertEquals(expectedAbsoluteUri, absoluteRef.toString()); - } - - /** - * Test the relativization of absolute references - * - * @param baseUri - * @param absoluteUri - * @param expectedRelativeUri - */ - private void testRef2(String baseUri, String absoluteUri, String expectedRelativeUri) { - final Reference baseRef = new Reference(baseUri); - final Reference absoluteRef = new Reference(absoluteUri); - final Reference relativeRef = absoluteRef.getRelativeRef(baseRef); - assertEquals(expectedRelativeUri, relativeRef.toString()); - } - - /** - * Test the toString method with or without query/fragment - * - * @param reference - * @param query - * @param fragment - * @param toString - */ - private void testRef3(String reference, boolean query, boolean fragment, String toString) { - final Reference ref = new Reference(reference); - assertEquals(ref.toString(query, fragment), toString); - } - - /** Test the behaviour of several getters upon a Reference object. */ - private void testRef4( - Reference reference, - String scheme, - String authority, - String path, - String remainingPart, - String toString, - String targetRef, - String query, - String relativePart) { - assertEquals(reference.getScheme(), scheme); - assertEquals(reference.getAuthority(), authority); - assertEquals(reference.getPath(), path); - assertEquals(reference.getRemainingPart(), remainingPart); - assertEquals(reference.toString(), toString); - assertEquals(reference.getTargetRef().toString(), targetRef); - assertEquals(reference.getQuery(), query); - assertEquals(reference.getRelativePart(), relativePart); - } - @Test void testRiap() { Reference baseRef = new Reference("riap://component/exist/db/"); @@ -658,7 +303,7 @@ void testScheme() { assertEquals(DEFAULT_SCHEME, ref.getScheme()); } - /** Test scheme-specific part getting/setting. */ + /** Test scheme specific part getting/setting. */ @Test void testSchemeSpecificPart() { final Reference ref = getDefaultReference(); @@ -690,37 +335,67 @@ void testSetLastSegment() { assertEquals("http://localhost:1234/test/last", ref.toString()); } - @Test - void testTargetRef() { - Reference ref = - new Reference( - "http://twitter.com?status=RT @gamasutra: Devil May Cry : Born Again http://www.gamasutra.com/view/feature/177267/"); - Reference targetRef = - new Reference( - new Reference( - "http://www.gamasutra.com/view/feature/177267/devil_may_cry_born_again.php"), - ref) - .getTargetRef(); - assertEquals( - "http://twitter.com?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http:?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http://www.gamasutra.com/view/feature/177267/", - targetRef.toString()); + @ParameterizedTest + @CsvSource({ + "http://localhost:81,//localhost:81", + "http://localhost:81?query,//localhost:81?query", + "http://localhost:81?query#fragment,//localhost:81?query", + "http://localhost:81#fragment,//localhost:81", + "http://localhost:81/#fragment,//localhost:81/", + "http://localhost:81/?query,//localhost:81/?query", + "http://localhost:81/?query=https://perdu.com,//localhost:81/?query=https://perdu.com", + }) + void testSchemeSpecificPart(final String uri, final String expected) { + Reference ref = new Reference(uri); + assertEquals(expected, ref.getSchemeSpecificPart()); + } + + @ParameterizedTest + @CsvSource({ + "http://localhost:81,localhost:81", + "http://localhost:81?query,localhost:81", + "http://localhost:81?query#fragment,localhost:81", + "http://localhost:81#fragment,localhost:81", + "http://localhost:81/#fragment,localhost:81", + "http://localhost:81/?query,localhost:81", + "http://localhost:81/?query=https://perdu.com,localhost:81" + }) + void testAuthority(final String uri, final String expected) { + Reference ref = new Reference(uri); + assertEquals(expected, ref.getAuthority()); + } + + @ParameterizedTest + @CsvSource({ + "http://localhost:81,", + "http://localhost:81/a,/a", + "http://localhost:81?query,", + "http://localhost:81/a?query,/a", + "http://localhost:81?query#fragment,", + "http://localhost:81#fragment/1234,", + "http://localhost:81/a#fragment/1234,/a", + "http://localhost:81/?query,/", + "http://localhost:81/?query=https://perdu.com/1234,/" + }) + void testPath(final String uri, final String expected) { + Reference ref = new Reference(uri); + assertEquals(expected, ref.getPath()); } /** Test references that are unequal. */ @Test - void testUnEquals() throws Exception { + void testUnEquals() { final String uri1 = "http://restlet.org/"; final String uri2 = "http://restlet.net/"; final Reference ref1 = new Reference(uri1); final Reference ref2 = new Reference(uri2); assertNotEquals(ref1, ref2); - assertNotEquals(null, ref1); } @Test void testUserinfo() { final Reference reference = new Reference("http://localhost:81"); - // This format is depre. however, we may prevent failures. + // This format is deprecated; however, we may prevent failures. reference.setUserInfo("login:password"); assertEquals("login:password@localhost:81", reference.getAuthority()); assertEquals("localhost", reference.getHostDomain()); @@ -762,4 +437,277 @@ void testValidity() { ref = new Reference(uri); assertEquals("file:///C%7C/wherever%5Cwhatever.swf", ref.toString()); } + + @Nested + class TestParsing { + + @ParameterizedTest + @CsvSource({ + "urn:example:animal:ferret:nose,urn,,example:animal:ferret:nose,,", + "foo://example.com:8042/over/there?name=ferret#nose,foo,example.com:8042,/over/there,name=ferret,nose", + "mailto:fred@example.com,mailto,,fred@example.com,,", + "foo://info.example.com?fred,foo,info.example.com,,fred,", + "http://localhost?query,http,localhost,,query,", + "http://localhost#?query,http,localhost,,,?query", + "http://localhost/?query,http,localhost,/,query,", + "http://localhost/#?query,http,localhost,/,,?query", + "http://localhost/path#frag/ment,http,localhost,/path,,frag/ment", + "http://localhost/path?qu/ery,http,localhost,/path,qu/ery," + }) + void testComponentsParsing( + String reference, + String scheme, + String authority, + String path, + String query, + String fragment) { + final Reference ref = new Reference(reference); + assertEquals(scheme, ref.getScheme()); + assertEquals(authority, ref.getAuthority()); + assertEquals(path, ref.getPath()); + assertEquals(query, ref.getQuery()); + assertEquals(fragment, ref.getFragment()); + } + + @ParameterizedTest + @CsvSource({ + "http://localhost/path#fragment,true,true,http://localhost/path#fragment", + "http://localhost/path#fragment,true,false,http://localhost/path", + "http://localhost/path#fragment,false,true,http://localhost/path#fragment", + "http://localhost/path#fragment,false,false,http://localhost/path", + "http://localhost/path?query,true,true,http://localhost/path?query", + "http://localhost/path?query,true,false,http://localhost/path?query", + "http://localhost/path?query,false,true,http://localhost/path", + "http://localhost/path?query,false,false,http://localhost/path", + "http://localhost/path?query#fragment,true,true,http://localhost/path?query#fragment", + "http://localhost/path?query#fragment,true,false,http://localhost/path?query", + "http://localhost/path?query#fragment,false,true,http://localhost/path#fragment", + "http://localhost/path?query#fragment,false,false,http://localhost/path", + "http://localhost/path#fragment?query,true,true,http://localhost/path#fragment?query", + "http://localhost/path#fragment?query,true,false,http://localhost/path", + "http://localhost/path#fragment?query,false,true,http://localhost/path#fragment?query", + "http://localhost/path#fragment?query,false,false,http://localhost/path" + }) + void testParsingOfQueryAndFragment( + String reference, boolean query, boolean fragment, String toString) { + final Reference ref = new Reference(reference); + assertEquals(ref.toString(query, fragment), toString); + } + + @Test + void testGetters() { + final Reference host = new Reference("http://host.com"); + final Reference slashdir = new Reference(host, "/dir"); + final Reference dir = new Reference(host, "dir"); + final Reference dirslash = new Reference(host, "dir/"); + final Reference fulldir = new Reference("http://host.com/dir"); + final Reference fulldirsub = new Reference(fulldir, "sub"); + final Reference fulldirslashsub = new Reference(fulldir, "/sub"); + final Reference slashdirsub = new Reference(slashdir, "sub"); + final Reference slashdirslashsub = new Reference(slashdir, "/sub"); + final Reference dirslashsub = new Reference(dirslash, "sub"); + final Reference fullsub = new Reference("http://host.com/dir/sub"); + final Reference fullsubQuery = new Reference("http://host.com/dir/sub?query"); + + testGetters( + host, + "http", + "host.com", + null, + "http://host.com", + "http://host.com", + "http://host.com", + null, + null); + testGetters( + slashdir, + null, + null, + "/dir", + null, + "/dir", + "http://host.com/dir", + null, + "/dir"); + testGetters(dir, null, null, "dir", null, "dir", "http://host.com/dir", null, "dir"); + testGetters( + dirslash, + null, + null, + "dir/", + null, + "dir/", + "http://host.com/dir/", + null, + "dir/"); + testGetters( + fulldir, + "http", + "host.com", + "/dir", + "http://host.com/dir", + "http://host.com/dir", + "http://host.com/dir", + null, + null); + testGetters( + fulldirsub, null, null, "sub", null, "sub", "http://host.com/sub", null, "sub"); + testGetters( + fulldirslashsub, + null, + null, + "/sub", + null, + "/sub", + "http://host.com/sub", + null, + "/sub"); + testGetters( + slashdirsub, + null, + null, + "sub", + null, + "sub", + "http://host.com/sub", + null, + "sub"); + testGetters( + slashdirslashsub, + null, + null, + "/sub", + null, + "/sub", + "http://host.com/sub", + null, + "/sub"); + testGetters( + dirslashsub, + null, + null, + "sub", + null, + "sub", + "http://host.com/dir/sub", + null, + "sub"); + testGetters( + fullsub, + "http", + "host.com", + "/dir/sub", + "http://host.com/dir/sub", + "http://host.com/dir/sub", + "http://host.com/dir/sub", + null, + null); + testGetters( + fullsubQuery, + "http", + "host.com", + "/dir/sub", + "http://host.com/dir/sub?query", + "http://host.com/dir/sub?query", + "http://host.com/dir/sub?query", + "query", + null); + } + + private void testGetters( + Reference reference, + String scheme, + String authority, + String path, + String remainingPart, + String toString, + String targetRef, + String query, + String relativePart) { + assertEquals(reference.getScheme(), scheme); + assertEquals(reference.getAuthority(), authority); + assertEquals(reference.getPath(), path); + assertEquals(reference.getRemainingPart(), remainingPart); + assertEquals(reference.toString(), toString); + assertEquals(reference.getTargetRef().toString(), targetRef); + assertEquals(reference.getQuery(), query); + assertEquals(reference.getRelativePart(), relativePart); + } + + @ParameterizedTest + @CsvSource({ + "http://a/b/c/d;p?q,g:h,g:h", + "http://a/b/c/d;p?q,g,http://a/b/c/g", + "http://a/b/c/d;p?q,./g,http://a/b/c/g", + "http://a/b/c/d;p?q,g/,http://a/b/c/g/", + "http://a/b/c/d;p?q,/g,http://a/g", + "http://a/b/c/d;p?q,//g,http://g", + "http://a/b/c/d;p?q,?y,http://a/b/c/d;p?y", + "http://a/b/c/d;p?q,g?y,http://a/b/c/g?y", + "http://a/b/c/d;p?q,#s,http://a/b/c/d;p?q#s", + "http://a/b/c/d;p?q,g#s,http://a/b/c/g#s", + "http://a/b/c/d;p?q,g?y#s,http://a/b/c/g?y#s", + "http://a/b/c/d;p?q,;x,http://a/b/c/;x", + "http://a/b/c/d;p?q,g;x,http://a/b/c/g;x", + "http://a/b/c/d;p?q,g;x?y#s,http://a/b/c/g;x?y#s", + "http://a/b/c/d;p?q,,http://a/b/c/d;p?q", + "http://a/b/c/d;p?q,.,http://a/b/c/", + "http://a/b/c/d;p?q,./,http://a/b/c/", + "http://a/b/c/d;p?q,..,http://a/b/", + "http://a/b/c/d;p?q,../,http://a/b/", + "http://a/b/c/d;p?q,../g,http://a/b/g", + "http://a/b/c/d;p?q,../..,http://a/", + "http://a/b/c/d;p?q,../../,http://a/", + "http://a/b/c/d;p?q,../../g,http://a/g", + "http://a/b/c/d;p?q,../../../g,http://a/g", + "http://a/b/c/d;p?q,../../../../g,http://a/g", + "http://a/b/c/d;p?q,/./g,http://a/g", + "http://a/b/c/d;p?q,/../g,http://a/g", + "http://a/b/c/d;p?q,g.,http://a/b/c/g.", + "http://a/b/c/d;p?q,.g,http://a/b/c/.g", + "http://a/b/c/d;p?q,g..,http://a/b/c/g..", + "http://a/b/c/d;p?q,..g,http://a/b/c/..g", + "http://a/b/c/d;p?q,./../g,http://a/b/g", + "http://a/b/c/d;p?q,./g/.,http://a/b/c/g/", + "http://a/b/c/d;p?q,g/./h,http://a/b/c/g/h", + "http://a/b/c/d;p?q,g/../h,http://a/b/c/h", + "http://a/b/c/d;p?q,g;x=1/./y,http://a/b/c/g;x=1/y", + "http://a/b/c/d;p?q,g;x=1/../y,http://a/b/c/y" + }) + void testResolutionRelativeReference( + String baseUri, String relativeUri, String expectedAbsoluteUri) { + final Reference baseRef = new Reference(baseUri); + final Reference relativeRef = new Reference(baseRef, relativeUri); + final Reference absoluteRef = relativeRef.getTargetRef(); + assertEquals(expectedAbsoluteUri, absoluteRef.toString()); + } + + @ParameterizedTest + @CsvSource({ + "http://a/b/c/d;p?q,http://a/b/c/g,g", + "http://a/b/c/d;p?q,http://a/b/c/g/,g/", + "http://a/b/c/d;p?q,http://a/b/c/d;p?y,?y", + "http://a/b/c/d;p?q,http://a/b/c/g?y,g?y", + "http://a/b/c/d;p?q,http://a/b/c/d;p?q#s,#s", + "http://a/b/c/d;p?q,http://a/b/c/g#s,g#s", + "http://a/b/c/d;p?q,http://a/b/c/g?y#s,g?y#s", + "http://a/b/c/d;p?q,http://a/b/c/;x,;x", + "http://a/b/c/d;p?q,http://a/b/c/g;x,g;x", + "http://a/b/c/d;p?q,http://a/b/c/g;x?y#s,g;x?y#s", + "http://a/b/c/d;p?q,http://a/b/c/,.", + "http://a/b/c/d;p?q,http://a/b/,..", + "http://a/b/c/d;p?q,http://a/b/g,../g", + "http://a/b/c/d;p?q,http://a/,../..", + "http://a/b/c/d;p?q,http://a/g,../../g", + "http://a/b/c/g/,http://a/b/c/,..", + "http://a/b/c/g/,http://a/b/,../.." + }) + void testRelativizeAbsoluteReference( + String baseUri, String absoluteUri, String expectedRelativeUri) { + final Reference baseRef = new Reference(baseUri); + final Reference absoluteRef = new Reference(absoluteUri); + final Reference relativeRef = absoluteRef.getRelativeRef(baseRef); + assertEquals(expectedRelativeUri, relativeRef.toString()); + } + } } diff --git a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java index 79a06aa89e..c5ffe713ce 100644 --- a/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java +++ b/org.restlet/src/test/java/org/restlet/engine/application/TunnelFilterTestCase.java @@ -19,6 +19,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import org.restlet.Application; import org.restlet.Context; import org.restlet.Request; @@ -119,10 +122,6 @@ void assertNotSameMethod(Method method) { assertNotSame(this.request.getMethod(), method); } - /** - * @param expectedCut - * @param expectedExtensions - */ private void check(String expectedCut, String expectedExtensions) { final Reference resourceRef = this.request.getResourceRef(); assertEquals(expectedCut, resourceRef.toString()); @@ -135,7 +134,7 @@ private void check(String expectedCut, String expectedExtensions) { /** * @param expectedSubPathCut if null, the same as subPathOrig - * @param expectedExtension if null, then same as "" for this test + * @param expectedExtension if null, then the same as "" for this test */ private void checkFromPath(String expectedSubPathCut, String expectedExtension) { if (expectedSubPathCut == null) { @@ -154,7 +153,6 @@ void createGet(String reference) { } /** - * @param subPathToCheck * @see #createGet(String) * @see #createRequest(Method, String) */ @@ -169,16 +167,14 @@ void createPost(String reference) { /** * Creates a {@link Request} and put it into {@link #request}.
- * To use the methods provided by the test case class use ever the provided create methods to + * To use the methods provided by the test case class, use ever the provided create methods to * create a request. * - * @param method - * @param reference * @see #createPost(String) * @see #createGet(String) * @see #createGetFromPath(String) */ - void createRequest(Method method, String reference) { + private void createRequest(Method method, String reference) { this.request = new Request(method, reference); this.request.setOriginalRef(new Reference(reference)); this.response = new Response(this.request); @@ -206,7 +202,7 @@ private void setPrefs() { } @BeforeEach - public void setUpEach() throws Exception { + void setUpEach() { Application app = new Application(new Context()); Application.setCurrent(app); this.tunnelFilter = new TunnelFilter(app.getContext()); @@ -214,34 +210,21 @@ public void setUpEach() throws Exception { } @AfterEach - protected void tearDownEach() throws Exception { + void tearDownEach() { this.tunnelFilter = null; this.request = null; this.response = null; } - @Test - void testExtMappingOff1() { - extensionTunnelOff(); - createGet(UNEFFECTED); - this.accLanguages.add(new Preference<>(Language.valueOf("ajh"))); - this.accMediaTypes.add(new Preference<>(MediaType.APPLICATION_STUFFIT)); - filter(); - assertEquals(UNEFFECTED, this.request.getResourceRef().toString()); - assertLanguages(Language.valueOf("ajh")); - assertMediaTypes(MediaType.APPLICATION_STUFFIT); - assertCharSets(); - assertEncodings(); - } - - @Test - void testExtMappingOff2() { + @ParameterizedTest + @ValueSource(strings = {EFFECTED, UNEFFECTED}) + void testExtensionsMappingOff(String reference) { extensionTunnelOff(); - createGet(EFFECTED); + createGet(reference); this.accLanguages.add(new Preference<>(Language.valueOf("ajh"))); this.accMediaTypes.add(new Preference<>(MediaType.APPLICATION_STUFFIT)); filter(); - assertEquals(EFFECTED, this.request.getResourceRef().toString()); + assertEquals(reference, this.request.getResourceRef().toString()); assertLanguages(Language.valueOf("ajh")); assertMediaTypes(MediaType.APPLICATION_STUFFIT); assertCharSets(); @@ -249,23 +232,27 @@ void testExtMappingOff2() { } @Test - void testExtMappingOn() { + void shouldDetectExtensionWithoutMediaType() { createGet(UNEFFECTED); filter(); check(UNEFFECTED, "ab"); assertLanguages(); assertCharSets(); - assertCharSets(); assertMediaTypes(); + } + @Test + void shouldDetectMediaType() { createGet(EFFECTED); filter(); check("http://example.org/adf.asdf/af", null); assertMediaTypes(MediaType.TEXT_HTML); assertLanguages(); assertCharSets(); - assertCharSets(); + } + @Test + void shouldCutPathAndExtension() { createGetFromPath("afhhh"); filter(); checkFromPath(null, null); @@ -273,7 +260,10 @@ void testExtMappingOn() { assertLanguages(); assertEncodings(); assertCharSets(); + } + @Test + void shouldCutPathAndDetectExtension() { createGetFromPath("hksf.afsdf"); filter(); checkFromPath(null, "afsdf"); @@ -281,7 +271,10 @@ void testExtMappingOn() { assertLanguages(); assertEncodings(); assertCharSets(); + } + @Test + void shouldKeepPathAndDetectExtensionAndMediaType() { createGetFromPath("hksf.afsdf.html"); filter(); checkFromPath("hksf.afsdf", "afsdf"); @@ -289,29 +282,31 @@ void testExtMappingOn() { assertLanguages(); assertEncodings(); assertCharSets(); + } - createGetFromPath("hksf.afsdf.html.txt"); - filter(); - checkFromPath("hksf.afsdf.html", "afsdf.html"); - assertMediaTypes(MediaType.TEXT_PLAIN); - assertLanguages(); - assertEncodings(); - assertCharSets(); - - createGetFromPath("hksf.html.afsdf.txt"); + @ParameterizedTest + @CsvSource({ + "hksf.afsdf.html.txt,hksf.afsdf.html,afsdf.html", + "hksf.html.afsdf.txt,hksf.html.afsdf,html.afsdf" + }) + void shouldKeepPathAndDetectExtensionAndKeepLastMediaType( + String path, String expectedSubPathCut, String expectedExtension) { + createGetFromPath(path); filter(); - checkFromPath("hksf.html.afsdf", "html.afsdf"); + checkFromPath(expectedSubPathCut, expectedExtension); assertMediaTypes(MediaType.TEXT_PLAIN); assertLanguages(); assertEncodings(); assertCharSets(); + } + @Test + void shouldKeepPathAndDetectExtensionAndKeepLastMediaTypeAndLastLanguage() { createGetFromPath("hksf.html.afsdf.txt.en.fr"); filter(); checkFromPath("hksf.html.afsdf.txt.en", "html.afsdf.txt.en"); // Take care about the fact that only one extension per metadata "type" // is allowed: ie only one Language, one encoding, one media type, etc. - // assertMediaTypes(MediaType.TEXT_PLAIN); assertMediaTypes(); assertLanguages(Language.FRENCH); assertEncodings(); @@ -324,7 +319,10 @@ void testExtMappingOn() { assertLanguages(Language.ENGLISH); assertEncodings(); assertCharSets(); + } + @Test + void shouldDetectNoExtension() { createGet(START_REF_FOR_PATH_TEST); filter(); checkFromPath(null, null); From 03c15f8f9f0969a886b4055f1fe2d049b9f83dd0 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Tue, 7 Apr 2026 23:14:55 +0200 Subject: [PATCH 30/30] Taken into account PR review comment --- .../restlet/ext/json/JsonpFilterTestCase.java | 2 +- .../restlet/security/HttpBasicTestCase.java | 64 +++++++------------ 2 files changed, 25 insertions(+), 41 deletions(-) diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java index 60ca95cbc7..44435ac59b 100644 --- a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonpFilterTestCase.java @@ -100,7 +100,7 @@ void testAfterHandle_without_callback_should_return_entity_unchanged() { } @Test - void testAfterHandle_with_other_mediatype_should_return_entity_unchanged() throws Exception { + void testAfterHandle_with_other_mediatype_should_return_entity_unchanged() { JsonpFilter filter = new JsonpFilter(null); diff --git a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java index 4341c0005c..0ff9ddc764 100644 --- a/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java +++ b/org.restlet/src/test/java/org/restlet/security/HttpBasicTestCase.java @@ -42,6 +42,25 @@ */ class HttpBasicTestCase { + public static final String AUTHENTICATED_MSG = "You are authenticated"; + public static final String LONG_PASSWORD = "thisLongPasswordIsExtremelySecure"; + public static final String LONG_USERNAME = "aVeryLongUsernameIsIndeedRequiredForThisTest"; + public static final String SHORT_PASSWORD = "pw15"; + public static final String SHORT_USERNAME = "user13"; + public static final String WRONG_USERNAME = "wrongUser"; + + static Stream invalidCredentials() { + return Stream.of( + arguments(LONG_USERNAME, SHORT_PASSWORD), + arguments(SHORT_USERNAME, LONG_PASSWORD), + arguments(WRONG_USERNAME, SHORT_PASSWORD)); + } + + static Stream validCredentials() { + return Stream.of( + arguments(LONG_USERNAME, LONG_PASSWORD), arguments(SHORT_USERNAME, SHORT_PASSWORD)); + } + public static class AuthenticatedRestlet extends Restlet { @Override public void handle(Request request, Response response) { @@ -57,50 +76,15 @@ public TestVerifier() { @Override public int verify(String identifier, char[] inputSecret) { - // NOTE: Allocating Strings are not really secure treatment of passwords - String almostSecret = new String(inputSecret); - try { return super.verify(identifier, inputSecret); } finally { - // Clear secret from memory as soon as possible (This is better - // treatment, but useless due to our almostSecret copy) + // Clear secret from memory as soon as possible Arrays.fill(inputSecret, '\000'); } } } - public static class BlockerVerifier implements Verifier { - @Override - public int verify(Request request, Response response) { - return RESULT_INVALID; - } - } - - public static final String AUTHENTICATED_MSG = "You are authenticated"; - - public static final String LONG_PASSWORD = "thisLongPasswordIsExtremelySecure"; - - public static final String LONG_USERNAME = "aVeryLongUsernameIsIndeedRequiredForThisTest"; - - public static final String SHORT_PASSWORD = "pw15"; - - public static final String SHORT_USERNAME = "user13"; - - public static final String WRONG_USERNAME = "wrongUser"; - - static Stream invalidCredentials() { - return Stream.of( - arguments(LONG_USERNAME, SHORT_PASSWORD), - arguments(SHORT_USERNAME, LONG_PASSWORD), - arguments(WRONG_USERNAME, SHORT_PASSWORD)); - } - - static Stream validCredentials() { - return Stream.of( - arguments(LONG_USERNAME, LONG_PASSWORD), arguments(SHORT_USERNAME, SHORT_PASSWORD)); - } - @Nested class TestMapVerifier { @@ -128,14 +112,14 @@ class TestHttpBasicServer { private Client client; @Test - void HttpBasicNone() throws Exception { + void HttpBasicNone() { final Response response = client.handle(request); assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); } @ParameterizedTest @MethodSource("org.restlet.security.HttpBasicTestCase#invalidCredentials") - void testInvalidCredentials(final String login, final String password) throws Exception { + void testInvalidCredentials(final String login, final String password) { ChallengeResponse authentication = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password); request.setChallengeResponse(authentication); @@ -157,7 +141,7 @@ void testValidCredentials(final String login, final String password) throws Exce } @BeforeEach - public void makeServer() throws Exception { + void makeServer() throws Exception { final String REALM = HttpBasicTestCase.class.getSimpleName(); this.component = new Component(); final Server server = this.component.getServers().add(Protocol.HTTP, 0); @@ -182,7 +166,7 @@ public Restlet createInboundRoot() { } @AfterEach - public void cleanup() throws Exception { + void cleanup() throws Exception { client.stop(); if (this.component.isStarted()) { this.component.stop();

list of currently used attributes